skybridge 0.0.0-dev.bffc31c → 0.0.0-dev.c003b05

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 (70) hide show
  1. package/dist/cli/use-nodemon.js +2 -2
  2. package/dist/cli/use-nodemon.js.map +1 -1
  3. package/dist/commands/build.js +27 -6
  4. package/dist/commands/build.js.map +1 -1
  5. package/dist/commands/start.js +2 -7
  6. package/dist/commands/start.js.map +1 -1
  7. package/dist/server/content-helpers.d.ts +27 -0
  8. package/dist/server/content-helpers.js +46 -0
  9. package/dist/server/content-helpers.js.map +1 -0
  10. package/dist/server/content-helpers.test.js +70 -0
  11. package/dist/server/content-helpers.test.js.map +1 -0
  12. package/dist/server/express.js +2 -2
  13. package/dist/server/express.js.map +1 -1
  14. package/dist/server/express.test.js +2 -2
  15. package/dist/server/express.test.js.map +1 -1
  16. package/dist/server/index.d.ts +4 -3
  17. package/dist/server/index.js +3 -2
  18. package/dist/server/index.js.map +1 -1
  19. package/dist/server/middleware.test.js +12 -9
  20. package/dist/server/middleware.test.js.map +1 -1
  21. package/dist/server/server.d.ts +79 -72
  22. package/dist/server/server.js +141 -85
  23. package/dist/server/server.js.map +1 -1
  24. package/dist/server/templateHelper.d.ts +5 -5
  25. package/dist/server/templates/development.hbs +2 -2
  26. package/dist/server/templates/production.hbs +1 -1
  27. package/dist/server/viewsDevServer.d.ts +14 -0
  28. package/dist/server/viewsDevServer.js +45 -0
  29. package/dist/server/viewsDevServer.js.map +1 -0
  30. package/dist/test/utils.d.ts +8 -20
  31. package/dist/test/utils.js +39 -35
  32. package/dist/test/utils.js.map +1 -1
  33. package/dist/test/{widget.test.js → view.test.js} +229 -109
  34. package/dist/test/view.test.js.map +1 -0
  35. package/dist/web/bridges/types.d.ts +2 -2
  36. package/dist/web/generate-helpers.d.ts +20 -18
  37. package/dist/web/generate-helpers.js +20 -18
  38. package/dist/web/generate-helpers.js.map +1 -1
  39. package/dist/web/generate-helpers.test-d.js +20 -20
  40. package/dist/web/generate-helpers.test-d.js.map +1 -1
  41. package/dist/web/index.d.ts +1 -0
  42. package/dist/web/index.js.map +1 -1
  43. package/dist/web/plugin/plugin.d.ts +4 -1
  44. package/dist/web/plugin/plugin.js +112 -25
  45. package/dist/web/plugin/plugin.js.map +1 -1
  46. package/dist/web/plugin/scan-views.d.ts +8 -0
  47. package/dist/web/plugin/scan-views.js +70 -0
  48. package/dist/web/plugin/scan-views.js.map +1 -0
  49. package/dist/web/plugin/scan-views.test.d.ts +1 -0
  50. package/dist/web/plugin/scan-views.test.js +67 -0
  51. package/dist/web/plugin/scan-views.test.js.map +1 -0
  52. package/dist/web/plugin/validate-view.d.ts +1 -0
  53. package/dist/web/plugin/validate-view.js +9 -0
  54. package/dist/web/plugin/validate-view.js.map +1 -0
  55. package/dist/web/plugin/validate-view.test.d.ts +1 -0
  56. package/dist/web/plugin/validate-view.test.js +24 -0
  57. package/dist/web/plugin/validate-view.test.js.map +1 -0
  58. package/package.json +4 -1
  59. package/tsconfig.base.json +2 -0
  60. package/dist/server/widgetsDevServer.d.ts +0 -13
  61. package/dist/server/widgetsDevServer.js +0 -52
  62. package/dist/server/widgetsDevServer.js.map +0 -1
  63. package/dist/test/widget.test.js.map +0 -1
  64. package/dist/web/plugin/validate-widget.d.ts +0 -5
  65. package/dist/web/plugin/validate-widget.js +0 -27
  66. package/dist/web/plugin/validate-widget.js.map +0 -1
  67. package/dist/web/plugin/validate-widget.test.js +0 -42
  68. package/dist/web/plugin/validate-widget.test.js.map +0 -1
  69. /package/dist/{test/widget.test.d.ts → server/content-helpers.test.d.ts} +0 -0
  70. /package/dist/{web/plugin/validate-widget.test.d.ts → test/view.test.d.ts} +0 -0
@@ -2,8 +2,16 @@ import crypto from "node:crypto";
2
2
  import { afterEach, beforeEach, describe, expect, it, vi, } from "vitest";
3
3
  import { createMockExtra, createMockMcpServer, resetTestEnv, setTestEnv, } from "./utils.js";
4
4
  const mockManifest = {
5
- "src/widgets/my-widget.tsx": { file: "my-widget.js" },
6
- "src/widgets/folder-widget/index.tsx": { file: "folder-widget.js" },
5
+ "skybridge:view:my-widget": {
6
+ file: "assets/my-widget-abc123.js",
7
+ name: "my-widget",
8
+ isEntry: true,
9
+ },
10
+ "skybridge:view:folder-widget": {
11
+ file: "assets/folder-widget-def456.js",
12
+ name: "folder-widget",
13
+ isEntry: true,
14
+ },
7
15
  "style.css": { file: "style.css" },
8
16
  };
9
17
  const actual = vi.hoisted(() => require("node:fs"));
@@ -22,7 +30,7 @@ vi.mock("node:fs", () => {
22
30
  },
23
31
  };
24
32
  });
25
- describe("McpServer.registerWidget", () => {
33
+ describe("McpServer.registerTool (unified API)", () => {
26
34
  let server;
27
35
  let mockRegisterResource;
28
36
  let mockRegisterTool;
@@ -36,11 +44,14 @@ describe("McpServer.registerWidget", () => {
36
44
  });
37
45
  it("should generate correct HTML for development mode", async () => {
38
46
  setTestEnv({ NODE_ENV: "development" });
39
- const mockToolCallback = vi.fn();
40
- const mockRegisterResourceConfig = { description: "Test widget" };
41
- const mockToolConfig = { description: "Test tool" };
42
- server.registerWidget("my-widget", mockRegisterResourceConfig, mockToolConfig, mockToolCallback);
43
- // Get the resource callback function
47
+ server.registerTool({
48
+ name: "my-widget",
49
+ description: "Test tool",
50
+ view: {
51
+ component: "my-widget",
52
+ description: "Test view",
53
+ },
54
+ }, vi.fn());
44
55
  const appsSdkResourceCallback = mockRegisterResource.mock
45
56
  .calls[0]?.[3];
46
57
  expect(appsSdkResourceCallback).toBeDefined();
@@ -62,24 +73,25 @@ describe("McpServer.registerWidget", () => {
62
73
  connect_domains: [serverUrl, hmrUrl],
63
74
  },
64
75
  "openai/widgetDomain": serverUrl,
65
- "openai/widgetDescription": "Test widget",
76
+ "openai/widgetDescription": "Test view",
66
77
  },
67
78
  },
68
79
  ],
69
80
  });
70
- // Check development-specific content
71
81
  expect(result.contents[0]?.text).toContain(`${serverUrl}/assets/@react-refresh`);
72
82
  expect(result.contents[0]?.text).toContain(`${serverUrl}/@vite/client`);
73
- expect(result.contents[0]?.text).toContain(`${serverUrl}/src/widgets/my-widget`);
74
- expect(result.contents[0]?.text).not.toContain(`${serverUrl}/src/widgets/my-widget.tsx`);
83
+ expect(result.contents[0]?.text).toContain(`${serverUrl}/_skybridge/view/my-widget`);
75
84
  });
76
85
  it("should generate correct HTML for production mode", async () => {
77
86
  setTestEnv({ NODE_ENV: "production" });
78
- const mockToolCallback = vi.fn();
79
- const mockRegisterResourceConfig = { description: "Test widget" };
80
- const mockToolConfig = { description: "Test tool" };
81
- server.registerWidget("my-widget", mockRegisterResourceConfig, mockToolConfig, mockToolCallback);
82
- // Get the resource callback function
87
+ server.registerTool({
88
+ name: "my-widget",
89
+ description: "Test tool",
90
+ view: {
91
+ component: "my-widget",
92
+ description: "Test view",
93
+ },
94
+ }, vi.fn());
83
95
  const appsSdkResourceCallback = mockRegisterResource.mock
84
96
  .calls[0]?.[3];
85
97
  expect(appsSdkResourceCallback).toBeDefined();
@@ -99,21 +111,23 @@ describe("McpServer.registerWidget", () => {
99
111
  connect_domains: [serverUrl],
100
112
  },
101
113
  "openai/widgetDomain": serverUrl,
102
- "openai/widgetDescription": "Test widget",
114
+ "openai/widgetDescription": "Test view",
103
115
  },
104
116
  },
105
117
  ],
106
118
  });
107
- // Check production-specific content
108
119
  expect(result.contents[0]?.text).not.toContain(`${serverUrl}/assets/@react-refresh`);
109
120
  expect(result.contents[0]?.text).not.toContain(`${serverUrl}@vite/client`);
110
- expect(result.contents[0]?.text).toContain(`${serverUrl}/assets/my-widget.js`);
121
+ expect(result.contents[0]?.text).toContain(`${serverUrl}/assets/assets/my-widget-abc123.js`);
111
122
  expect(result.contents[0]?.text).toContain(`${serverUrl}/assets/style.css`);
112
123
  });
113
- it("should prefer x-alpic-forwarded-url when hashing Claude widget domains", async () => {
124
+ it("should prefer x-alpic-forwarded-url when hashing Claude view domains", async () => {
114
125
  setTestEnv({ NODE_ENV: "production" });
115
- const mockToolCallback = vi.fn();
116
- server.registerWidget("my-widget", { description: "Test widget" }, { description: "Test tool" }, mockToolCallback);
126
+ server.registerTool({
127
+ name: "my-widget",
128
+ description: "Test tool",
129
+ view: { component: "my-widget", description: "Test view" },
130
+ }, vi.fn());
117
131
  const extAppsResourceCallback = mockRegisterResource.mock
118
132
  .calls[1]?.[3];
119
133
  expect(extAppsResourceCallback).toBeDefined();
@@ -135,42 +149,30 @@ describe("McpServer.registerWidget", () => {
135
149
  csp: {
136
150
  resourceDomains: ["http://localhost:3000"],
137
151
  connectDomains: ["http://localhost:3000"],
152
+ baseUriDomains: ["http://localhost:3000"],
138
153
  },
154
+ description: "Test view",
139
155
  domain: expectedDomain,
140
156
  },
141
157
  });
142
158
  });
143
- it("should resolve folder-based widgets (barrel files) in production mode", async () => {
144
- setTestEnv({ NODE_ENV: "production" });
145
- const mockToolCallback = vi.fn();
146
- const mockRegisterResourceConfig = { description: "Folder widget" };
147
- const mockToolConfig = { description: "Folder tool" };
148
- server.registerWidget("folder-widget", mockRegisterResourceConfig, mockToolConfig, mockToolCallback);
149
- const appsSdkResourceCallback = mockRegisterResource.mock
150
- .calls[0]?.[3];
151
- expect(appsSdkResourceCallback).toBeDefined();
152
- const host = "myapp.com";
153
- const serverUrl = `https://${host}`;
154
- const mockExtra = createMockExtra(host);
155
- const result = await appsSdkResourceCallback(new URL("ui://widgets/apps-sdk/folder-widget.html"), mockExtra);
156
- // Should resolve to folder-widget.js from the manifest entry "src/widgets/folder-widget/index.tsx"
157
- expect(result.contents[0]?.text).toContain(`${serverUrl}/assets/folder-widget.js`);
158
- });
159
- it("should register resources with correct hostType for both apps-sdk and ext-apps formats", async () => {
160
- const mockToolCallback = vi.fn();
161
- const mockRegisterResourceConfig = {
162
- description: "Test widget",
163
- _meta: { "openai/widgetPrefersBorder": true },
164
- };
165
- const mockToolConfig = { description: "Test tool" };
166
- server.registerWidget("my-widget", mockRegisterResourceConfig, mockToolConfig, mockToolCallback);
159
+ it("should register resources with correct hostType for both apps-sdk and ext-apps", async () => {
160
+ server.registerTool({
161
+ name: "my-widget",
162
+ description: "Test tool",
163
+ view: {
164
+ component: "my-widget",
165
+ description: "Test view",
166
+ prefersBorder: true,
167
+ },
168
+ }, vi.fn());
167
169
  expect(mockRegisterResource).toHaveBeenCalledTimes(2);
168
170
  const appsSdkCallback = mockRegisterResource.mock
169
171
  .calls[0]?.[3];
170
- const appsSdkResult = await appsSdkCallback(new URL("ui://widgets/apps-sdk/my-widget.html"), createMockExtra("localhost:3000"));
171
172
  const host = "localhost:3000";
172
173
  const serverUrl = `http://${host}`;
173
174
  const hmrUrl = `ws://${host}`;
175
+ const appsSdkResult = await appsSdkCallback(new URL("ui://widgets/apps-sdk/my-widget.html"), createMockExtra(host));
174
176
  expect(appsSdkResult).toEqual({
175
177
  contents: [
176
178
  {
@@ -183,7 +185,7 @@ describe("McpServer.registerWidget", () => {
183
185
  connect_domains: [serverUrl, hmrUrl],
184
186
  },
185
187
  "openai/widgetDomain": serverUrl,
186
- "openai/widgetDescription": "Test widget",
188
+ "openai/widgetDescription": "Test view",
187
189
  "openai/widgetPrefersBorder": true,
188
190
  },
189
191
  },
@@ -205,8 +207,11 @@ describe("McpServer.registerWidget", () => {
205
207
  csp: {
206
208
  resourceDomains: [serverUrl],
207
209
  connectDomains: [serverUrl, hmrUrl],
210
+ baseUriDomains: [serverUrl],
208
211
  },
209
212
  domain: serverUrl,
213
+ description: "Test view",
214
+ prefersBorder: true,
210
215
  },
211
216
  },
212
217
  },
@@ -214,11 +219,12 @@ describe("McpServer.registerWidget", () => {
214
219
  });
215
220
  expect(extAppsResult.contents[0]?.text).toContain('window.skybridge = { hostType: "mcp-app", serverUrl: "http://localhost:3000" };');
216
221
  });
217
- it("should register tool with ui.resourceUri metadata (not deprecated ui/resourceUri)", async () => {
218
- const mockToolCallback = vi.fn();
219
- const mockRegisterResourceConfig = { description: "Test widget" };
220
- const mockToolConfig = { description: "Test tool" };
221
- server.registerWidget("my-widget", mockRegisterResourceConfig, mockToolConfig, mockToolCallback);
222
+ it("should register tool with ui.resourceUri metadata", async () => {
223
+ server.registerTool({
224
+ name: "my-widget",
225
+ description: "Test tool",
226
+ view: { component: "my-widget", description: "Test view" },
227
+ }, vi.fn());
222
228
  expect(mockRegisterTool).toHaveBeenCalledTimes(1);
223
229
  const toolCallArgs = mockRegisterTool.mock.calls[0];
224
230
  const toolConfig = toolCallArgs?.[1];
@@ -227,51 +233,32 @@ describe("McpServer.registerWidget", () => {
227
233
  resourceUri: "ui://widgets/ext-apps/my-widget.html",
228
234
  });
229
235
  });
230
- it("should apply meta overrides in correct priority: defaults < ui.* < direct openai/* keys", async () => {
231
- const mockToolCallback = vi.fn();
232
- const mockRegisterResourceConfig = {
233
- description: "Test widget",
234
- _meta: {
235
- // ui.csp overrides (middle priority)
236
- ui: {
237
- csp: {
238
- resourceDomains: ["https://from-ui-csp.com"],
239
- connectDomains: ["https://from-ui-csp.com"],
240
- },
241
- domain: "https://from-ui-domain.com",
242
- prefersBorder: false,
243
- },
244
- // Direct openai/* keys (highest priority) - should override ui.*
245
- "openai/widgetDomain": "https://direct-override.com",
246
- "openai/widgetPrefersBorder": true,
236
+ it("should register tool with openai/outputTemplate when apps-sdk only", async () => {
237
+ server.registerTool({
238
+ name: "my-widget",
239
+ description: "Test tool",
240
+ view: {
241
+ component: "my-widget",
242
+ description: "Test view",
243
+ hosts: ["apps-sdk"],
247
244
  },
248
- };
249
- const mockToolConfig = { description: "Test tool" };
250
- server.registerWidget("override-test", mockRegisterResourceConfig, mockToolConfig, mockToolCallback);
251
- const appsSdkCallback = mockRegisterResource.mock
252
- .calls[0]?.[3];
253
- const host = `localhost:3000`;
254
- const serverUrl = `http://${host}`;
255
- const hmrUrl = `ws://${host}`;
256
- const result = await appsSdkCallback(new URL("ui://widgets/apps-sdk/override-test.html"), createMockExtra(host));
257
- const meta = result.contents[0]?._meta;
258
- // CSP arrays are merged with union - all unique domains from defaults and user config are preserved
259
- expect(meta["openai/widgetCSP"]).toEqual({
260
- resource_domains: [serverUrl, "https://from-ui-csp.com"],
261
- connect_domains: [serverUrl, hmrUrl, "https://from-ui-csp.com"],
262
- frame_domains: undefined,
263
- redirect_domains: undefined,
264
- });
265
- // Domain should be overridden by direct openai/* key (highest priority)
266
- expect(meta["openai/widgetDomain"]).toBe("https://direct-override.com");
267
- // PrefersBorder should be overridden by direct openai/* key (highest priority)
268
- expect(meta["openai/widgetPrefersBorder"]).toBe(true);
269
- // Description should be from defaults (toolConfig.description)
270
- expect(meta["openai/widgetDescription"]).toBe("Test widget");
245
+ }, vi.fn());
246
+ expect(mockRegisterTool).toHaveBeenCalledTimes(1);
247
+ const toolCallArgs = mockRegisterTool.mock.calls[0];
248
+ const toolConfig = toolCallArgs?.[1];
249
+ expect(toolConfig._meta).not.toHaveProperty("ui");
250
+ expect(toolConfig._meta?.["openai/outputTemplate"]).toBe("ui://widgets/apps-sdk/my-widget.html");
271
251
  });
272
- it("should register tool with ui.resourceUri metadata only", async () => {
273
- const mockToolCallback = vi.fn();
274
- server.registerWidget("my-widget", { description: "Test widget", hosts: ["mcp-app"] }, { description: "Test tool" }, mockToolCallback);
252
+ it("should register tool with ui.resourceUri only when mcp-app only", async () => {
253
+ server.registerTool({
254
+ name: "my-widget",
255
+ description: "Test tool",
256
+ view: {
257
+ component: "my-widget",
258
+ description: "Test view",
259
+ hosts: ["mcp-app"],
260
+ },
261
+ }, vi.fn());
275
262
  expect(mockRegisterTool).toHaveBeenCalledTimes(1);
276
263
  const toolCallArgs = mockRegisterTool.mock.calls[0];
277
264
  const toolConfig = toolCallArgs?.[1];
@@ -279,22 +266,24 @@ describe("McpServer.registerWidget", () => {
279
266
  expect(toolConfig._meta?.ui).toEqual({
280
267
  resourceUri: "ui://widgets/ext-apps/my-widget.html",
281
268
  });
282
- expect(toolConfig._meta?.["openai/outputTemplate"]).to.be.undefined;
269
+ expect(toolConfig._meta?.["openai/outputTemplate"]).toBeUndefined();
283
270
  });
284
271
  it("should inject viewUUID into _meta of tool callback results", async () => {
285
272
  const mockToolCallback = vi.fn().mockResolvedValue({
286
273
  content: [{ type: "text", text: "result" }],
287
274
  structuredContent: { data: "test" },
288
275
  });
289
- server.registerWidget("my-widget", { description: "Test widget" }, { description: "Test tool" }, mockToolCallback);
290
- // The registerTool should have been called with a wrapped callback
276
+ server.registerTool({
277
+ name: "my-widget",
278
+ description: "Test tool",
279
+ view: { component: "my-widget", description: "Test view" },
280
+ }, mockToolCallback);
291
281
  const wrappedCallback = mockRegisterTool.mock.calls[0]?.[2];
292
282
  expect(wrappedCallback).toBeDefined();
293
283
  const result = await wrappedCallback({}, {});
294
284
  expect(result._meta).toBeDefined();
295
285
  expect(result._meta?.viewUUID).toBeDefined();
296
286
  expect(typeof result._meta?.viewUUID).toBe("string");
297
- // UUID v4 format
298
287
  expect(result._meta?.viewUUID).toMatch(/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/);
299
288
  });
300
289
  it("should preserve existing _meta when injecting viewUUID", async () => {
@@ -303,7 +292,11 @@ describe("McpServer.registerWidget", () => {
303
292
  structuredContent: { data: "test" },
304
293
  _meta: { requestId: "req-123", cached: true },
305
294
  });
306
- server.registerWidget("my-widget", { description: "Test widget" }, { description: "Test tool" }, mockToolCallback);
295
+ server.registerTool({
296
+ name: "my-widget",
297
+ description: "Test tool",
298
+ view: { component: "my-widget", description: "Test view" },
299
+ }, mockToolCallback);
307
300
  const wrappedCallback = mockRegisterTool.mock.calls[0]?.[2];
308
301
  const result = await wrappedCallback({}, {});
309
302
  expect(result._meta?.requestId).toBe("req-123");
@@ -315,20 +308,147 @@ describe("McpServer.registerWidget", () => {
315
308
  content: [{ type: "text", text: "result" }],
316
309
  structuredContent: {},
317
310
  });
318
- server.registerWidget("my-widget", { description: "Test widget" }, { description: "Test tool" }, mockToolCallback);
311
+ server.registerTool({
312
+ name: "my-widget",
313
+ description: "Test tool",
314
+ view: { component: "my-widget", description: "Test view" },
315
+ }, mockToolCallback);
319
316
  const wrappedCallback = mockRegisterTool.mock.calls[0]?.[2];
320
317
  const result1 = await wrappedCallback({}, {});
321
318
  const result2 = await wrappedCallback({}, {});
322
319
  expect(result1._meta?.viewUUID).not.toBe(result2._meta?.viewUUID);
323
320
  });
324
- it("should register tool with uopenai/outputTemplate metadata only", async () => {
325
- const mockToolCallback = vi.fn();
326
- server.registerWidget("my-widget", { description: "Test widget", hosts: ["apps-sdk"] }, { description: "Test tool" }, mockToolCallback);
321
+ it("should enforce one-tool-per-view constraint", () => {
322
+ server.registerTool({
323
+ name: "shake",
324
+ description: "First tool",
325
+ view: { component: "magic-8-ball" },
326
+ }, vi.fn());
327
+ expect(() => {
328
+ server.registerTool({
329
+ name: "shake-v2",
330
+ description: "Second tool",
331
+ view: { component: "magic-8-ball" },
332
+ }, vi.fn());
333
+ }).toThrow('skybridge: view "magic-8-ball" is already used by tool "shake"');
334
+ });
335
+ it("should normalize string content to ContentBlock array", async () => {
336
+ const mockToolCallback = vi.fn().mockResolvedValue({
337
+ content: "Hello world",
338
+ structuredContent: {},
339
+ });
340
+ server.registerTool({
341
+ name: "string-content",
342
+ description: "Test tool",
343
+ view: { component: "string-content" },
344
+ }, mockToolCallback);
345
+ const wrappedCallback = mockRegisterTool.mock.calls[0]?.[2];
346
+ const result = await wrappedCallback({}, {});
347
+ expect(result.content).toEqual([{ type: "text", text: "Hello world" }]);
348
+ });
349
+ it("should normalize single ContentBlock to array", async () => {
350
+ const mockToolCallback = vi.fn().mockResolvedValue({
351
+ content: { type: "text", text: "Single block" },
352
+ structuredContent: {},
353
+ });
354
+ server.registerTool({
355
+ name: "single-block",
356
+ description: "Test tool",
357
+ view: { component: "single-block" },
358
+ }, mockToolCallback);
359
+ const wrappedCallback = mockRegisterTool.mock.calls[0]?.[2];
360
+ const result = await wrappedCallback({}, {});
361
+ expect(result.content).toEqual([{ type: "text", text: "Single block" }]);
362
+ });
363
+ it("should pass through ContentBlock array unchanged", async () => {
364
+ const blocks = [
365
+ { type: "text", text: "A" },
366
+ { type: "text", text: "B" },
367
+ ];
368
+ const mockToolCallback = vi.fn().mockResolvedValue({
369
+ content: blocks,
370
+ structuredContent: {},
371
+ });
372
+ server.registerTool({
373
+ name: "array-content",
374
+ description: "Test tool",
375
+ view: { component: "array-content" },
376
+ }, mockToolCallback);
377
+ const wrappedCallback = mockRegisterTool.mock.calls[0]?.[2];
378
+ const result = await wrappedCallback({}, {});
379
+ expect(result.content).toEqual(blocks);
380
+ });
381
+ it("should register tool without view (no resource registration)", () => {
382
+ server.registerTool({
383
+ name: "plain-tool",
384
+ description: "No view",
385
+ }, vi.fn());
386
+ expect(mockRegisterResource).not.toHaveBeenCalled();
327
387
  expect(mockRegisterTool).toHaveBeenCalledTimes(1);
388
+ });
389
+ it("should apply view.csp fields to resource _meta", async () => {
390
+ server.registerTool({
391
+ name: "csp-tool",
392
+ description: "Test tool",
393
+ view: {
394
+ component: "csp-tool",
395
+ description: "Test view",
396
+ csp: {
397
+ connectDomains: ["https://api.example.com"],
398
+ resourceDomains: ["https://cdn.example.com"],
399
+ },
400
+ },
401
+ }, vi.fn());
402
+ const appsSdkCallback = mockRegisterResource.mock
403
+ .calls[0]?.[3];
404
+ const host = "localhost:3000";
405
+ const serverUrl = `http://${host}`;
406
+ const hmrUrl = `ws://${host}`;
407
+ const result = await appsSdkCallback(new URL("ui://widgets/apps-sdk/csp-tool.html"), createMockExtra(host));
408
+ const meta = result.contents[0]?._meta;
409
+ expect(meta["openai/widgetCSP"]).toEqual({
410
+ resource_domains: [serverUrl, "https://cdn.example.com"],
411
+ connect_domains: [serverUrl, hmrUrl, "https://api.example.com"],
412
+ });
413
+ });
414
+ it("should let view._meta override framework-computed keys", async () => {
415
+ server.registerTool({
416
+ name: "override-tool",
417
+ description: "Test tool",
418
+ view: {
419
+ component: "override-tool",
420
+ description: "Test view",
421
+ csp: { connectDomains: ["https://api.x.com"] },
422
+ _meta: {
423
+ "openai/widgetCSP": {
424
+ connect_domains: ["https://api.y.com"],
425
+ },
426
+ },
427
+ },
428
+ }, vi.fn());
429
+ const appsSdkCallback = mockRegisterResource.mock
430
+ .calls[0]?.[3];
431
+ const result = await appsSdkCallback(new URL("ui://widgets/apps-sdk/override-tool.html"), createMockExtra("localhost:3000"));
432
+ const meta = result.contents[0]?._meta;
433
+ expect(meta["openai/widgetCSP"]).toEqual({
434
+ connect_domains: ["https://api.y.com"],
435
+ });
436
+ });
437
+ it("should pass user _meta keys through to tool config", () => {
438
+ server.registerTool({
439
+ name: "meta-tool",
440
+ description: "Test tool",
441
+ _meta: {
442
+ "openai/widgetAccessible": true,
443
+ "openai/toolInvocation/invoking": "Loading...",
444
+ "acme.com/category": "utility",
445
+ },
446
+ }, vi.fn());
328
447
  const toolCallArgs = mockRegisterTool.mock.calls[0];
329
448
  const toolConfig = toolCallArgs?.[1];
330
- expect(toolConfig._meta).not.toHaveProperty("ui");
331
- expect(toolConfig._meta?.["openai/outputTemplate"]).to.eq("ui://widgets/apps-sdk/my-widget.html");
449
+ expect(toolConfig._meta?.["openai/widgetAccessible"]).toBe(true);
450
+ expect(toolConfig._meta?.["openai/toolInvocation/invoking"]).toBe("Loading...");
451
+ expect(toolConfig._meta?.["acme.com/category"]).toBe("utility");
332
452
  });
333
453
  });
334
- //# sourceMappingURL=widget.test.js.map
454
+ //# sourceMappingURL=view.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"view.test.js","sourceRoot":"","sources":["../../src/test/view.test.ts"],"names":[],"mappings":"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"}
@@ -1,8 +1,8 @@
1
1
  import type { CallToolResult } from "@modelcontextprotocol/sdk/types.js";
2
2
  import type { useSyncExternalStore } from "react";
3
- import type { WidgetHostType } from "../../server/index.js";
3
+ import type { ViewHostType } from "../../server/index.js";
4
4
  export type SkybridgeProperties = {
5
- hostType: WidgetHostType;
5
+ hostType: ViewHostType;
6
6
  serverUrl: string;
7
7
  };
8
8
  declare global {
@@ -15,7 +15,7 @@ type TypedToolInfoReturn<TInput, TOutput, TResponseMetadata> = ToolState<Objecti
15
15
  * Creates typed versions of skybridge hooks with full type inference
16
16
  * for tool names, inputs, and outputs.
17
17
  *
18
- * This is the recommended way to use skybridge hooks in your widgets.
18
+ * This is the recommended way to use skybridge hooks in your views.
19
19
  * Set this up once in a dedicated file and export the typed hooks for use across your app.
20
20
  *
21
21
  * @typeParam ServerType - The type of your McpServer instance (use `typeof server`).
@@ -24,11 +24,13 @@ type TypedToolInfoReturn<TInput, TOutput, TResponseMetadata> = ToolState<Objecti
24
24
  *
25
25
  * @example
26
26
  * ```typescript
27
- * // server/src/index.ts
27
+ * // src/server.ts
28
28
  * const server = new McpServer({ name: "my-app", version: "1.0" }, {})
29
- * .widget("search-voyage", {}, {
29
+ * .registerTool({
30
+ * name: "search-trip",
30
31
  * inputSchema: { destination: z.string() },
31
32
  * outputSchema: { results: z.array(z.string()) },
33
+ * view: { component: "search-trip", description: "Search trips" },
32
34
  * }, async ({ destination }) => {
33
35
  * return { content: [{ type: "text", text: `Found trips to ${destination}` }] };
34
36
  * });
@@ -38,8 +40,8 @@ type TypedToolInfoReturn<TInput, TOutput, TResponseMetadata> = ToolState<Objecti
38
40
  *
39
41
  * @example
40
42
  * ```typescript
41
- * // web/src/helpers.ts (one-time setup)
42
- * import type { AppType } from "../server";
43
+ * // src/helpers.ts (one-time setup)
44
+ * import type { AppType } from "./server";
43
45
  * import { generateHelpers } from "skybridge/web";
44
46
  *
45
47
  * export const { useCallTool, useToolInfo } = generateHelpers<AppType>();
@@ -47,19 +49,19 @@ type TypedToolInfoReturn<TInput, TOutput, TResponseMetadata> = ToolState<Objecti
47
49
  *
48
50
  * @example
49
51
  * ```typescript
50
- * // web/src/widgets/search.tsx (usage)
51
- * import { useCallTool, useToolInfo } from "../skybridge";
52
+ * // src/views/search.tsx (usage)
53
+ * import { useCallTool, useToolInfo } from "../helpers";
52
54
  *
53
- * export function SearchWidget() {
54
- * const { callTool, data } = useCallTool("search-voyage");
55
+ * export function SearchView() {
56
+ * const { callTool, data } = useCallTool("search-trip");
55
57
  * // ^ autocomplete for tool names
56
58
  * callTool({ destination: "Spain" });
57
59
  * // ^ autocomplete for input fields
58
60
  *
59
- * const toolInfo = useToolInfo<"search-voyage">();
60
- * // ^ autocomplete for widget names
61
- * // toolInfo.input is typed based on widget input schema
62
- * // toolInfo.output.structuredContent is typed based on widget output schema
61
+ * const toolInfo = useToolInfo<"search-trip">();
62
+ * // ^ autocomplete for view names
63
+ * // toolInfo.input is typed based on view input schema
64
+ * // toolInfo.output.structuredContent is typed based on view output schema
63
65
  * }
64
66
  * ```
65
67
  */
@@ -68,12 +70,12 @@ export declare function generateHelpers<ServerType = never>(): {
68
70
  * Typed version of `useCallTool` that provides autocomplete for tool names
69
71
  * and type inference for inputs and outputs.
70
72
  *
71
- * @param name - The name of the widget to call. Autocompletes based on your server's widget registry.
73
+ * @param name - The name of the tool to call. Autocompletes based on your server's tool registry.
72
74
  * @returns A hook with typed `callTool` function and `data` property.
73
75
  *
74
76
  * @example
75
77
  * ```typescript
76
- * const { callTool, data, isPending } = useCallTool("search-voyage");
78
+ * const { callTool, data, isPending } = useCallTool("search-trip");
77
79
  * // TypeScript knows callTool expects { destination: string }
78
80
  * callTool({ destination: "Spain" });
79
81
  *
@@ -85,15 +87,15 @@ export declare function generateHelpers<ServerType = never>(): {
85
87
  */
86
88
  useCallTool: <ToolName extends keyof InferTools<ServerType> & string>(name: ToolName) => TypedCallToolReturn<ToolInput<ServerType, ToolName>, ToolOutput<ServerType, ToolName>>;
87
89
  /**
88
- * Typed version of `useToolInfo` that provides autocomplete for widget names
90
+ * Typed version of `useToolInfo` that provides autocomplete for tool names
89
91
  * and type inference for inputs, outputs, and responseMetadata.
90
92
  *
91
- * @typeParam K - The name of the widget. Autocompletes based on your server's widget registry.
93
+ * @typeParam K - The name of the tool. Autocompletes based on your server's tool registry.
92
94
  * @returns A discriminated union with `status: "pending" | "success"` that narrows correctly.
93
95
  *
94
96
  * @example
95
97
  * ```typescript
96
- * const toolInfo = useToolInfo<"search-voyage">();
98
+ * const toolInfo = useToolInfo<"search-trip">();
97
99
  * // toolInfo.input is typed as { destination: string; ... }
98
100
  * // toolInfo.output is typed as { results: Array<...>; ... }
99
101
  * // toolInfo.responseMetadata is typed based on _meta in callback return