skybridge 2.0.0-beta.5f8bd6f → 2.0.0-beta.78ceed1

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 (98) hide show
  1. package/dist/cli/detect-port.js +0 -1
  2. package/dist/cli/detect-port.js.map +1 -1
  3. package/dist/cli/use-nodemon.js +10 -2
  4. package/dist/cli/use-nodemon.js.map +1 -1
  5. package/dist/cli/use-typescript-check.js +1 -1
  6. package/dist/cli/use-typescript-check.js.map +1 -1
  7. package/dist/server/app.d.ts +41 -13
  8. package/dist/server/app.js +67 -26
  9. package/dist/server/app.js.map +1 -1
  10. package/dist/server/auth/discovery.d.ts +1 -1
  11. package/dist/server/auth/discovery.js +1 -1
  12. package/dist/server/auth/discovery.js.map +1 -1
  13. package/dist/server/express.d.ts +10 -1
  14. package/dist/server/express.js +25 -1
  15. package/dist/server/express.js.map +1 -1
  16. package/dist/server/factory-loader.test.d.ts +1 -0
  17. package/dist/server/factory-loader.test.js +81 -0
  18. package/dist/server/factory-loader.test.js.map +1 -0
  19. package/dist/server/file-ref.d.ts +1 -1
  20. package/dist/server/file-ref.js +1 -1
  21. package/dist/server/file-ref.js.map +1 -1
  22. package/dist/server/index.d.ts +1 -2
  23. package/dist/server/index.js.map +1 -1
  24. package/dist/server/middleware.test.js +10 -10
  25. package/dist/server/middleware.test.js.map +1 -1
  26. package/dist/server/register-tool.test.js +1 -1
  27. package/dist/server/register-tool.test.js.map +1 -1
  28. package/dist/server/server.d.ts +12 -19
  29. package/dist/server/server.js +1 -4
  30. package/dist/server/server.js.map +1 -1
  31. package/dist/server/skills-integration.test.js +1 -1
  32. package/dist/server/skills-integration.test.js.map +1 -1
  33. package/dist/server/skills.js +1 -1
  34. package/dist/server/skills.js.map +1 -1
  35. package/dist/server/templateHelper.d.ts +0 -3
  36. package/dist/server/templateHelper.js.map +1 -1
  37. package/dist/server/templates.generated.js +4 -8
  38. package/dist/server/templates.generated.js.map +1 -1
  39. package/dist/server/tool-extra.test.js +2 -2
  40. package/dist/server/tool-extra.test.js.map +1 -1
  41. package/dist/server/view-resource-resolution.test.js +1 -1
  42. package/dist/server/view-resource-resolution.test.js.map +1 -1
  43. package/dist/test/view.test.js +2 -2
  44. package/dist/test/view.test.js.map +1 -1
  45. package/dist/web/bridges/adaptor.test.js +1 -1
  46. package/dist/web/bridges/adaptor.test.js.map +1 -1
  47. package/dist/web/bridges/get-adaptor.test.js +2 -2
  48. package/dist/web/bridges/get-adaptor.test.js.map +1 -1
  49. package/dist/web/bridges/mcp-app/bridge.js +1 -1
  50. package/dist/web/bridges/mcp-app/bridge.js.map +1 -1
  51. package/dist/web/bridges/mcp-app/bridge.test.js +2 -2
  52. package/dist/web/bridges/mcp-app/bridge.test.js.map +1 -1
  53. package/dist/web/bridges/mcp-app/use-mcp-app-context.test.js +1 -1
  54. package/dist/web/bridges/mcp-app/use-mcp-app-context.test.js.map +1 -1
  55. package/dist/web/bridges/mcp-app/view-tools.test.js +1 -1
  56. package/dist/web/bridges/mcp-app/view-tools.test.js.map +1 -1
  57. package/dist/web/bridges/types.d.ts +6 -9
  58. package/dist/web/bridges/types.js.map +1 -1
  59. package/dist/web/create-store.test.js +2 -2
  60. package/dist/web/create-store.test.js.map +1 -1
  61. package/dist/web/data-llm.test.js +2 -2
  62. package/dist/web/data-llm.test.js.map +1 -1
  63. package/dist/web/hooks/use-call-tool.test.js +2 -2
  64. package/dist/web/hooks/use-call-tool.test.js.map +1 -1
  65. package/dist/web/hooks/use-display-mode.test.js +1 -1
  66. package/dist/web/hooks/use-display-mode.test.js.map +1 -1
  67. package/dist/web/hooks/use-download.test.js +3 -3
  68. package/dist/web/hooks/use-download.test.js.map +1 -1
  69. package/dist/web/hooks/use-files.test.js +1 -1
  70. package/dist/web/hooks/use-files.test.js.map +1 -1
  71. package/dist/web/hooks/use-host.test.js +1 -1
  72. package/dist/web/hooks/use-host.test.js.map +1 -1
  73. package/dist/web/hooks/use-open-external.test.js +2 -2
  74. package/dist/web/hooks/use-open-external.test.js.map +1 -1
  75. package/dist/web/hooks/use-request-close.test.js +1 -1
  76. package/dist/web/hooks/use-request-close.test.js.map +1 -1
  77. package/dist/web/hooks/use-request-modal.test.js +1 -1
  78. package/dist/web/hooks/use-request-modal.test.js.map +1 -1
  79. package/dist/web/hooks/use-request-size.test.js +1 -1
  80. package/dist/web/hooks/use-request-size.test.js.map +1 -1
  81. package/dist/web/hooks/use-set-open-in-app-url.test.js +2 -8
  82. package/dist/web/hooks/use-set-open-in-app-url.test.js.map +1 -1
  83. package/dist/web/hooks/use-tool-info.test.js +2 -2
  84. package/dist/web/hooks/use-tool-info.test.js.map +1 -1
  85. package/dist/web/hooks/use-user.test.js +2 -2
  86. package/dist/web/hooks/use-user.test.js.map +1 -1
  87. package/dist/web/hooks/use-view-state.test.js +2 -2
  88. package/dist/web/hooks/use-view-state.test.js.map +1 -1
  89. package/dist/web/hooks/use-viewport.test.js +2 -2
  90. package/dist/web/hooks/use-viewport.test.js.map +1 -1
  91. package/dist/web/index.d.ts +0 -1
  92. package/dist/web/index.js.map +1 -1
  93. package/dist/web/mount-view.js +2 -6
  94. package/dist/web/mount-view.js.map +1 -1
  95. package/package.json +5 -4
  96. package/dist/standard-schema.d.ts +0 -3
  97. package/dist/standard-schema.js +0 -2
  98. package/dist/standard-schema.js.map +0 -1
@@ -1 +1 @@
1
- {"version":3,"file":"adaptor.test.js","sourceRoot":"","sources":["../../../src/web/bridges/adaptor.test.ts"],"names":[],"mappings":"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","sourcesContent":["import { afterEach, beforeEach, describe, expect, it, vi } from \"vitest\";\nimport { VIEW_STATE_WARNING_TOKENS } from \"../../context-warnings.js\";\nimport { HostAdaptor } from \"./adaptor.js\";\nimport { AppsSdkBridge } from \"./apps-sdk/bridge.js\";\nimport { McpAppBridge } from \"./mcp-app/bridge.js\";\nimport { NotSupportedError } from \"./types.js\";\n\ndescribe(\"HostAdaptor\", () => {\n beforeEach(() => {\n McpAppBridge.resetInstance();\n AppsSdkBridge.resetInstance();\n vi.stubGlobal(\"skybridge\", { hostType: \"mcp-app\" });\n vi.stubGlobal(\"parent\", { postMessage: vi.fn() });\n localStorage.clear();\n });\n afterEach(() => {\n vi.unstubAllGlobals();\n McpAppBridge.resetInstance();\n AppsSdkBridge.resetInstance();\n localStorage.clear();\n });\n\n it(\"callTool always routes to mcp.callServerTool\", async () => {\n vi.stubGlobal(\"openai\", { callTool: vi.fn() });\n const adaptor = new HostAdaptor();\n const fakeApp = {\n callServerTool: vi.fn().mockResolvedValue({\n content: [],\n structuredContent: { ok: true },\n isError: false,\n _meta: { x: 1 },\n }),\n };\n // biome-ignore lint/suspicious/noExplicitAny: test seam\n (adaptor as any).mcp.getApp = vi.fn().mockResolvedValue(fakeApp);\n\n const result = await adaptor.callTool(\"greet\", { name: \"alice\" });\n expect(fakeApp.callServerTool).toHaveBeenCalledWith({\n name: \"greet\",\n arguments: { name: \"alice\" },\n });\n expect(result.structuredContent).toEqual({ ok: true });\n expect(result.meta).toEqual({ x: 1 });\n });\n\n it(\"download returns isError when host lacks downloadFile capability\", async () => {\n vi.stubGlobal(\"openai\", undefined);\n const adaptor = new HostAdaptor();\n const fakeApp = {\n getHostCapabilities: () => ({}),\n downloadFile: vi.fn(),\n };\n // biome-ignore lint/suspicious/noExplicitAny: test seam\n (adaptor as any).mcp.getApp = vi.fn().mockResolvedValue(fakeApp);\n const r = await adaptor.download({ contents: [] });\n expect(r.isError).toBe(true);\n expect(fakeApp.downloadFile).not.toHaveBeenCalled();\n });\n\n it(\"sendFollowUpMessage routes to window.openai only when scrollToBottom is set\", async () => {\n const sendFollowUpMessage = vi.fn().mockResolvedValue(undefined);\n vi.stubGlobal(\"openai\", { sendFollowUpMessage });\n let adaptor = new HostAdaptor();\n await adaptor.sendFollowUpMessage(\"hi\", { scrollToBottom: false });\n expect(sendFollowUpMessage).toHaveBeenCalledWith({\n prompt: \"hi\",\n scrollToBottom: false,\n });\n\n // Default case falls through to MCP\n McpAppBridge.resetInstance();\n AppsSdkBridge.resetInstance();\n vi.stubGlobal(\"openai\", undefined);\n adaptor = new HostAdaptor();\n const sendMessage = vi.fn().mockResolvedValue(undefined);\n // biome-ignore lint/suspicious/noExplicitAny: test seam\n (adaptor as any).mcp.getApp = vi.fn().mockResolvedValue({ sendMessage });\n await adaptor.sendFollowUpMessage(\"hi\");\n expect(sendMessage).toHaveBeenCalledWith({\n role: \"user\",\n content: [{ type: \"text\", text: \"hi\" }],\n });\n });\n\n it(\"openExternal routes to window.openai only when redirectUrl: false\", async () => {\n const openExternal = vi.fn();\n vi.stubGlobal(\"openai\", { openExternal });\n let adaptor = new HostAdaptor();\n adaptor.openExternal(\"https://x\", { redirectUrl: false });\n expect(openExternal).toHaveBeenCalledWith({\n href: \"https://x\",\n redirectUrl: false,\n });\n\n // Default case falls through to MCP openLink\n McpAppBridge.resetInstance();\n AppsSdkBridge.resetInstance();\n vi.stubGlobal(\"openai\", undefined);\n adaptor = new HostAdaptor();\n const openLink = vi.fn();\n // biome-ignore lint/suspicious/noExplicitAny: test seam\n (adaptor as any).mcp.getApp = vi.fn().mockResolvedValue({ openLink });\n adaptor.openExternal(\"https://x\");\n await new Promise((r) => setTimeout(r, 0));\n expect(openLink).toHaveBeenCalledWith({ url: \"https://x\" });\n });\n\n it(\"Apps-SDK-exclusive methods throw NotSupportedError when window.openai is null\", async () => {\n vi.stubGlobal(\"openai\", undefined);\n const adaptor = new HostAdaptor();\n await expect(adaptor.uploadFile(new File([], \"x\"))).rejects.toThrow(\n NotSupportedError,\n );\n await expect(adaptor.selectFiles()).rejects.toThrow(NotSupportedError);\n await expect(adaptor.getFileDownloadUrl({ fileId: \"x\" })).rejects.toThrow(\n NotSupportedError,\n );\n await expect(adaptor.setOpenInAppUrl(\"https://x\")).rejects.toThrow(\n NotSupportedError,\n );\n });\n\n it(\"uploadFile delegates to window.openai.uploadFile and tracks fileId in widgetState\", async () => {\n const setWidgetState = vi.fn().mockResolvedValue(undefined);\n const uploadFile = vi.fn().mockResolvedValue({\n fileId: \"abc\",\n fileName: \"x\",\n mimeType: \"image/png\",\n });\n vi.stubGlobal(\"openai\", { uploadFile, setWidgetState, widgetState: null });\n const adaptor = new HostAdaptor();\n const file = new File([], \"x\", { type: \"image/png\" });\n const r = await adaptor.uploadFile(file, { library: true });\n expect(uploadFile).toHaveBeenCalledWith(file, { library: true });\n expect(r.fileId).toBe(\"abc\");\n expect(setWidgetState).toHaveBeenCalledWith(\n expect.objectContaining({ imageIds: [\"abc\"] }),\n );\n });\n\n it(\"uploadFile does not track a non-image fileId in widgetState.imageIds\", async () => {\n const setWidgetState = vi.fn().mockResolvedValue(undefined);\n const uploadFile = vi.fn().mockResolvedValue({\n fileId: \"sediment://file_txt\",\n fileName: \"notes.txt\",\n mimeType: \"text/plain\",\n });\n vi.stubGlobal(\"openai\", { uploadFile, setWidgetState, widgetState: null });\n const adaptor = new HostAdaptor();\n\n await adaptor.uploadFile(new File([], \"notes.txt\", { type: \"text/plain\" }));\n\n for (const call of setWidgetState.mock.calls) {\n expect(call[0].imageIds ?? []).not.toContain(\"sediment://file_txt\");\n }\n });\n\n it(\"setViewState uses window.openai.setWidgetState when present\", async () => {\n const setWidgetState = vi.fn().mockResolvedValue(undefined);\n vi.stubGlobal(\"openai\", { setWidgetState, widgetState: null });\n const adaptor = new HostAdaptor();\n await adaptor.setViewState({ count: 1 });\n expect(setWidgetState).toHaveBeenCalledWith(\n expect.objectContaining({ modelContent: { count: 1 } }),\n );\n });\n\n it(\"warns when setViewState reaches 20K tokens\", async () => {\n const setWidgetState = vi.fn().mockResolvedValue(undefined);\n vi.stubGlobal(\"openai\", { setWidgetState, widgetState: null });\n const adaptor = new HostAdaptor();\n const warnSpy = vi.spyOn(console, \"warn\").mockImplementation(() => {});\n\n await adaptor.setViewState({\n longText: \"x\".repeat(VIEW_STATE_WARNING_TOKENS * 4),\n });\n\n expect(warnSpy).toHaveBeenCalledWith(\n expect.stringContaining(\"setWidgetState is persisting\"),\n );\n\n warnSpy.mockRestore();\n });\n\n it(\"setViewState falls back to MCP updateModelContext + localStorage when window.openai is absent\", async () => {\n vi.stubGlobal(\"openai\", undefined);\n const adaptor = new HostAdaptor();\n const updateModelContext = vi.fn().mockResolvedValue(undefined);\n // biome-ignore lint/suspicious/noExplicitAny: test seam\n (adaptor as any).mcp.getApp = vi\n .fn()\n .mockResolvedValue({ updateModelContext });\n // biome-ignore lint/suspicious/noExplicitAny: test seam\n (adaptor as any)._viewUUID = \"view-1\";\n await adaptor.setViewState({ count: 2 });\n expect(updateModelContext).toHaveBeenCalledWith({\n structuredContent: { count: 2 },\n content: [{ type: \"text\", text: JSON.stringify({ count: 2 }) }],\n });\n const keys: string[] = [];\n for (let i = 0; i < localStorage.length; i++) {\n const key = localStorage.key(i);\n if (key !== null) {\n keys.push(key);\n }\n }\n expect(keys.some((k) => k.endsWith(\":view-1\"))).toBe(true);\n });\n\n it(\"openModal/closeModal: delegates to window.openai.requestModal when present, polyfill state otherwise\", () => {\n const requestModal = vi.fn();\n vi.stubGlobal(\"openai\", { requestModal });\n let adaptor = new HostAdaptor();\n adaptor.openModal({ title: \"Hi\" });\n expect(requestModal).toHaveBeenCalledWith({ title: \"Hi\" });\n expect(() => adaptor.closeModal()).not.toThrow();\n\n // Polyfill path\n McpAppBridge.resetInstance();\n AppsSdkBridge.resetInstance();\n vi.stubGlobal(\"openai\", undefined);\n adaptor = new HostAdaptor();\n adaptor.openModal({ params: { x: 1 } });\n // biome-ignore lint/suspicious/noExplicitAny: test seam\n expect((adaptor as any)._polyfillDisplay).toEqual({\n mode: \"modal\",\n params: { x: 1 },\n });\n adaptor.closeModal();\n // biome-ignore lint/suspicious/noExplicitAny: test seam\n expect((adaptor as any)._polyfillDisplay).toEqual({ mode: \"inline\" });\n });\n\n it(\"getHostContextStore: display/viewState route to Apps SDK overlay when present, MCP otherwise\", () => {\n vi.stubGlobal(\"openai\", {\n view: { mode: \"fullscreen\" },\n widgetState: { modelContent: { count: 5 }, privateContent: {} },\n });\n let adaptor = new HostAdaptor();\n expect(adaptor.getHostContextStore(\"display\").getSnapshot()).toEqual({\n mode: \"fullscreen\",\n });\n expect(adaptor.getHostContextStore(\"viewState\").getSnapshot()).toEqual({\n count: 5,\n });\n // Other keys (theme) always flow through MCP; default fallback when no notification\n expect(adaptor.getHostContextStore(\"theme\").getSnapshot()).toBe(\"light\");\n\n // Polyfill / local-state path\n McpAppBridge.resetInstance();\n AppsSdkBridge.resetInstance();\n vi.stubGlobal(\"openai\", undefined);\n adaptor = new HostAdaptor();\n expect(adaptor.getHostContextStore(\"display\").getSnapshot()).toEqual({\n mode: \"inline\",\n });\n expect(adaptor.getHostContextStore(\"viewState\").getSnapshot()).toBe(null);\n });\n});\n"]}
1
+ {"version":3,"file":"adaptor.test.js","sourceRoot":"","sources":["../../../src/web/bridges/adaptor.test.ts"],"names":[],"mappings":"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","sourcesContent":["import { afterEach, beforeEach, describe, expect, it, vi } from \"vitest\";\nimport { VIEW_STATE_WARNING_TOKENS } from \"../../context-warnings.js\";\nimport { HostAdaptor } from \"./adaptor.js\";\nimport { AppsSdkBridge } from \"./apps-sdk/bridge.js\";\nimport { McpAppBridge } from \"./mcp-app/bridge.js\";\nimport { NotSupportedError } from \"./types.js\";\n\ndescribe(\"HostAdaptor\", () => {\n beforeEach(() => {\n McpAppBridge.resetInstance();\n AppsSdkBridge.resetInstance();\n vi.stubGlobal(\"skybridge\", {});\n vi.stubGlobal(\"parent\", { postMessage: vi.fn() });\n localStorage.clear();\n });\n afterEach(() => {\n vi.unstubAllGlobals();\n McpAppBridge.resetInstance();\n AppsSdkBridge.resetInstance();\n localStorage.clear();\n });\n\n it(\"callTool always routes to mcp.callServerTool\", async () => {\n vi.stubGlobal(\"openai\", { callTool: vi.fn() });\n const adaptor = new HostAdaptor();\n const fakeApp = {\n callServerTool: vi.fn().mockResolvedValue({\n content: [],\n structuredContent: { ok: true },\n isError: false,\n _meta: { x: 1 },\n }),\n };\n // biome-ignore lint/suspicious/noExplicitAny: test seam\n (adaptor as any).mcp.getApp = vi.fn().mockResolvedValue(fakeApp);\n\n const result = await adaptor.callTool(\"greet\", { name: \"alice\" });\n expect(fakeApp.callServerTool).toHaveBeenCalledWith({\n name: \"greet\",\n arguments: { name: \"alice\" },\n });\n expect(result.structuredContent).toEqual({ ok: true });\n expect(result.meta).toEqual({ x: 1 });\n });\n\n it(\"download returns isError when host lacks downloadFile capability\", async () => {\n vi.stubGlobal(\"openai\", undefined);\n const adaptor = new HostAdaptor();\n const fakeApp = {\n getHostCapabilities: () => ({}),\n downloadFile: vi.fn(),\n };\n // biome-ignore lint/suspicious/noExplicitAny: test seam\n (adaptor as any).mcp.getApp = vi.fn().mockResolvedValue(fakeApp);\n const r = await adaptor.download({ contents: [] });\n expect(r.isError).toBe(true);\n expect(fakeApp.downloadFile).not.toHaveBeenCalled();\n });\n\n it(\"sendFollowUpMessage routes to window.openai only when scrollToBottom is set\", async () => {\n const sendFollowUpMessage = vi.fn().mockResolvedValue(undefined);\n vi.stubGlobal(\"openai\", { sendFollowUpMessage });\n let adaptor = new HostAdaptor();\n await adaptor.sendFollowUpMessage(\"hi\", { scrollToBottom: false });\n expect(sendFollowUpMessage).toHaveBeenCalledWith({\n prompt: \"hi\",\n scrollToBottom: false,\n });\n\n // Default case falls through to MCP\n McpAppBridge.resetInstance();\n AppsSdkBridge.resetInstance();\n vi.stubGlobal(\"openai\", undefined);\n adaptor = new HostAdaptor();\n const sendMessage = vi.fn().mockResolvedValue(undefined);\n // biome-ignore lint/suspicious/noExplicitAny: test seam\n (adaptor as any).mcp.getApp = vi.fn().mockResolvedValue({ sendMessage });\n await adaptor.sendFollowUpMessage(\"hi\");\n expect(sendMessage).toHaveBeenCalledWith({\n role: \"user\",\n content: [{ type: \"text\", text: \"hi\" }],\n });\n });\n\n it(\"openExternal routes to window.openai only when redirectUrl: false\", async () => {\n const openExternal = vi.fn();\n vi.stubGlobal(\"openai\", { openExternal });\n let adaptor = new HostAdaptor();\n adaptor.openExternal(\"https://x\", { redirectUrl: false });\n expect(openExternal).toHaveBeenCalledWith({\n href: \"https://x\",\n redirectUrl: false,\n });\n\n // Default case falls through to MCP openLink\n McpAppBridge.resetInstance();\n AppsSdkBridge.resetInstance();\n vi.stubGlobal(\"openai\", undefined);\n adaptor = new HostAdaptor();\n const openLink = vi.fn();\n // biome-ignore lint/suspicious/noExplicitAny: test seam\n (adaptor as any).mcp.getApp = vi.fn().mockResolvedValue({ openLink });\n adaptor.openExternal(\"https://x\");\n await new Promise((r) => setTimeout(r, 0));\n expect(openLink).toHaveBeenCalledWith({ url: \"https://x\" });\n });\n\n it(\"Apps-SDK-exclusive methods throw NotSupportedError when window.openai is null\", async () => {\n vi.stubGlobal(\"openai\", undefined);\n const adaptor = new HostAdaptor();\n await expect(adaptor.uploadFile(new File([], \"x\"))).rejects.toThrow(\n NotSupportedError,\n );\n await expect(adaptor.selectFiles()).rejects.toThrow(NotSupportedError);\n await expect(adaptor.getFileDownloadUrl({ fileId: \"x\" })).rejects.toThrow(\n NotSupportedError,\n );\n await expect(adaptor.setOpenInAppUrl(\"https://x\")).rejects.toThrow(\n NotSupportedError,\n );\n });\n\n it(\"uploadFile delegates to window.openai.uploadFile and tracks fileId in widgetState\", async () => {\n const setWidgetState = vi.fn().mockResolvedValue(undefined);\n const uploadFile = vi.fn().mockResolvedValue({\n fileId: \"abc\",\n fileName: \"x\",\n mimeType: \"image/png\",\n });\n vi.stubGlobal(\"openai\", { uploadFile, setWidgetState, widgetState: null });\n const adaptor = new HostAdaptor();\n const file = new File([], \"x\", { type: \"image/png\" });\n const r = await adaptor.uploadFile(file, { library: true });\n expect(uploadFile).toHaveBeenCalledWith(file, { library: true });\n expect(r.fileId).toBe(\"abc\");\n expect(setWidgetState).toHaveBeenCalledWith(\n expect.objectContaining({ imageIds: [\"abc\"] }),\n );\n });\n\n it(\"uploadFile does not track a non-image fileId in widgetState.imageIds\", async () => {\n const setWidgetState = vi.fn().mockResolvedValue(undefined);\n const uploadFile = vi.fn().mockResolvedValue({\n fileId: \"sediment://file_txt\",\n fileName: \"notes.txt\",\n mimeType: \"text/plain\",\n });\n vi.stubGlobal(\"openai\", { uploadFile, setWidgetState, widgetState: null });\n const adaptor = new HostAdaptor();\n\n await adaptor.uploadFile(new File([], \"notes.txt\", { type: \"text/plain\" }));\n\n for (const call of setWidgetState.mock.calls) {\n expect(call[0].imageIds ?? []).not.toContain(\"sediment://file_txt\");\n }\n });\n\n it(\"setViewState uses window.openai.setWidgetState when present\", async () => {\n const setWidgetState = vi.fn().mockResolvedValue(undefined);\n vi.stubGlobal(\"openai\", { setWidgetState, widgetState: null });\n const adaptor = new HostAdaptor();\n await adaptor.setViewState({ count: 1 });\n expect(setWidgetState).toHaveBeenCalledWith(\n expect.objectContaining({ modelContent: { count: 1 } }),\n );\n });\n\n it(\"warns when setViewState reaches 20K tokens\", async () => {\n const setWidgetState = vi.fn().mockResolvedValue(undefined);\n vi.stubGlobal(\"openai\", { setWidgetState, widgetState: null });\n const adaptor = new HostAdaptor();\n const warnSpy = vi.spyOn(console, \"warn\").mockImplementation(() => {});\n\n await adaptor.setViewState({\n longText: \"x\".repeat(VIEW_STATE_WARNING_TOKENS * 4),\n });\n\n expect(warnSpy).toHaveBeenCalledWith(\n expect.stringContaining(\"setWidgetState is persisting\"),\n );\n\n warnSpy.mockRestore();\n });\n\n it(\"setViewState falls back to MCP updateModelContext + localStorage when window.openai is absent\", async () => {\n vi.stubGlobal(\"openai\", undefined);\n const adaptor = new HostAdaptor();\n const updateModelContext = vi.fn().mockResolvedValue(undefined);\n // biome-ignore lint/suspicious/noExplicitAny: test seam\n (adaptor as any).mcp.getApp = vi\n .fn()\n .mockResolvedValue({ updateModelContext });\n // biome-ignore lint/suspicious/noExplicitAny: test seam\n (adaptor as any)._viewUUID = \"view-1\";\n await adaptor.setViewState({ count: 2 });\n expect(updateModelContext).toHaveBeenCalledWith({\n structuredContent: { count: 2 },\n content: [{ type: \"text\", text: JSON.stringify({ count: 2 }) }],\n });\n const keys: string[] = [];\n for (let i = 0; i < localStorage.length; i++) {\n const key = localStorage.key(i);\n if (key !== null) {\n keys.push(key);\n }\n }\n expect(keys.some((k) => k.endsWith(\":view-1\"))).toBe(true);\n });\n\n it(\"openModal/closeModal: delegates to window.openai.requestModal when present, polyfill state otherwise\", () => {\n const requestModal = vi.fn();\n vi.stubGlobal(\"openai\", { requestModal });\n let adaptor = new HostAdaptor();\n adaptor.openModal({ title: \"Hi\" });\n expect(requestModal).toHaveBeenCalledWith({ title: \"Hi\" });\n expect(() => adaptor.closeModal()).not.toThrow();\n\n // Polyfill path\n McpAppBridge.resetInstance();\n AppsSdkBridge.resetInstance();\n vi.stubGlobal(\"openai\", undefined);\n adaptor = new HostAdaptor();\n adaptor.openModal({ params: { x: 1 } });\n // biome-ignore lint/suspicious/noExplicitAny: test seam\n expect((adaptor as any)._polyfillDisplay).toEqual({\n mode: \"modal\",\n params: { x: 1 },\n });\n adaptor.closeModal();\n // biome-ignore lint/suspicious/noExplicitAny: test seam\n expect((adaptor as any)._polyfillDisplay).toEqual({ mode: \"inline\" });\n });\n\n it(\"getHostContextStore: display/viewState route to Apps SDK overlay when present, MCP otherwise\", () => {\n vi.stubGlobal(\"openai\", {\n view: { mode: \"fullscreen\" },\n widgetState: { modelContent: { count: 5 }, privateContent: {} },\n });\n let adaptor = new HostAdaptor();\n expect(adaptor.getHostContextStore(\"display\").getSnapshot()).toEqual({\n mode: \"fullscreen\",\n });\n expect(adaptor.getHostContextStore(\"viewState\").getSnapshot()).toEqual({\n count: 5,\n });\n // Other keys (theme) always flow through MCP; default fallback when no notification\n expect(adaptor.getHostContextStore(\"theme\").getSnapshot()).toBe(\"light\");\n\n // Polyfill / local-state path\n McpAppBridge.resetInstance();\n AppsSdkBridge.resetInstance();\n vi.stubGlobal(\"openai\", undefined);\n adaptor = new HostAdaptor();\n expect(adaptor.getHostContextStore(\"display\").getSnapshot()).toEqual({\n mode: \"inline\",\n });\n expect(adaptor.getHostContextStore(\"viewState\").getSnapshot()).toBe(null);\n });\n});\n"]}
@@ -17,12 +17,12 @@ describe("getAdaptor", () => {
17
17
  AppsSdkBridge.resetInstance();
18
18
  });
19
19
  it("returns a HostAdaptor instance", () => {
20
- vi.stubGlobal("skybridge", { hostType: "mcp-app" });
20
+ vi.stubGlobal("skybridge", {});
21
21
  vi.stubGlobal("openai", undefined);
22
22
  expect(getAdaptor()).toBeInstanceOf(HostAdaptor);
23
23
  });
24
24
  it("memoizes the instance", () => {
25
- vi.stubGlobal("skybridge", { hostType: "mcp-app" });
25
+ vi.stubGlobal("skybridge", {});
26
26
  vi.stubGlobal("openai", undefined);
27
27
  const a = getAdaptor();
28
28
  const b = getAdaptor();
@@ -1 +1 @@
1
- {"version":3,"file":"get-adaptor.test.js","sourceRoot":"","sources":["../../../src/web/bridges/get-adaptor.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,UAAU,EAAE,QAAQ,EAAE,MAAM,EAAE,EAAE,EAAE,EAAE,EAAE,MAAM,QAAQ,CAAC;AACzE,OAAO,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AAC3C,OAAO,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AACrD,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAC9C,OAAO,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AAEnD,QAAQ,CAAC,YAAY,EAAE,GAAG,EAAE;IAC1B,UAAU,CAAC,GAAG,EAAE;QACd,WAAW,CAAC,aAAa,EAAE,CAAC;QAC5B,YAAY,CAAC,aAAa,EAAE,CAAC;QAC7B,aAAa,CAAC,aAAa,EAAE,CAAC;QAC9B,EAAE,CAAC,UAAU,CAAC,QAAQ,EAAE,EAAE,WAAW,EAAE,EAAE,CAAC,EAAE,EAAE,EAAE,CAAC,CAAC;IACpD,CAAC,CAAC,CAAC;IACH,SAAS,CAAC,GAAG,EAAE;QACb,EAAE,CAAC,gBAAgB,EAAE,CAAC;QACtB,WAAW,CAAC,aAAa,EAAE,CAAC;QAC5B,YAAY,CAAC,aAAa,EAAE,CAAC;QAC7B,aAAa,CAAC,aAAa,EAAE,CAAC;IAChC,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,gCAAgC,EAAE,GAAG,EAAE;QACxC,EAAE,CAAC,UAAU,CAAC,WAAW,EAAE,EAAE,QAAQ,EAAE,SAAS,EAAE,CAAC,CAAC;QACpD,EAAE,CAAC,UAAU,CAAC,QAAQ,EAAE,SAAS,CAAC,CAAC;QACnC,MAAM,CAAC,UAAU,EAAE,CAAC,CAAC,cAAc,CAAC,WAAW,CAAC,CAAC;IACnD,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,uBAAuB,EAAE,GAAG,EAAE;QAC/B,EAAE,CAAC,UAAU,CAAC,WAAW,EAAE,EAAE,QAAQ,EAAE,SAAS,EAAE,CAAC,CAAC;QACpD,EAAE,CAAC,UAAU,CAAC,QAAQ,EAAE,SAAS,CAAC,CAAC;QACnC,MAAM,CAAC,GAAG,UAAU,EAAE,CAAC;QACvB,MAAM,CAAC,GAAG,UAAU,EAAE,CAAC;QACvB,MAAM,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IACpB,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC","sourcesContent":["import { afterEach, beforeEach, describe, expect, it, vi } from \"vitest\";\nimport { HostAdaptor } from \"./adaptor.js\";\nimport { AppsSdkBridge } from \"./apps-sdk/bridge.js\";\nimport { getAdaptor } from \"./get-adaptor.js\";\nimport { McpAppBridge } from \"./mcp-app/bridge.js\";\n\ndescribe(\"getAdaptor\", () => {\n beforeEach(() => {\n HostAdaptor.resetInstance();\n McpAppBridge.resetInstance();\n AppsSdkBridge.resetInstance();\n vi.stubGlobal(\"parent\", { postMessage: vi.fn() });\n });\n afterEach(() => {\n vi.unstubAllGlobals();\n HostAdaptor.resetInstance();\n McpAppBridge.resetInstance();\n AppsSdkBridge.resetInstance();\n });\n\n it(\"returns a HostAdaptor instance\", () => {\n vi.stubGlobal(\"skybridge\", { hostType: \"mcp-app\" });\n vi.stubGlobal(\"openai\", undefined);\n expect(getAdaptor()).toBeInstanceOf(HostAdaptor);\n });\n\n it(\"memoizes the instance\", () => {\n vi.stubGlobal(\"skybridge\", { hostType: \"mcp-app\" });\n vi.stubGlobal(\"openai\", undefined);\n const a = getAdaptor();\n const b = getAdaptor();\n expect(a).toBe(b);\n });\n});\n"]}
1
+ {"version":3,"file":"get-adaptor.test.js","sourceRoot":"","sources":["../../../src/web/bridges/get-adaptor.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,UAAU,EAAE,QAAQ,EAAE,MAAM,EAAE,EAAE,EAAE,EAAE,EAAE,MAAM,QAAQ,CAAC;AACzE,OAAO,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AAC3C,OAAO,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AACrD,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAC9C,OAAO,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AAEnD,QAAQ,CAAC,YAAY,EAAE,GAAG,EAAE;IAC1B,UAAU,CAAC,GAAG,EAAE;QACd,WAAW,CAAC,aAAa,EAAE,CAAC;QAC5B,YAAY,CAAC,aAAa,EAAE,CAAC;QAC7B,aAAa,CAAC,aAAa,EAAE,CAAC;QAC9B,EAAE,CAAC,UAAU,CAAC,QAAQ,EAAE,EAAE,WAAW,EAAE,EAAE,CAAC,EAAE,EAAE,EAAE,CAAC,CAAC;IACpD,CAAC,CAAC,CAAC;IACH,SAAS,CAAC,GAAG,EAAE;QACb,EAAE,CAAC,gBAAgB,EAAE,CAAC;QACtB,WAAW,CAAC,aAAa,EAAE,CAAC;QAC5B,YAAY,CAAC,aAAa,EAAE,CAAC;QAC7B,aAAa,CAAC,aAAa,EAAE,CAAC;IAChC,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,gCAAgC,EAAE,GAAG,EAAE;QACxC,EAAE,CAAC,UAAU,CAAC,WAAW,EAAE,EAAE,CAAC,CAAC;QAC/B,EAAE,CAAC,UAAU,CAAC,QAAQ,EAAE,SAAS,CAAC,CAAC;QACnC,MAAM,CAAC,UAAU,EAAE,CAAC,CAAC,cAAc,CAAC,WAAW,CAAC,CAAC;IACnD,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,uBAAuB,EAAE,GAAG,EAAE;QAC/B,EAAE,CAAC,UAAU,CAAC,WAAW,EAAE,EAAE,CAAC,CAAC;QAC/B,EAAE,CAAC,UAAU,CAAC,QAAQ,EAAE,SAAS,CAAC,CAAC;QACnC,MAAM,CAAC,GAAG,UAAU,EAAE,CAAC;QACvB,MAAM,CAAC,GAAG,UAAU,EAAE,CAAC;QACvB,MAAM,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IACpB,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC","sourcesContent":["import { afterEach, beforeEach, describe, expect, it, vi } from \"vitest\";\nimport { HostAdaptor } from \"./adaptor.js\";\nimport { AppsSdkBridge } from \"./apps-sdk/bridge.js\";\nimport { getAdaptor } from \"./get-adaptor.js\";\nimport { McpAppBridge } from \"./mcp-app/bridge.js\";\n\ndescribe(\"getAdaptor\", () => {\n beforeEach(() => {\n HostAdaptor.resetInstance();\n McpAppBridge.resetInstance();\n AppsSdkBridge.resetInstance();\n vi.stubGlobal(\"parent\", { postMessage: vi.fn() });\n });\n afterEach(() => {\n vi.unstubAllGlobals();\n HostAdaptor.resetInstance();\n McpAppBridge.resetInstance();\n AppsSdkBridge.resetInstance();\n });\n\n it(\"returns a HostAdaptor instance\", () => {\n vi.stubGlobal(\"skybridge\", {});\n vi.stubGlobal(\"openai\", undefined);\n expect(getAdaptor()).toBeInstanceOf(HostAdaptor);\n });\n\n it(\"memoizes the instance\", () => {\n vi.stubGlobal(\"skybridge\", {});\n vi.stubGlobal(\"openai\", undefined);\n const a = getAdaptor();\n const b = getAdaptor();\n expect(a).toBe(b);\n });\n});\n"]}
@@ -1,7 +1,7 @@
1
1
  import { App } from "@modelcontextprotocol/ext-apps";
2
2
  import { ListToolsRequestSchema, } from "@modelcontextprotocol/sdk/types.js";
3
3
  import { dequal } from "dequal/lite";
4
- import * as z from "zod";
4
+ import * as z from "zod/v4";
5
5
  function createMcpStore(bridge, keys, computeSnapshot) {
6
6
  let cachedValue;
7
7
  return {
@@ -1 +1 @@
1
- {"version":3,"file":"bridge.js","sourceRoot":"","sources":["../../../../src/web/bridges/mcp-app/bridge.ts"],"names":[],"mappings":"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","sourcesContent":["import { App } from \"@modelcontextprotocol/ext-apps\";\nimport {\n type Implementation,\n ListToolsRequestSchema,\n} from \"@modelcontextprotocol/sdk/types.js\";\nimport { dequal } from \"dequal/lite\";\nimport * as z from \"zod\";\nimport type {\n AnyViewToolHandler,\n Bridge,\n HostContextStore,\n Subscribe,\n ViewToolConfig,\n} from \"../types.js\";\nimport type { McpAppContext, McpAppContextKey } from \"./types.js\";\n\ntype PickContext<K extends readonly McpAppContextKey[]> = {\n [P in K[number]]: McpAppContext[P];\n};\n\nfunction createMcpStore<const Keys extends readonly McpAppContextKey[], R>(\n bridge: McpAppBridge,\n keys: Keys,\n computeSnapshot: (context: PickContext<Keys>) => R,\n) {\n let cachedValue: R | undefined;\n return {\n subscribe: bridge.subscribe(keys),\n getSnapshot: () => {\n const context = Object.fromEntries(\n keys.map((k) => [k, bridge.getSnapshot(k)]),\n ) as PickContext<Keys>;\n const newValue = computeSnapshot(context);\n if (cachedValue !== undefined && dequal(cachedValue, newValue)) {\n return cachedValue;\n }\n cachedValue = newValue;\n return newValue;\n },\n };\n}\n\n/** @internal Singleton bridge over the `ext-apps` JSON-RPC App connection. Used by `HostAdaptor`. */\nexport class McpAppBridge implements Bridge<McpAppContext> {\n private static instance: McpAppBridge | null = null;\n public context: McpAppContext = {\n toolInput: null,\n toolCancelled: null,\n toolResult: null,\n hostInfo: null,\n };\n private listeners = new Map<McpAppContextKey, Set<() => void>>();\n private app: App;\n private connectPromise: Promise<void>;\n\n constructor(options: { appInfo: Implementation }) {\n this.app = new App(options.appInfo, { tools: { listChanged: true } });\n\n this.app.setRequestHandler(ListToolsRequestSchema, async () => ({\n tools: [],\n }));\n\n this.app.ontoolinput = (params) => {\n this.updateContext({ toolInput: params.arguments ?? {} });\n };\n\n this.app.ontoolinputpartial = (params) => {\n this.updateContext({ toolInput: params.arguments ?? {} });\n };\n\n this.app.ontoolresult = (params) => {\n this.updateContext({ toolResult: params });\n };\n\n this.app.ontoolcancelled = (params) => {\n this.updateContext({ toolCancelled: params });\n };\n\n this.app.onhostcontextchanged = (params) => {\n this.updateContext(params);\n };\n\n this.connectPromise = this.connect();\n }\n\n private async connect() {\n try {\n await this.app.connect();\n const hostContext = this.app.getHostContext();\n this.updateContext({\n ...hostContext,\n hostInfo: this.app.getHostVersion() ?? null,\n });\n } catch (err) {\n console.error(err);\n }\n }\n\n public async getApp(): Promise<App> {\n await this.connectPromise;\n return this.app;\n }\n\n public registerViewTool(\n config: ViewToolConfig,\n handler: AnyViewToolHandler,\n ): () => void {\n const inputSchema = config.inputSchema\n ? z.object(config.inputSchema)\n : z.object({});\n\n const registered = this.app.registerTool(\n config.name,\n {\n ...(config.title !== undefined ? { title: config.title } : {}),\n ...(config.description !== undefined\n ? { description: config.description }\n : {}),\n inputSchema,\n ...(config.annotations ? { annotations: config.annotations } : {}),\n },\n handler,\n );\n\n return () => {\n registered.remove();\n };\n }\n\n public static getInstance(\n options?: Partial<{ appInfo: Implementation }>,\n ): McpAppBridge {\n if (McpAppBridge.instance && options) {\n console.warn(\n \"McpAppBridge.getInstance: options ignored, instance already exists\",\n );\n }\n if (!McpAppBridge.instance) {\n const defaultOptions = {\n appInfo: { name: \"skybridge-app\", version: \"0.0.1\" },\n };\n McpAppBridge.instance = new McpAppBridge({\n ...defaultOptions,\n ...options,\n });\n }\n return McpAppBridge.instance;\n }\n\n public subscribe(key: McpAppContextKey): Subscribe;\n public subscribe(keys: readonly McpAppContextKey[]): Subscribe;\n public subscribe(\n keyOrKeys: McpAppContextKey | readonly McpAppContextKey[],\n ): Subscribe {\n const keys = Array.isArray(keyOrKeys) ? keyOrKeys : [keyOrKeys];\n return (onChange: () => void) => {\n for (const key of keys) {\n this.listeners.set(\n key,\n new Set([...(this.listeners.get(key) || []), onChange]),\n );\n }\n return () => {\n for (const key of keys) {\n this.listeners.get(key)?.delete(onChange);\n }\n };\n };\n }\n\n public getSnapshot<K extends keyof McpAppContext>(key: K): McpAppContext[K] {\n return this.context[key];\n }\n\n public createContextStores(): {\n theme: HostContextStore<\"theme\">;\n locale: HostContextStore<\"locale\">;\n safeArea: HostContextStore<\"safeArea\">;\n displayMode: HostContextStore<\"displayMode\">;\n maxHeight: HostContextStore<\"maxHeight\">;\n userAgent: HostContextStore<\"userAgent\">;\n toolInput: HostContextStore<\"toolInput\">;\n toolOutput: HostContextStore<\"toolOutput\">;\n toolResponseMetadata: HostContextStore<\"toolResponseMetadata\">;\n } {\n return {\n theme: createMcpStore(this, [\"theme\"], ({ theme }) => theme ?? \"light\"),\n locale: createMcpStore(\n this,\n [\"locale\"],\n ({ locale }) => locale ?? \"en-US\",\n ),\n safeArea: createMcpStore(\n this,\n [\"safeAreaInsets\"],\n ({ safeAreaInsets }) => ({\n insets: safeAreaInsets ?? { top: 0, right: 0, bottom: 0, left: 0 },\n }),\n ),\n displayMode: createMcpStore(\n this,\n [\"displayMode\"],\n ({ displayMode }) => displayMode ?? \"inline\",\n ),\n maxHeight: createMcpStore(\n this,\n [\"containerDimensions\"],\n ({ containerDimensions }) => {\n if (containerDimensions && \"maxHeight\" in containerDimensions) {\n return containerDimensions.maxHeight;\n }\n return undefined;\n },\n ),\n userAgent: createMcpStore(\n this,\n [\"platform\", \"deviceCapabilities\"],\n ({ platform, deviceCapabilities }) => ({\n device: {\n type: platform === \"web\" ? \"desktop\" : (platform ?? \"unknown\"),\n },\n capabilities: {\n hover: true,\n touch: true,\n ...deviceCapabilities,\n },\n }),\n ),\n toolInput: createMcpStore(\n this,\n [\"toolInput\"],\n ({ toolInput }) => toolInput ?? null,\n ),\n toolOutput: createMcpStore(\n this,\n [\"toolResult\"],\n ({ toolResult }) => toolResult?.structuredContent ?? null,\n ),\n toolResponseMetadata: createMcpStore(\n this,\n [\"toolResult\"],\n ({ toolResult }) => toolResult?._meta ?? null,\n ),\n };\n }\n\n public cleanup = () => {\n this.listeners.clear();\n };\n\n public static resetInstance(): void {\n if (McpAppBridge.instance) {\n McpAppBridge.instance.cleanup();\n McpAppBridge.instance = null;\n }\n }\n\n private emit(key: McpAppContextKey) {\n this.listeners.get(key)?.forEach((listener) => {\n listener();\n });\n }\n\n private updateContext(context: Partial<McpAppContext>) {\n this.context = { ...this.context, ...context };\n for (const key of Object.keys(context)) {\n this.emit(key);\n }\n }\n}\n"]}
1
+ {"version":3,"file":"bridge.js","sourceRoot":"","sources":["../../../../src/web/bridges/mcp-app/bridge.ts"],"names":[],"mappings":"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","sourcesContent":["import { App } from \"@modelcontextprotocol/ext-apps\";\nimport {\n type Implementation,\n ListToolsRequestSchema,\n} from \"@modelcontextprotocol/sdk/types.js\";\nimport { dequal } from \"dequal/lite\";\nimport * as z from \"zod/v4\";\nimport type {\n AnyViewToolHandler,\n Bridge,\n HostContextStore,\n Subscribe,\n ViewToolConfig,\n} from \"../types.js\";\nimport type { McpAppContext, McpAppContextKey } from \"./types.js\";\n\ntype PickContext<K extends readonly McpAppContextKey[]> = {\n [P in K[number]]: McpAppContext[P];\n};\n\nfunction createMcpStore<const Keys extends readonly McpAppContextKey[], R>(\n bridge: McpAppBridge,\n keys: Keys,\n computeSnapshot: (context: PickContext<Keys>) => R,\n) {\n let cachedValue: R | undefined;\n return {\n subscribe: bridge.subscribe(keys),\n getSnapshot: () => {\n const context = Object.fromEntries(\n keys.map((k) => [k, bridge.getSnapshot(k)]),\n ) as PickContext<Keys>;\n const newValue = computeSnapshot(context);\n if (cachedValue !== undefined && dequal(cachedValue, newValue)) {\n return cachedValue;\n }\n cachedValue = newValue;\n return newValue;\n },\n };\n}\n\n/** @internal Singleton bridge over the `ext-apps` JSON-RPC App connection. Used by `HostAdaptor`. */\nexport class McpAppBridge implements Bridge<McpAppContext> {\n private static instance: McpAppBridge | null = null;\n public context: McpAppContext = {\n toolInput: null,\n toolCancelled: null,\n toolResult: null,\n hostInfo: null,\n };\n private listeners = new Map<McpAppContextKey, Set<() => void>>();\n private app: App;\n private connectPromise: Promise<void>;\n\n constructor(options: { appInfo: Implementation }) {\n this.app = new App(options.appInfo, { tools: { listChanged: true } });\n\n this.app.setRequestHandler(ListToolsRequestSchema, async () => ({\n tools: [],\n }));\n\n this.app.ontoolinput = (params) => {\n this.updateContext({ toolInput: params.arguments ?? {} });\n };\n\n this.app.ontoolinputpartial = (params) => {\n this.updateContext({ toolInput: params.arguments ?? {} });\n };\n\n this.app.ontoolresult = (params) => {\n this.updateContext({ toolResult: params });\n };\n\n this.app.ontoolcancelled = (params) => {\n this.updateContext({ toolCancelled: params });\n };\n\n this.app.onhostcontextchanged = (params) => {\n this.updateContext(params);\n };\n\n this.connectPromise = this.connect();\n }\n\n private async connect() {\n try {\n await this.app.connect();\n const hostContext = this.app.getHostContext();\n this.updateContext({\n ...hostContext,\n hostInfo: this.app.getHostVersion() ?? null,\n });\n } catch (err) {\n console.error(err);\n }\n }\n\n public async getApp(): Promise<App> {\n await this.connectPromise;\n return this.app;\n }\n\n public registerViewTool(\n config: ViewToolConfig,\n handler: AnyViewToolHandler,\n ): () => void {\n const inputSchema = config.inputSchema\n ? z.object(config.inputSchema)\n : z.object({});\n\n const registered = this.app.registerTool(\n config.name,\n {\n ...(config.title !== undefined ? { title: config.title } : {}),\n ...(config.description !== undefined\n ? { description: config.description }\n : {}),\n inputSchema,\n ...(config.annotations ? { annotations: config.annotations } : {}),\n },\n handler,\n );\n\n return () => {\n registered.remove();\n };\n }\n\n public static getInstance(\n options?: Partial<{ appInfo: Implementation }>,\n ): McpAppBridge {\n if (McpAppBridge.instance && options) {\n console.warn(\n \"McpAppBridge.getInstance: options ignored, instance already exists\",\n );\n }\n if (!McpAppBridge.instance) {\n const defaultOptions = {\n appInfo: { name: \"skybridge-app\", version: \"0.0.1\" },\n };\n McpAppBridge.instance = new McpAppBridge({\n ...defaultOptions,\n ...options,\n });\n }\n return McpAppBridge.instance;\n }\n\n public subscribe(key: McpAppContextKey): Subscribe;\n public subscribe(keys: readonly McpAppContextKey[]): Subscribe;\n public subscribe(\n keyOrKeys: McpAppContextKey | readonly McpAppContextKey[],\n ): Subscribe {\n const keys = Array.isArray(keyOrKeys) ? keyOrKeys : [keyOrKeys];\n return (onChange: () => void) => {\n for (const key of keys) {\n this.listeners.set(\n key,\n new Set([...(this.listeners.get(key) || []), onChange]),\n );\n }\n return () => {\n for (const key of keys) {\n this.listeners.get(key)?.delete(onChange);\n }\n };\n };\n }\n\n public getSnapshot<K extends keyof McpAppContext>(key: K): McpAppContext[K] {\n return this.context[key];\n }\n\n public createContextStores(): {\n theme: HostContextStore<\"theme\">;\n locale: HostContextStore<\"locale\">;\n safeArea: HostContextStore<\"safeArea\">;\n displayMode: HostContextStore<\"displayMode\">;\n maxHeight: HostContextStore<\"maxHeight\">;\n userAgent: HostContextStore<\"userAgent\">;\n toolInput: HostContextStore<\"toolInput\">;\n toolOutput: HostContextStore<\"toolOutput\">;\n toolResponseMetadata: HostContextStore<\"toolResponseMetadata\">;\n } {\n return {\n theme: createMcpStore(this, [\"theme\"], ({ theme }) => theme ?? \"light\"),\n locale: createMcpStore(\n this,\n [\"locale\"],\n ({ locale }) => locale ?? \"en-US\",\n ),\n safeArea: createMcpStore(\n this,\n [\"safeAreaInsets\"],\n ({ safeAreaInsets }) => ({\n insets: safeAreaInsets ?? { top: 0, right: 0, bottom: 0, left: 0 },\n }),\n ),\n displayMode: createMcpStore(\n this,\n [\"displayMode\"],\n ({ displayMode }) => displayMode ?? \"inline\",\n ),\n maxHeight: createMcpStore(\n this,\n [\"containerDimensions\"],\n ({ containerDimensions }) => {\n if (containerDimensions && \"maxHeight\" in containerDimensions) {\n return containerDimensions.maxHeight;\n }\n return undefined;\n },\n ),\n userAgent: createMcpStore(\n this,\n [\"platform\", \"deviceCapabilities\"],\n ({ platform, deviceCapabilities }) => ({\n device: {\n type: platform === \"web\" ? \"desktop\" : (platform ?? \"unknown\"),\n },\n capabilities: {\n hover: true,\n touch: true,\n ...deviceCapabilities,\n },\n }),\n ),\n toolInput: createMcpStore(\n this,\n [\"toolInput\"],\n ({ toolInput }) => toolInput ?? null,\n ),\n toolOutput: createMcpStore(\n this,\n [\"toolResult\"],\n ({ toolResult }) => toolResult?.structuredContent ?? null,\n ),\n toolResponseMetadata: createMcpStore(\n this,\n [\"toolResult\"],\n ({ toolResult }) => toolResult?._meta ?? null,\n ),\n };\n }\n\n public cleanup = () => {\n this.listeners.clear();\n };\n\n public static resetInstance(): void {\n if (McpAppBridge.instance) {\n McpAppBridge.instance.cleanup();\n McpAppBridge.instance = null;\n }\n }\n\n private emit(key: McpAppContextKey) {\n this.listeners.get(key)?.forEach((listener) => {\n listener();\n });\n }\n\n private updateContext(context: Partial<McpAppContext>) {\n this.context = { ...this.context, ...context };\n for (const key of Object.keys(context)) {\n this.emit(key);\n }\n }\n}\n"]}
@@ -9,8 +9,8 @@ describe("McpAppBridge.getInstance", () => {
9
9
  vi.unstubAllGlobals();
10
10
  McpAppBridge.resetInstance();
11
11
  });
12
- it("instantiates regardless of injected hostType", () => {
13
- vi.stubGlobal("skybridge", { hostType: "mcp-app" });
12
+ it("instantiates without injected globals", () => {
13
+ vi.stubGlobal("skybridge", {});
14
14
  expect(() => McpAppBridge.getInstance()).not.toThrow();
15
15
  });
16
16
  });
@@ -1 +1 @@
1
- {"version":3,"file":"bridge.test.js","sourceRoot":"","sources":["../../../../src/web/bridges/mcp-app/bridge.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,UAAU,EAAE,QAAQ,EAAE,MAAM,EAAE,EAAE,EAAE,EAAE,EAAE,MAAM,QAAQ,CAAC;AACzE,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAE3C,QAAQ,CAAC,0BAA0B,EAAE,GAAG,EAAE;IACxC,UAAU,CAAC,GAAG,EAAE;QACd,YAAY,CAAC,aAAa,EAAE,CAAC;QAC7B,EAAE,CAAC,UAAU,CAAC,QAAQ,EAAE,EAAE,WAAW,EAAE,EAAE,CAAC,EAAE,EAAE,EAAE,CAAC,CAAC;IACpD,CAAC,CAAC,CAAC;IACH,SAAS,CAAC,GAAG,EAAE;QACb,EAAE,CAAC,gBAAgB,EAAE,CAAC;QACtB,YAAY,CAAC,aAAa,EAAE,CAAC;IAC/B,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,8CAA8C,EAAE,GAAG,EAAE;QACtD,EAAE,CAAC,UAAU,CAAC,WAAW,EAAE,EAAE,QAAQ,EAAE,SAAS,EAAE,CAAC,CAAC;QACpD,MAAM,CAAC,GAAG,EAAE,CAAC,YAAY,CAAC,WAAW,EAAE,CAAC,CAAC,GAAG,CAAC,OAAO,EAAE,CAAC;IACzD,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC","sourcesContent":["import { afterEach, beforeEach, describe, expect, it, vi } from \"vitest\";\nimport { McpAppBridge } from \"./bridge.js\";\n\ndescribe(\"McpAppBridge.getInstance\", () => {\n beforeEach(() => {\n McpAppBridge.resetInstance();\n vi.stubGlobal(\"parent\", { postMessage: vi.fn() });\n });\n afterEach(() => {\n vi.unstubAllGlobals();\n McpAppBridge.resetInstance();\n });\n\n it(\"instantiates regardless of injected hostType\", () => {\n vi.stubGlobal(\"skybridge\", { hostType: \"mcp-app\" });\n expect(() => McpAppBridge.getInstance()).not.toThrow();\n });\n});\n"]}
1
+ {"version":3,"file":"bridge.test.js","sourceRoot":"","sources":["../../../../src/web/bridges/mcp-app/bridge.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,UAAU,EAAE,QAAQ,EAAE,MAAM,EAAE,EAAE,EAAE,EAAE,EAAE,MAAM,QAAQ,CAAC;AACzE,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAE3C,QAAQ,CAAC,0BAA0B,EAAE,GAAG,EAAE;IACxC,UAAU,CAAC,GAAG,EAAE;QACd,YAAY,CAAC,aAAa,EAAE,CAAC;QAC7B,EAAE,CAAC,UAAU,CAAC,QAAQ,EAAE,EAAE,WAAW,EAAE,EAAE,CAAC,EAAE,EAAE,EAAE,CAAC,CAAC;IACpD,CAAC,CAAC,CAAC;IACH,SAAS,CAAC,GAAG,EAAE;QACb,EAAE,CAAC,gBAAgB,EAAE,CAAC;QACtB,YAAY,CAAC,aAAa,EAAE,CAAC;IAC/B,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,uCAAuC,EAAE,GAAG,EAAE;QAC/C,EAAE,CAAC,UAAU,CAAC,WAAW,EAAE,EAAE,CAAC,CAAC;QAC/B,MAAM,CAAC,GAAG,EAAE,CAAC,YAAY,CAAC,WAAW,EAAE,CAAC,CAAC,GAAG,CAAC,OAAO,EAAE,CAAC;IACzD,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC","sourcesContent":["import { afterEach, beforeEach, describe, expect, it, vi } from \"vitest\";\nimport { McpAppBridge } from \"./bridge.js\";\n\ndescribe(\"McpAppBridge.getInstance\", () => {\n beforeEach(() => {\n McpAppBridge.resetInstance();\n vi.stubGlobal(\"parent\", { postMessage: vi.fn() });\n });\n afterEach(() => {\n vi.unstubAllGlobals();\n McpAppBridge.resetInstance();\n });\n\n it(\"instantiates without injected globals\", () => {\n vi.stubGlobal(\"skybridge\", {});\n expect(() => McpAppBridge.getInstance()).not.toThrow();\n });\n});\n"]}
@@ -5,7 +5,7 @@ import { McpAppBridge } from "./bridge.js";
5
5
  import { useMcpAppContext } from "./use-mcp-app-context.js";
6
6
  describe("useMcpAppContext", () => {
7
7
  beforeEach(async () => {
8
- vi.stubGlobal("skybridge", { hostType: "mcp-app" });
8
+ vi.stubGlobal("skybridge", {});
9
9
  vi.stubGlobal("ResizeObserver", MockResizeObserver);
10
10
  McpAppBridge.resetInstance();
11
11
  });
@@ -1 +1 @@
1
- {"version":3,"file":"use-mcp-app-context.test.js","sourceRoot":"","sources":["../../../../src/web/bridges/mcp-app/use-mcp-app-context.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,OAAO,EAAE,MAAM,wBAAwB,CAAC;AAC7D,OAAO,EAAE,SAAS,EAAE,UAAU,EAAE,QAAQ,EAAE,MAAM,EAAE,EAAE,EAAE,EAAE,EAAE,MAAM,QAAQ,CAAC;AACzE,OAAO,EACL,4BAA4B,EAC5B,kBAAkB,GACnB,MAAM,2BAA2B,CAAC;AACnC,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAC3C,OAAO,EAAE,gBAAgB,EAAE,MAAM,0BAA0B,CAAC;AAE5D,QAAQ,CAAC,kBAAkB,EAAE,GAAG,EAAE;IAChC,UAAU,CAAC,KAAK,IAAI,EAAE;QACpB,EAAE,CAAC,UAAU,CAAC,WAAW,EAAE,EAAE,QAAQ,EAAE,SAAS,EAAE,CAAC,CAAC;QACpD,EAAE,CAAC,UAAU,CAAC,gBAAgB,EAAE,kBAAkB,CAAC,CAAC;QACpD,YAAY,CAAC,aAAa,EAAE,CAAC;IAC/B,CAAC,CAAC,CAAC;IAEH,SAAS,CAAC,GAAG,EAAE;QACb,EAAE,CAAC,gBAAgB,EAAE,CAAC;QACtB,EAAE,CAAC,aAAa,EAAE,CAAC;IACrB,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,4EAA4E,EAAE,KAAK,IAAI,EAAE;QAC1F,EAAE,CAAC,UAAU,CAAC,QAAQ,EAAE;YACtB,WAAW,EAAE,4BAA4B,CAAC,EAAE,KAAK,EAAE,OAAO,EAAE,CAAC;SAC9D,CAAC,CAAC;QACH,MAAM,EAAE,MAAM,EAAE,GAAG,UAAU,CAAC,GAAG,EAAE,CAAC,gBAAgB,CAAC,OAAO,CAAC,CAAC,CAAC;QAE/D,MAAM,OAAO,CAAC,GAAG,EAAE;YACjB,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;QACvC,CAAC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC","sourcesContent":["import { renderHook, waitFor } from \"@testing-library/react\";\nimport { afterEach, beforeEach, describe, expect, it, vi } from \"vitest\";\nimport {\n getMcpAppHostPostMessageMock,\n MockResizeObserver,\n} from \"../../hooks/test/utils.js\";\nimport { McpAppBridge } from \"./bridge.js\";\nimport { useMcpAppContext } from \"./use-mcp-app-context.js\";\n\ndescribe(\"useMcpAppContext\", () => {\n beforeEach(async () => {\n vi.stubGlobal(\"skybridge\", { hostType: \"mcp-app\" });\n vi.stubGlobal(\"ResizeObserver\", MockResizeObserver);\n McpAppBridge.resetInstance();\n });\n\n afterEach(() => {\n vi.unstubAllGlobals();\n vi.clearAllMocks();\n });\n\n it(\"should return the theme value from host context and update on notification\", async () => {\n vi.stubGlobal(\"parent\", {\n postMessage: getMcpAppHostPostMessageMock({ theme: \"light\" }),\n });\n const { result } = renderHook(() => useMcpAppContext(\"theme\"));\n\n await waitFor(() => {\n expect(result.current).toBe(\"light\");\n });\n });\n});\n"]}
1
+ {"version":3,"file":"use-mcp-app-context.test.js","sourceRoot":"","sources":["../../../../src/web/bridges/mcp-app/use-mcp-app-context.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,OAAO,EAAE,MAAM,wBAAwB,CAAC;AAC7D,OAAO,EAAE,SAAS,EAAE,UAAU,EAAE,QAAQ,EAAE,MAAM,EAAE,EAAE,EAAE,EAAE,EAAE,MAAM,QAAQ,CAAC;AACzE,OAAO,EACL,4BAA4B,EAC5B,kBAAkB,GACnB,MAAM,2BAA2B,CAAC;AACnC,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAC3C,OAAO,EAAE,gBAAgB,EAAE,MAAM,0BAA0B,CAAC;AAE5D,QAAQ,CAAC,kBAAkB,EAAE,GAAG,EAAE;IAChC,UAAU,CAAC,KAAK,IAAI,EAAE;QACpB,EAAE,CAAC,UAAU,CAAC,WAAW,EAAE,EAAE,CAAC,CAAC;QAC/B,EAAE,CAAC,UAAU,CAAC,gBAAgB,EAAE,kBAAkB,CAAC,CAAC;QACpD,YAAY,CAAC,aAAa,EAAE,CAAC;IAC/B,CAAC,CAAC,CAAC;IAEH,SAAS,CAAC,GAAG,EAAE;QACb,EAAE,CAAC,gBAAgB,EAAE,CAAC;QACtB,EAAE,CAAC,aAAa,EAAE,CAAC;IACrB,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,4EAA4E,EAAE,KAAK,IAAI,EAAE;QAC1F,EAAE,CAAC,UAAU,CAAC,QAAQ,EAAE;YACtB,WAAW,EAAE,4BAA4B,CAAC,EAAE,KAAK,EAAE,OAAO,EAAE,CAAC;SAC9D,CAAC,CAAC;QACH,MAAM,EAAE,MAAM,EAAE,GAAG,UAAU,CAAC,GAAG,EAAE,CAAC,gBAAgB,CAAC,OAAO,CAAC,CAAC,CAAC;QAE/D,MAAM,OAAO,CAAC,GAAG,EAAE;YACjB,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;QACvC,CAAC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC","sourcesContent":["import { renderHook, waitFor } from \"@testing-library/react\";\nimport { afterEach, beforeEach, describe, expect, it, vi } from \"vitest\";\nimport {\n getMcpAppHostPostMessageMock,\n MockResizeObserver,\n} from \"../../hooks/test/utils.js\";\nimport { McpAppBridge } from \"./bridge.js\";\nimport { useMcpAppContext } from \"./use-mcp-app-context.js\";\n\ndescribe(\"useMcpAppContext\", () => {\n beforeEach(async () => {\n vi.stubGlobal(\"skybridge\", {});\n vi.stubGlobal(\"ResizeObserver\", MockResizeObserver);\n McpAppBridge.resetInstance();\n });\n\n afterEach(() => {\n vi.unstubAllGlobals();\n vi.clearAllMocks();\n });\n\n it(\"should return the theme value from host context and update on notification\", async () => {\n vi.stubGlobal(\"parent\", {\n postMessage: getMcpAppHostPostMessageMock({ theme: \"light\" }),\n });\n const { result } = renderHook(() => useMcpAppContext(\"theme\"));\n\n await waitFor(() => {\n expect(result.current).toBe(\"light\");\n });\n });\n});\n"]}
@@ -51,7 +51,7 @@ async function callHost(method, params = {}) {
51
51
  }
52
52
  describe("McpApp view tools", () => {
53
53
  beforeEach(() => {
54
- vi.stubGlobal("skybridge", { hostType: "mcp-app" });
54
+ vi.stubGlobal("skybridge", {});
55
55
  vi.stubGlobal("ResizeObserver", MockResizeObserver);
56
56
  McpAppBridge.resetInstance();
57
57
  installHostMock();
@@ -1 +1 @@
1
- {"version":3,"file":"view-tools.test.js","sourceRoot":"","sources":["../../../../src/web/bridges/mcp-app/view-tools.test.ts"],"names":[],"mappings":"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","sourcesContent":["import { waitFor } from \"@testing-library/react\";\nimport { act } from \"react\";\nimport { afterEach, beforeEach, describe, expect, it, vi } from \"vitest\";\nimport * as z from \"zod\";\nimport { MockResizeObserver } from \"../../hooks/test/utils.js\";\nimport { HostAdaptor } from \"../adaptor.js\";\nimport { McpAppBridge } from \"./bridge.js\";\n\ntype JsonRpcMessage = {\n jsonrpc: \"2.0\";\n id?: number;\n method?: string;\n params?: Record<string, unknown>;\n result?: Record<string, unknown>;\n error?: { message: string };\n};\n\nconst outgoing: JsonRpcMessage[] = [];\n\n/**\n * Stand-in MCP Apps host: replies to `ui/initialize` and records every message\n * the app posts so tests can assert on responses and notifications.\n */\nfunction installHostMock() {\n outgoing.length = 0;\n const postMessage = vi.fn((message: JsonRpcMessage) => {\n outgoing.push(message);\n if (message.method === \"ui/initialize\" && message.id !== undefined) {\n act(() => {\n window.dispatchEvent(\n new MessageEvent(\"message\", {\n source: window.parent,\n data: {\n jsonrpc: \"2.0\",\n id: message.id,\n result: {\n protocolVersion: \"2025-06-18\",\n hostInfo: { name: \"test-host\", version: \"1.0.0\" },\n hostCapabilities: {},\n hostContext: {},\n },\n },\n }),\n );\n });\n }\n });\n vi.stubGlobal(\"parent\", { postMessage });\n}\n\nlet nextId = 1000;\n\n/** Send a host → app JSON-RPC request and resolve with the full response (result or error). */\nasync function callHost(method: string, params: Record<string, unknown> = {}) {\n const id = ++nextId;\n act(() => {\n window.dispatchEvent(\n new MessageEvent(\"message\", {\n source: window.parent,\n data: { jsonrpc: \"2.0\", id, method, params },\n }),\n );\n });\n await waitFor(() => {\n expect(outgoing.some((m) => m.id === id)).toBe(true);\n });\n return outgoing.find((m) => m.id === id);\n}\n\ndescribe(\"McpApp view tools\", () => {\n beforeEach(() => {\n vi.stubGlobal(\"skybridge\", { hostType: \"mcp-app\" });\n vi.stubGlobal(\"ResizeObserver\", MockResizeObserver);\n McpAppBridge.resetInstance();\n installHostMock();\n });\n\n afterEach(() => {\n vi.unstubAllGlobals();\n vi.clearAllMocks();\n });\n\n it(\"advertises the tools capability during ui/initialize\", async () => {\n await McpAppBridge.getInstance().getApp();\n const init = outgoing.find((m) => m.method === \"ui/initialize\");\n expect(init?.params?.appCapabilities).toMatchObject({\n tools: { listChanged: true },\n });\n });\n\n it(\"lists a registered view tool with its input schema\", async () => {\n const adaptor = new HostAdaptor();\n await McpAppBridge.getInstance().getApp();\n\n adaptor.registerViewTool(\n {\n name: \"chess_make_move\",\n description: \"Play a move\",\n inputSchema: { san: z.string() },\n annotations: { readOnlyHint: false },\n },\n () => ({ content: [{ type: \"text\", text: \"ok\" }] }),\n );\n\n const response = await callHost(\"tools/list\");\n const tools = response?.result?.tools as Array<{\n name: string;\n description?: string;\n inputSchema: { properties?: Record<string, unknown> };\n annotations?: { readOnlyHint?: boolean };\n }>;\n expect(tools).toHaveLength(1);\n const [tool] = tools;\n expect(tool?.name).toBe(\"chess_make_move\");\n expect(tool?.description).toBe(\"Play a move\");\n expect(tool?.inputSchema.properties).toHaveProperty(\"san\");\n expect(tool?.annotations?.readOnlyHint).toBe(false);\n });\n\n it(\"invokes the handler with validated args and returns its result\", async () => {\n const adaptor = new HostAdaptor();\n await McpAppBridge.getInstance().getApp();\n\n const handler = vi.fn(({ san }: { san: string }) => ({\n content: [{ type: \"text\" as const, text: `played ${san}` }],\n structuredContent: { lastMove: san },\n }));\n\n adaptor.registerViewTool(\n { name: \"chess_make_move\", inputSchema: { san: z.string() } },\n handler as never,\n );\n\n const response = await callHost(\"tools/call\", {\n name: \"chess_make_move\",\n arguments: { san: \"e4\" },\n });\n const result = response?.result;\n\n // ext-apps invokes the callback as `(args, extra)`; assert on the args only.\n expect(handler.mock.calls[0]?.[0]).toEqual({ san: \"e4\" });\n expect(result?.structuredContent).toEqual({ lastMove: \"e4\" });\n expect(result?.isError).toBeFalsy();\n expect(result?.content).toEqual([{ type: \"text\", text: \"played e4\" }]);\n });\n\n it(\"rejects the call without invoking the handler when args are invalid\", async () => {\n const adaptor = new HostAdaptor();\n await McpAppBridge.getInstance().getApp();\n\n const handler = vi.fn(() => ({ content: [] }));\n adaptor.registerViewTool(\n { name: \"chess_make_move\", inputSchema: { san: z.string() } },\n handler as never,\n );\n\n // ext-apps validates input against the schema and rejects with a JSON-RPC\n // error before the handler runs.\n const response = await callHost(\"tools/call\", {\n name: \"chess_make_move\",\n arguments: { san: 42 },\n });\n\n expect(handler).not.toHaveBeenCalled();\n expect(response?.error).toBeDefined();\n });\n\n it(\"rejects a call to an unknown tool\", async () => {\n await McpAppBridge.getInstance().getApp();\n const response = await callHost(\"tools/call\", {\n name: \"nope\",\n arguments: {},\n });\n expect(response?.error).toBeDefined();\n });\n\n it(\"removes the tool and notifies the host when unregistered\", async () => {\n const adaptor = new HostAdaptor();\n await McpAppBridge.getInstance().getApp();\n\n const unregister = adaptor.registerViewTool(\n { name: \"chess_reset\" },\n () => ({ content: [{ type: \"text\", text: \"reset\" }] }),\n );\n\n await waitFor(() => {\n expect(\n outgoing.some((m) => m.method === \"notifications/tools/list_changed\"),\n ).toBe(true);\n });\n\n let listed = await callHost(\"tools/list\");\n expect(listed?.result?.tools).toHaveLength(1);\n\n unregister();\n listed = await callHost(\"tools/list\");\n expect(listed?.result?.tools).toHaveLength(0);\n });\n});\n"]}
1
+ {"version":3,"file":"view-tools.test.js","sourceRoot":"","sources":["../../../../src/web/bridges/mcp-app/view-tools.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,wBAAwB,CAAC;AACjD,OAAO,EAAE,GAAG,EAAE,MAAM,OAAO,CAAC;AAC5B,OAAO,EAAE,SAAS,EAAE,UAAU,EAAE,QAAQ,EAAE,MAAM,EAAE,EAAE,EAAE,EAAE,EAAE,MAAM,QAAQ,CAAC;AACzE,OAAO,KAAK,CAAC,MAAM,KAAK,CAAC;AACzB,OAAO,EAAE,kBAAkB,EAAE,MAAM,2BAA2B,CAAC;AAC/D,OAAO,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AAC5C,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAW3C,MAAM,QAAQ,GAAqB,EAAE,CAAC;AAEtC;;;GAGG;AACH,SAAS,eAAe;IACtB,QAAQ,CAAC,MAAM,GAAG,CAAC,CAAC;IACpB,MAAM,WAAW,GAAG,EAAE,CAAC,EAAE,CAAC,CAAC,OAAuB,EAAE,EAAE;QACpD,QAAQ,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;QACvB,IAAI,OAAO,CAAC,MAAM,KAAK,eAAe,IAAI,OAAO,CAAC,EAAE,KAAK,SAAS,EAAE,CAAC;YACnE,GAAG,CAAC,GAAG,EAAE;gBACP,MAAM,CAAC,aAAa,CAClB,IAAI,YAAY,CAAC,SAAS,EAAE;oBAC1B,MAAM,EAAE,MAAM,CAAC,MAAM;oBACrB,IAAI,EAAE;wBACJ,OAAO,EAAE,KAAK;wBACd,EAAE,EAAE,OAAO,CAAC,EAAE;wBACd,MAAM,EAAE;4BACN,eAAe,EAAE,YAAY;4BAC7B,QAAQ,EAAE,EAAE,IAAI,EAAE,WAAW,EAAE,OAAO,EAAE,OAAO,EAAE;4BACjD,gBAAgB,EAAE,EAAE;4BACpB,WAAW,EAAE,EAAE;yBAChB;qBACF;iBACF,CAAC,CACH,CAAC;YACJ,CAAC,CAAC,CAAC;QACL,CAAC;IACH,CAAC,CAAC,CAAC;IACH,EAAE,CAAC,UAAU,CAAC,QAAQ,EAAE,EAAE,WAAW,EAAE,CAAC,CAAC;AAC3C,CAAC;AAED,IAAI,MAAM,GAAG,IAAI,CAAC;AAElB,+FAA+F;AAC/F,KAAK,UAAU,QAAQ,CAAC,MAAc,EAAE,SAAkC,EAAE;IAC1E,MAAM,EAAE,GAAG,EAAE,MAAM,CAAC;IACpB,GAAG,CAAC,GAAG,EAAE;QACP,MAAM,CAAC,aAAa,CAClB,IAAI,YAAY,CAAC,SAAS,EAAE;YAC1B,MAAM,EAAE,MAAM,CAAC,MAAM;YACrB,IAAI,EAAE,EAAE,OAAO,EAAE,KAAK,EAAE,EAAE,EAAE,MAAM,EAAE,MAAM,EAAE;SAC7C,CAAC,CACH,CAAC;IACJ,CAAC,CAAC,CAAC;IACH,MAAM,OAAO,CAAC,GAAG,EAAE;QACjB,MAAM,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IACvD,CAAC,CAAC,CAAC;IACH,OAAO,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC,CAAC;AAC3C,CAAC;AAED,QAAQ,CAAC,mBAAmB,EAAE,GAAG,EAAE;IACjC,UAAU,CAAC,GAAG,EAAE;QACd,EAAE,CAAC,UAAU,CAAC,WAAW,EAAE,EAAE,CAAC,CAAC;QAC/B,EAAE,CAAC,UAAU,CAAC,gBAAgB,EAAE,kBAAkB,CAAC,CAAC;QACpD,YAAY,CAAC,aAAa,EAAE,CAAC;QAC7B,eAAe,EAAE,CAAC;IACpB,CAAC,CAAC,CAAC;IAEH,SAAS,CAAC,GAAG,EAAE;QACb,EAAE,CAAC,gBAAgB,EAAE,CAAC;QACtB,EAAE,CAAC,aAAa,EAAE,CAAC;IACrB,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,sDAAsD,EAAE,KAAK,IAAI,EAAE;QACpE,MAAM,YAAY,CAAC,WAAW,EAAE,CAAC,MAAM,EAAE,CAAC;QAC1C,MAAM,IAAI,GAAG,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,KAAK,eAAe,CAAC,CAAC;QAChE,MAAM,CAAC,IAAI,EAAE,MAAM,EAAE,eAAe,CAAC,CAAC,aAAa,CAAC;YAClD,KAAK,EAAE,EAAE,WAAW,EAAE,IAAI,EAAE;SAC7B,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,oDAAoD,EAAE,KAAK,IAAI,EAAE;QAClE,MAAM,OAAO,GAAG,IAAI,WAAW,EAAE,CAAC;QAClC,MAAM,YAAY,CAAC,WAAW,EAAE,CAAC,MAAM,EAAE,CAAC;QAE1C,OAAO,CAAC,gBAAgB,CACtB;YACE,IAAI,EAAE,iBAAiB;YACvB,WAAW,EAAE,aAAa;YAC1B,WAAW,EAAE,EAAE,GAAG,EAAE,CAAC,CAAC,MAAM,EAAE,EAAE;YAChC,WAAW,EAAE,EAAE,YAAY,EAAE,KAAK,EAAE;SACrC,EACD,GAAG,EAAE,CAAC,CAAC,EAAE,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC,EAAE,CAAC,CACpD,CAAC;QAEF,MAAM,QAAQ,GAAG,MAAM,QAAQ,CAAC,YAAY,CAAC,CAAC;QAC9C,MAAM,KAAK,GAAG,QAAQ,EAAE,MAAM,EAAE,KAK9B,CAAC;QACH,MAAM,CAAC,KAAK,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC;QAC9B,MAAM,CAAC,IAAI,CAAC,GAAG,KAAK,CAAC;QACrB,MAAM,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC,IAAI,CAAC,iBAAiB,CAAC,CAAC;QAC3C,MAAM,CAAC,IAAI,EAAE,WAAW,CAAC,CAAC,IAAI,CAAC,aAAa,CAAC,CAAC;QAC9C,MAAM,CAAC,IAAI,EAAE,WAAW,CAAC,UAAU,CAAC,CAAC,cAAc,CAAC,KAAK,CAAC,CAAC;QAC3D,MAAM,CAAC,IAAI,EAAE,WAAW,EAAE,YAAY,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;IACtD,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,gEAAgE,EAAE,KAAK,IAAI,EAAE;QAC9E,MAAM,OAAO,GAAG,IAAI,WAAW,EAAE,CAAC;QAClC,MAAM,YAAY,CAAC,WAAW,EAAE,CAAC,MAAM,EAAE,CAAC;QAE1C,MAAM,OAAO,GAAG,EAAE,CAAC,EAAE,CAAC,CAAC,EAAE,GAAG,EAAmB,EAAE,EAAE,CAAC,CAAC;YACnD,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAe,EAAE,IAAI,EAAE,UAAU,GAAG,EAAE,EAAE,CAAC;YAC3D,iBAAiB,EAAE,EAAE,QAAQ,EAAE,GAAG,EAAE;SACrC,CAAC,CAAC,CAAC;QAEJ,OAAO,CAAC,gBAAgB,CACtB,EAAE,IAAI,EAAE,iBAAiB,EAAE,WAAW,EAAE,EAAE,GAAG,EAAE,CAAC,CAAC,MAAM,EAAE,EAAE,EAAE,EAC7D,OAAgB,CACjB,CAAC;QAEF,MAAM,QAAQ,GAAG,MAAM,QAAQ,CAAC,YAAY,EAAE;YAC5C,IAAI,EAAE,iBAAiB;YACvB,SAAS,EAAE,EAAE,GAAG,EAAE,IAAI,EAAE;SACzB,CAAC,CAAC;QACH,MAAM,MAAM,GAAG,QAAQ,EAAE,MAAM,CAAC;QAEhC,6EAA6E;QAC7E,MAAM,CAAC,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,EAAE,GAAG,EAAE,IAAI,EAAE,CAAC,CAAC;QAC1D,MAAM,CAAC,MAAM,EAAE,iBAAiB,CAAC,CAAC,OAAO,CAAC,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC,CAAC;QAC9D,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,SAAS,EAAE,CAAC;QACpC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,OAAO,CAAC,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,WAAW,EAAE,CAAC,CAAC,CAAC;IACzE,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,qEAAqE,EAAE,KAAK,IAAI,EAAE;QACnF,MAAM,OAAO,GAAG,IAAI,WAAW,EAAE,CAAC;QAClC,MAAM,YAAY,CAAC,WAAW,EAAE,CAAC,MAAM,EAAE,CAAC;QAE1C,MAAM,OAAO,GAAG,EAAE,CAAC,EAAE,CAAC,GAAG,EAAE,CAAC,CAAC,EAAE,OAAO,EAAE,EAAE,EAAE,CAAC,CAAC,CAAC;QAC/C,OAAO,CAAC,gBAAgB,CACtB,EAAE,IAAI,EAAE,iBAAiB,EAAE,WAAW,EAAE,EAAE,GAAG,EAAE,CAAC,CAAC,MAAM,EAAE,EAAE,EAAE,EAC7D,OAAgB,CACjB,CAAC;QAEF,0EAA0E;QAC1E,iCAAiC;QACjC,MAAM,QAAQ,GAAG,MAAM,QAAQ,CAAC,YAAY,EAAE;YAC5C,IAAI,EAAE,iBAAiB;YACvB,SAAS,EAAE,EAAE,GAAG,EAAE,EAAE,EAAE;SACvB,CAAC,CAAC;QAEH,MAAM,CAAC,OAAO,CAAC,CAAC,GAAG,CAAC,gBAAgB,EAAE,CAAC;QACvC,MAAM,CAAC,QAAQ,EAAE,KAAK,CAAC,CAAC,WAAW,EAAE,CAAC;IACxC,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,mCAAmC,EAAE,KAAK,IAAI,EAAE;QACjD,MAAM,YAAY,CAAC,WAAW,EAAE,CAAC,MAAM,EAAE,CAAC;QAC1C,MAAM,QAAQ,GAAG,MAAM,QAAQ,CAAC,YAAY,EAAE;YAC5C,IAAI,EAAE,MAAM;YACZ,SAAS,EAAE,EAAE;SACd,CAAC,CAAC;QACH,MAAM,CAAC,QAAQ,EAAE,KAAK,CAAC,CAAC,WAAW,EAAE,CAAC;IACxC,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,0DAA0D,EAAE,KAAK,IAAI,EAAE;QACxE,MAAM,OAAO,GAAG,IAAI,WAAW,EAAE,CAAC;QAClC,MAAM,YAAY,CAAC,WAAW,EAAE,CAAC,MAAM,EAAE,CAAC;QAE1C,MAAM,UAAU,GAAG,OAAO,CAAC,gBAAgB,CACzC,EAAE,IAAI,EAAE,aAAa,EAAE,EACvB,GAAG,EAAE,CAAC,CAAC,EAAE,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,OAAO,EAAE,CAAC,EAAE,CAAC,CACvD,CAAC;QAEF,MAAM,OAAO,CAAC,GAAG,EAAE;YACjB,MAAM,CACJ,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,KAAK,kCAAkC,CAAC,CACtE,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QACf,CAAC,CAAC,CAAC;QAEH,IAAI,MAAM,GAAG,MAAM,QAAQ,CAAC,YAAY,CAAC,CAAC;QAC1C,MAAM,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC;QAE9C,UAAU,EAAE,CAAC;QACb,MAAM,GAAG,MAAM,QAAQ,CAAC,YAAY,CAAC,CAAC;QACtC,MAAM,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC;IAChD,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC","sourcesContent":["import { waitFor } from \"@testing-library/react\";\nimport { act } from \"react\";\nimport { afterEach, beforeEach, describe, expect, it, vi } from \"vitest\";\nimport * as z from \"zod\";\nimport { MockResizeObserver } from \"../../hooks/test/utils.js\";\nimport { HostAdaptor } from \"../adaptor.js\";\nimport { McpAppBridge } from \"./bridge.js\";\n\ntype JsonRpcMessage = {\n jsonrpc: \"2.0\";\n id?: number;\n method?: string;\n params?: Record<string, unknown>;\n result?: Record<string, unknown>;\n error?: { message: string };\n};\n\nconst outgoing: JsonRpcMessage[] = [];\n\n/**\n * Stand-in MCP Apps host: replies to `ui/initialize` and records every message\n * the app posts so tests can assert on responses and notifications.\n */\nfunction installHostMock() {\n outgoing.length = 0;\n const postMessage = vi.fn((message: JsonRpcMessage) => {\n outgoing.push(message);\n if (message.method === \"ui/initialize\" && message.id !== undefined) {\n act(() => {\n window.dispatchEvent(\n new MessageEvent(\"message\", {\n source: window.parent,\n data: {\n jsonrpc: \"2.0\",\n id: message.id,\n result: {\n protocolVersion: \"2025-06-18\",\n hostInfo: { name: \"test-host\", version: \"1.0.0\" },\n hostCapabilities: {},\n hostContext: {},\n },\n },\n }),\n );\n });\n }\n });\n vi.stubGlobal(\"parent\", { postMessage });\n}\n\nlet nextId = 1000;\n\n/** Send a host → app JSON-RPC request and resolve with the full response (result or error). */\nasync function callHost(method: string, params: Record<string, unknown> = {}) {\n const id = ++nextId;\n act(() => {\n window.dispatchEvent(\n new MessageEvent(\"message\", {\n source: window.parent,\n data: { jsonrpc: \"2.0\", id, method, params },\n }),\n );\n });\n await waitFor(() => {\n expect(outgoing.some((m) => m.id === id)).toBe(true);\n });\n return outgoing.find((m) => m.id === id);\n}\n\ndescribe(\"McpApp view tools\", () => {\n beforeEach(() => {\n vi.stubGlobal(\"skybridge\", {});\n vi.stubGlobal(\"ResizeObserver\", MockResizeObserver);\n McpAppBridge.resetInstance();\n installHostMock();\n });\n\n afterEach(() => {\n vi.unstubAllGlobals();\n vi.clearAllMocks();\n });\n\n it(\"advertises the tools capability during ui/initialize\", async () => {\n await McpAppBridge.getInstance().getApp();\n const init = outgoing.find((m) => m.method === \"ui/initialize\");\n expect(init?.params?.appCapabilities).toMatchObject({\n tools: { listChanged: true },\n });\n });\n\n it(\"lists a registered view tool with its input schema\", async () => {\n const adaptor = new HostAdaptor();\n await McpAppBridge.getInstance().getApp();\n\n adaptor.registerViewTool(\n {\n name: \"chess_make_move\",\n description: \"Play a move\",\n inputSchema: { san: z.string() },\n annotations: { readOnlyHint: false },\n },\n () => ({ content: [{ type: \"text\", text: \"ok\" }] }),\n );\n\n const response = await callHost(\"tools/list\");\n const tools = response?.result?.tools as Array<{\n name: string;\n description?: string;\n inputSchema: { properties?: Record<string, unknown> };\n annotations?: { readOnlyHint?: boolean };\n }>;\n expect(tools).toHaveLength(1);\n const [tool] = tools;\n expect(tool?.name).toBe(\"chess_make_move\");\n expect(tool?.description).toBe(\"Play a move\");\n expect(tool?.inputSchema.properties).toHaveProperty(\"san\");\n expect(tool?.annotations?.readOnlyHint).toBe(false);\n });\n\n it(\"invokes the handler with validated args and returns its result\", async () => {\n const adaptor = new HostAdaptor();\n await McpAppBridge.getInstance().getApp();\n\n const handler = vi.fn(({ san }: { san: string }) => ({\n content: [{ type: \"text\" as const, text: `played ${san}` }],\n structuredContent: { lastMove: san },\n }));\n\n adaptor.registerViewTool(\n { name: \"chess_make_move\", inputSchema: { san: z.string() } },\n handler as never,\n );\n\n const response = await callHost(\"tools/call\", {\n name: \"chess_make_move\",\n arguments: { san: \"e4\" },\n });\n const result = response?.result;\n\n // ext-apps invokes the callback as `(args, extra)`; assert on the args only.\n expect(handler.mock.calls[0]?.[0]).toEqual({ san: \"e4\" });\n expect(result?.structuredContent).toEqual({ lastMove: \"e4\" });\n expect(result?.isError).toBeFalsy();\n expect(result?.content).toEqual([{ type: \"text\", text: \"played e4\" }]);\n });\n\n it(\"rejects the call without invoking the handler when args are invalid\", async () => {\n const adaptor = new HostAdaptor();\n await McpAppBridge.getInstance().getApp();\n\n const handler = vi.fn(() => ({ content: [] }));\n adaptor.registerViewTool(\n { name: \"chess_make_move\", inputSchema: { san: z.string() } },\n handler as never,\n );\n\n // ext-apps validates input against the schema and rejects with a JSON-RPC\n // error before the handler runs.\n const response = await callHost(\"tools/call\", {\n name: \"chess_make_move\",\n arguments: { san: 42 },\n });\n\n expect(handler).not.toHaveBeenCalled();\n expect(response?.error).toBeDefined();\n });\n\n it(\"rejects a call to an unknown tool\", async () => {\n await McpAppBridge.getInstance().getApp();\n const response = await callHost(\"tools/call\", {\n name: \"nope\",\n arguments: {},\n });\n expect(response?.error).toBeDefined();\n });\n\n it(\"removes the tool and notifies the host when unregistered\", async () => {\n const adaptor = new HostAdaptor();\n await McpAppBridge.getInstance().getApp();\n\n const unregister = adaptor.registerViewTool(\n { name: \"chess_reset\" },\n () => ({ content: [{ type: \"text\", text: \"reset\" }] }),\n );\n\n await waitFor(() => {\n expect(\n outgoing.some((m) => m.method === \"notifications/tools/list_changed\"),\n ).toBe(true);\n });\n\n let listed = await callHost(\"tools/list\");\n expect(listed?.result?.tools).toHaveLength(1);\n\n unregister();\n listed = await callHost(\"tools/list\");\n expect(listed?.result?.tools).toHaveLength(0);\n });\n});\n"]}
@@ -1,14 +1,11 @@
1
1
  import type { CallToolResult, EmbeddedResource, ResourceLink, ToolAnnotations } from "@modelcontextprotocol/sdk/types.js";
2
2
  import type { useSyncExternalStore } from "react";
3
- import type { ZodType } from "zod";
4
- import type { ViewHostType } from "../../server/server.js";
5
- import type { InferSchemaOutput } from "../../standard-schema.js";
3
+ import type { output as ZodOutput, ZodType } from "zod/v4";
6
4
  /**
7
- * Globals injected on `window.skybridge` by the host. Tells the view which
8
- * runtime it's running under and where to reach the MCP server.
5
+ * Globals injected on `window.skybridge` by the host. Tells the view where to
6
+ * reach the MCP server.
9
7
  */
10
8
  export type SkybridgeProperties = {
11
- hostType: ViewHostType;
12
9
  serverUrl: string;
13
10
  };
14
11
  declare global {
@@ -147,7 +144,7 @@ export type DownloadResult = {
147
144
  };
148
145
  /**
149
146
  * Shape of a view tool's `inputSchema`: a map of zod schemas. Narrower than
150
- * the server's `RawInputShape` because the MCP Apps bridge validates with
147
+ * the server's `Record<string, StandardSchemaWithJSON>` input shape because the MCP Apps bridge validates with
151
148
  * `z.object()` at runtime — other Standard Schema libraries would type-check
152
149
  * but throw on the first call.
153
150
  */
@@ -157,9 +154,9 @@ export type ViewToolInputShape = Record<string, ZodType>;
157
154
  * `inputSchema` (optionality preserved). Mirrors the server's `registerTool`.
158
155
  */
159
156
  export type InferViewToolArgs<Shape extends ViewToolInputShape> = {
160
- [K in keyof Shape as undefined extends InferSchemaOutput<Shape[K]> ? never : K]: InferSchemaOutput<Shape[K]>;
157
+ [K in keyof Shape as undefined extends ZodOutput<Shape[K]> ? never : K]: ZodOutput<Shape[K]>;
161
158
  } & {
162
- [K in keyof Shape as undefined extends InferSchemaOutput<Shape[K]> ? K : never]?: InferSchemaOutput<Shape[K]>;
159
+ [K in keyof Shape as undefined extends ZodOutput<Shape[K]> ? K : never]?: ZodOutput<Shape[K]>;
163
160
  };
164
161
  /**
165
162
  * Declares a tool the view exposes to the host/model (the MCP Apps
@@ -1 +1 @@
1
- {"version":3,"file":"types.js","sourceRoot":"","sources":["../../../src/web/bridges/types.ts"],"names":[],"mappings":"AAsQA;;;GAGG;AACH,MAAM,OAAO,iBAAkB,SAAQ,KAAK;IAExB;IACA;IAFlB,YACkB,MAAc,EACd,MAAe;QAE/B,KAAK,CACH,GAAG,MAAM,oCAAoC,MAAM,CAAC,CAAC,CAAC,KAAK,MAAM,EAAE,CAAC,CAAC,CAAC,EAAE,EAAE,CAC3E,CAAC;QALc,WAAM,GAAN,MAAM,CAAQ;QACd,WAAM,GAAN,MAAM,CAAS;QAK/B,IAAI,CAAC,IAAI,GAAG,mBAAmB,CAAC;IAClC,CAAC;CACF","sourcesContent":["import type {\n CallToolResult,\n EmbeddedResource,\n ResourceLink,\n ToolAnnotations,\n} from \"@modelcontextprotocol/sdk/types.js\";\nimport type { useSyncExternalStore } from \"react\";\nimport type { ZodType } from \"zod\";\nimport type { ViewHostType } from \"../../server/server.js\";\nimport type { InferSchemaOutput } from \"../../standard-schema.js\";\n\n/**\n * Globals injected on `window.skybridge` by the host. Tells the view which\n * runtime it's running under and where to reach the MCP server.\n */\nexport type SkybridgeProperties = {\n hostType: ViewHostType;\n serverUrl: string;\n};\n\ndeclare global {\n interface Window {\n skybridge: SkybridgeProperties;\n }\n}\n\n/** Arguments passed to a tool call. `null` for tools that take no input. */\nexport type CallToolArgs = Record<string, unknown> | null;\n\n/**\n * Result of a tool call as surfaced to the view: MCP `content` blocks plus\n * the typed `structuredContent` and optional `meta`. `isError` is set when\n * the server marks the call as failed.\n */\nexport type CallToolResponse = {\n content: CallToolResult[\"content\"];\n structuredContent: NonNullable<CallToolResult[\"structuredContent\"]>;\n isError: NonNullable<CallToolResult[\"isError\"]>;\n meta?: CallToolResult[\"_meta\"];\n};\n\n/**\n * How the view is laid out by the host. `\"modal\"` is host-driven (see\n * {@link useRequestModal}); `\"pip\"`, `\"inline\"`, and `\"fullscreen\"` are\n * requestable via {@link useDisplayMode}.\n */\nexport type DisplayMode = \"pip\" | \"inline\" | \"fullscreen\" | \"modal\";\n/** Subset of {@link DisplayMode} that the view can request from the host. */\nexport type RequestDisplayMode = Exclude<DisplayMode, \"modal\">;\n\n/** Host theme. Mirror this in your view's styling for a native feel. */\nexport type Theme = \"light\" | \"dark\";\n\n/** Coarse device class reported by the host. `\"unknown\"` when unavailable. */\nexport type DeviceType = \"mobile\" | \"tablet\" | \"desktop\" | \"unknown\";\n\n/** Pixel insets the view should keep clear of (notches, home indicators, etc.). */\nexport type SafeAreaInsets = {\n top: number;\n right: number;\n bottom: number;\n left: number;\n};\n\n/** Wrapper around {@link SafeAreaInsets} exposed via {@link useViewport}. */\nexport type SafeArea = {\n insets: SafeAreaInsets;\n};\n\n/** Device and input-capability hints exposed via {@link useUser}. */\nexport type UserAgent = {\n device: {\n type: DeviceType;\n };\n capabilities: {\n hover: boolean;\n touch: boolean;\n };\n};\n\n/**\n * Full snapshot of state the host exposes to the view. Most fields are\n * better accessed through their dedicated hooks (`useViewport`, `useUser`,\n * `useToolInfo`, etc.) — read this directly only for advanced cases.\n */\nexport interface HostContext {\n theme: Theme;\n locale: string;\n displayMode: DisplayMode;\n safeArea: SafeArea;\n maxHeight: number | undefined;\n userAgent: UserAgent;\n toolInput: Record<string, unknown> | null;\n toolOutput: Record<string, unknown> | null;\n toolResponseMetadata: Record<string, unknown> | null;\n display: {\n mode: DisplayMode;\n params?: Record<string, unknown>;\n };\n viewState: Record<string, unknown> | null;\n}\n\n/** @internal `useSyncExternalStore` subscribe signature, re-exported for bridge implementations. */\nexport type Subscribe = Parameters<typeof useSyncExternalStore>[0];\n\n/** @internal Bridge contract implemented by per-host bridge classes. */\nexport interface Bridge<Context> {\n subscribe(key: keyof Context): Subscribe;\n subscribe(keys: readonly (keyof Context)[]): Subscribe;\n getSnapshot<K extends keyof Context>(key: K): Context[K] | undefined;\n}\n\n/** @internal Per-key snapshot store backing {@link useHostContext}. */\nexport type HostContextStore<K extends keyof HostContext> = {\n subscribe: Subscribe;\n getSnapshot: () => HostContext[K];\n};\n\n/** Persisted view state shape (a plain object). See {@link useViewState}. */\nexport type ViewState = Record<string, unknown>;\n\n/** Updater form accepted when writing to view state. */\nexport type SetViewStateAction =\n | ViewState\n | ((prevState: ViewState | null) => ViewState);\n\n/** Reference to a host-managed file (returned by {@link useFiles}). */\nexport type FileMetadata = {\n fileId: string;\n fileName?: string;\n mimeType?: string;\n};\n\n/** Options for {@link useFiles}'s `upload`. `library: true` saves into the user's library when supported. */\nexport type UploadFileOptions = { library?: boolean };\n\n/** Options for {@link useRequestModal}'s `open` call. */\nexport type RequestModalOptions = {\n title?: string;\n params?: Record<string, unknown>;\n template?: string;\n anchor?: { top?: number; left?: number; width?: number; height?: number };\n};\n\n/**\n * Options for {@link useOpenExternal}. Set `redirectUrl: false` to tell the\n * host not to append its `?redirectUrl=…` tracking query parameter when\n * opening allowlisted targets.\n */\nexport type OpenExternalOptions = {\n redirectUrl?: false;\n};\n\n/** Options for {@link useSendFollowUpMessage}. */\nexport type SendFollowUpMessageOptions = { scrollToBottom?: boolean };\n\n/** Options for {@link useRequestSize}. Omit a dimension to leave it unchanged. */\nexport type RequestSizeOptions = {\n width?: number;\n height?: number;\n};\n\nexport type DownloadParams = {\n contents: (EmbeddedResource | ResourceLink)[];\n};\n\nexport type DownloadResult = {\n isError?: boolean;\n};\n\n/**\n * Shape of a view tool's `inputSchema`: a map of zod schemas. Narrower than\n * the server's `RawInputShape` because the MCP Apps bridge validates with\n * `z.object()` at runtime — other Standard Schema libraries would type-check\n * but throw on the first call.\n */\nexport type ViewToolInputShape = Record<string, ZodType>;\n\n/**\n * Args passed to a {@link ViewToolHandler}, inferred from the tool's\n * `inputSchema` (optionality preserved). Mirrors the server's `registerTool`.\n */\nexport type InferViewToolArgs<Shape extends ViewToolInputShape> = {\n [K in keyof Shape as undefined extends InferSchemaOutput<Shape[K]>\n ? never\n : K]: InferSchemaOutput<Shape[K]>;\n} & {\n [K in keyof Shape as undefined extends InferSchemaOutput<Shape[K]>\n ? K\n : never]?: InferSchemaOutput<Shape[K]>;\n};\n\n/**\n * Declares a tool the view exposes to the host/model (the MCP Apps\n * \"app-provided tools\" feature). Mirrors the server-side `registerTool` config.\n * Namespace `name` (e.g. `chess_make_move`) to avoid clashing with server tools.\n */\nexport type ViewToolConfig<\n TInput extends ViewToolInputShape = ViewToolInputShape,\n> = {\n name: string;\n title?: string;\n description?: string;\n inputSchema?: TInput;\n annotations?: ToolAnnotations;\n};\n\n/**\n * Value a {@link ViewToolHandler} returns — a standard MCP `CallToolResult`\n * (`content` blocks plus optional `structuredContent` / `isError` / `_meta`),\n * exactly as `ext-apps`' app tool callbacks return it.\n */\nexport type ViewToolResult = CallToolResult;\n\n/** Handler run when the host calls a view tool. Receives validated, typed args. */\nexport type ViewToolHandler<\n TInput extends ViewToolInputShape = ViewToolInputShape,\n> = (\n args: InferViewToolArgs<TInput>,\n) => ViewToolResult | Promise<ViewToolResult>;\n\n/** @internal Untyped handler signature stored by the adaptor/bridge after type erasure. */\nexport type AnyViewToolHandler = (\n args: Record<string, unknown>,\n) => ViewToolResult | Promise<ViewToolResult>;\n\n/**\n * @internal\n * Low-level interface every host bridge implements. End-user code should use\n * the React hooks (`useCallTool`, `useViewState`, `useFiles`, …) rather than\n * calling this directly.\n */\nexport interface Adaptor {\n getHostContextStore<K extends keyof HostContext>(key: K): HostContextStore<K>;\n callTool<\n ToolArgs extends CallToolArgs = null,\n ToolResponse extends CallToolResponse = CallToolResponse,\n >(name: string, args: ToolArgs): Promise<ToolResponse>;\n requestDisplayMode(mode: RequestDisplayMode): Promise<{\n mode: RequestDisplayMode;\n }>;\n requestClose(): Promise<void>;\n requestSize(size: RequestSizeOptions): Promise<void>;\n sendFollowUpMessage(\n prompt: string,\n options?: SendFollowUpMessageOptions,\n ): Promise<void>;\n openExternal(href: string, options?: OpenExternalOptions): void;\n download(params: DownloadParams): Promise<DownloadResult>;\n setViewState(stateOrUpdater: SetViewStateAction): Promise<void>;\n uploadFile(file: File, options?: UploadFileOptions): Promise<FileMetadata>;\n getFileDownloadUrl(file: FileMetadata): Promise<{ downloadUrl: string }>;\n selectFiles(): Promise<FileMetadata[]>;\n openModal(options: RequestModalOptions): void;\n setOpenInAppUrl(href: string): Promise<void>;\n closeModal(): void;\n registerViewTool(\n config: ViewToolConfig,\n handler: AnyViewToolHandler,\n ): () => void;\n}\n\n/**\n * Thrown when a host bridge method is called in a runtime that doesn't\n * support it (e.g. `uploadFile` outside the Apps SDK runtime).\n */\nexport class NotSupportedError extends Error {\n constructor(\n public readonly method: string,\n public readonly reason?: string,\n ) {\n super(\n `${method} is not supported in this runtime${reason ? `: ${reason}` : \"\"}`,\n );\n this.name = \"NotSupportedError\";\n }\n}\n"]}
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../../../src/web/bridges/types.ts"],"names":[],"mappings":"AAmQA;;;GAGG;AACH,MAAM,OAAO,iBAAkB,SAAQ,KAAK;IAExB;IACA;IAFlB,YACkB,MAAc,EACd,MAAe;QAE/B,KAAK,CACH,GAAG,MAAM,oCAAoC,MAAM,CAAC,CAAC,CAAC,KAAK,MAAM,EAAE,CAAC,CAAC,CAAC,EAAE,EAAE,CAC3E,CAAC;QALc,WAAM,GAAN,MAAM,CAAQ;QACd,WAAM,GAAN,MAAM,CAAS;QAK/B,IAAI,CAAC,IAAI,GAAG,mBAAmB,CAAC;IAClC,CAAC;CACF","sourcesContent":["import type {\n CallToolResult,\n EmbeddedResource,\n ResourceLink,\n ToolAnnotations,\n} from \"@modelcontextprotocol/sdk/types.js\";\nimport type { useSyncExternalStore } from \"react\";\nimport type { output as ZodOutput, ZodType } from \"zod/v4\";\n\n/**\n * Globals injected on `window.skybridge` by the host. Tells the view where to\n * reach the MCP server.\n */\nexport type SkybridgeProperties = {\n serverUrl: string;\n};\n\ndeclare global {\n interface Window {\n skybridge: SkybridgeProperties;\n }\n}\n\n/** Arguments passed to a tool call. `null` for tools that take no input. */\nexport type CallToolArgs = Record<string, unknown> | null;\n\n/**\n * Result of a tool call as surfaced to the view: MCP `content` blocks plus\n * the typed `structuredContent` and optional `meta`. `isError` is set when\n * the server marks the call as failed.\n */\nexport type CallToolResponse = {\n content: CallToolResult[\"content\"];\n structuredContent: NonNullable<CallToolResult[\"structuredContent\"]>;\n isError: NonNullable<CallToolResult[\"isError\"]>;\n meta?: CallToolResult[\"_meta\"];\n};\n\n/**\n * How the view is laid out by the host. `\"modal\"` is host-driven (see\n * {@link useRequestModal}); `\"pip\"`, `\"inline\"`, and `\"fullscreen\"` are\n * requestable via {@link useDisplayMode}.\n */\nexport type DisplayMode = \"pip\" | \"inline\" | \"fullscreen\" | \"modal\";\n/** Subset of {@link DisplayMode} that the view can request from the host. */\nexport type RequestDisplayMode = Exclude<DisplayMode, \"modal\">;\n\n/** Host theme. Mirror this in your view's styling for a native feel. */\nexport type Theme = \"light\" | \"dark\";\n\n/** Coarse device class reported by the host. `\"unknown\"` when unavailable. */\nexport type DeviceType = \"mobile\" | \"tablet\" | \"desktop\" | \"unknown\";\n\n/** Pixel insets the view should keep clear of (notches, home indicators, etc.). */\nexport type SafeAreaInsets = {\n top: number;\n right: number;\n bottom: number;\n left: number;\n};\n\n/** Wrapper around {@link SafeAreaInsets} exposed via {@link useViewport}. */\nexport type SafeArea = {\n insets: SafeAreaInsets;\n};\n\n/** Device and input-capability hints exposed via {@link useUser}. */\nexport type UserAgent = {\n device: {\n type: DeviceType;\n };\n capabilities: {\n hover: boolean;\n touch: boolean;\n };\n};\n\n/**\n * Full snapshot of state the host exposes to the view. Most fields are\n * better accessed through their dedicated hooks (`useViewport`, `useUser`,\n * `useToolInfo`, etc.) — read this directly only for advanced cases.\n */\nexport interface HostContext {\n theme: Theme;\n locale: string;\n displayMode: DisplayMode;\n safeArea: SafeArea;\n maxHeight: number | undefined;\n userAgent: UserAgent;\n toolInput: Record<string, unknown> | null;\n toolOutput: Record<string, unknown> | null;\n toolResponseMetadata: Record<string, unknown> | null;\n display: {\n mode: DisplayMode;\n params?: Record<string, unknown>;\n };\n viewState: Record<string, unknown> | null;\n}\n\n/** @internal `useSyncExternalStore` subscribe signature, re-exported for bridge implementations. */\nexport type Subscribe = Parameters<typeof useSyncExternalStore>[0];\n\n/** @internal Bridge contract implemented by per-host bridge classes. */\nexport interface Bridge<Context> {\n subscribe(key: keyof Context): Subscribe;\n subscribe(keys: readonly (keyof Context)[]): Subscribe;\n getSnapshot<K extends keyof Context>(key: K): Context[K] | undefined;\n}\n\n/** @internal Per-key snapshot store backing {@link useHostContext}. */\nexport type HostContextStore<K extends keyof HostContext> = {\n subscribe: Subscribe;\n getSnapshot: () => HostContext[K];\n};\n\n/** Persisted view state shape (a plain object). See {@link useViewState}. */\nexport type ViewState = Record<string, unknown>;\n\n/** Updater form accepted when writing to view state. */\nexport type SetViewStateAction =\n | ViewState\n | ((prevState: ViewState | null) => ViewState);\n\n/** Reference to a host-managed file (returned by {@link useFiles}). */\nexport type FileMetadata = {\n fileId: string;\n fileName?: string;\n mimeType?: string;\n};\n\n/** Options for {@link useFiles}'s `upload`. `library: true` saves into the user's library when supported. */\nexport type UploadFileOptions = { library?: boolean };\n\n/** Options for {@link useRequestModal}'s `open` call. */\nexport type RequestModalOptions = {\n title?: string;\n params?: Record<string, unknown>;\n template?: string;\n anchor?: { top?: number; left?: number; width?: number; height?: number };\n};\n\n/**\n * Options for {@link useOpenExternal}. Set `redirectUrl: false` to tell the\n * host not to append its `?redirectUrl=…` tracking query parameter when\n * opening allowlisted targets.\n */\nexport type OpenExternalOptions = {\n redirectUrl?: false;\n};\n\n/** Options for {@link useSendFollowUpMessage}. */\nexport type SendFollowUpMessageOptions = { scrollToBottom?: boolean };\n\n/** Options for {@link useRequestSize}. Omit a dimension to leave it unchanged. */\nexport type RequestSizeOptions = {\n width?: number;\n height?: number;\n};\n\nexport type DownloadParams = {\n contents: (EmbeddedResource | ResourceLink)[];\n};\n\nexport type DownloadResult = {\n isError?: boolean;\n};\n\n/**\n * Shape of a view tool's `inputSchema`: a map of zod schemas. Narrower than\n * the server's `Record<string, StandardSchemaWithJSON>` input shape because the MCP Apps bridge validates with\n * `z.object()` at runtime — other Standard Schema libraries would type-check\n * but throw on the first call.\n */\nexport type ViewToolInputShape = Record<string, ZodType>;\n\n/**\n * Args passed to a {@link ViewToolHandler}, inferred from the tool's\n * `inputSchema` (optionality preserved). Mirrors the server's `registerTool`.\n */\nexport type InferViewToolArgs<Shape extends ViewToolInputShape> = {\n [K in keyof Shape as undefined extends ZodOutput<Shape[K]>\n ? never\n : K]: ZodOutput<Shape[K]>;\n} & {\n [K in keyof Shape as undefined extends ZodOutput<Shape[K]>\n ? K\n : never]?: ZodOutput<Shape[K]>;\n};\n\n/**\n * Declares a tool the view exposes to the host/model (the MCP Apps\n * \"app-provided tools\" feature). Mirrors the server-side `registerTool` config.\n * Namespace `name` (e.g. `chess_make_move`) to avoid clashing with server tools.\n */\nexport type ViewToolConfig<\n TInput extends ViewToolInputShape = ViewToolInputShape,\n> = {\n name: string;\n title?: string;\n description?: string;\n inputSchema?: TInput;\n annotations?: ToolAnnotations;\n};\n\n/**\n * Value a {@link ViewToolHandler} returns — a standard MCP `CallToolResult`\n * (`content` blocks plus optional `structuredContent` / `isError` / `_meta`),\n * exactly as `ext-apps`' app tool callbacks return it.\n */\nexport type ViewToolResult = CallToolResult;\n\n/** Handler run when the host calls a view tool. Receives validated, typed args. */\nexport type ViewToolHandler<\n TInput extends ViewToolInputShape = ViewToolInputShape,\n> = (\n args: InferViewToolArgs<TInput>,\n) => ViewToolResult | Promise<ViewToolResult>;\n\n/** @internal Untyped handler signature stored by the adaptor/bridge after type erasure. */\nexport type AnyViewToolHandler = (\n args: Record<string, unknown>,\n) => ViewToolResult | Promise<ViewToolResult>;\n\n/**\n * @internal\n * Low-level interface every host bridge implements. End-user code should use\n * the React hooks (`useCallTool`, `useViewState`, `useFiles`, …) rather than\n * calling this directly.\n */\nexport interface Adaptor {\n getHostContextStore<K extends keyof HostContext>(key: K): HostContextStore<K>;\n callTool<\n ToolArgs extends CallToolArgs = null,\n ToolResponse extends CallToolResponse = CallToolResponse,\n >(name: string, args: ToolArgs): Promise<ToolResponse>;\n requestDisplayMode(mode: RequestDisplayMode): Promise<{\n mode: RequestDisplayMode;\n }>;\n requestClose(): Promise<void>;\n requestSize(size: RequestSizeOptions): Promise<void>;\n sendFollowUpMessage(\n prompt: string,\n options?: SendFollowUpMessageOptions,\n ): Promise<void>;\n openExternal(href: string, options?: OpenExternalOptions): void;\n download(params: DownloadParams): Promise<DownloadResult>;\n setViewState(stateOrUpdater: SetViewStateAction): Promise<void>;\n uploadFile(file: File, options?: UploadFileOptions): Promise<FileMetadata>;\n getFileDownloadUrl(file: FileMetadata): Promise<{ downloadUrl: string }>;\n selectFiles(): Promise<FileMetadata[]>;\n openModal(options: RequestModalOptions): void;\n setOpenInAppUrl(href: string): Promise<void>;\n closeModal(): void;\n registerViewTool(\n config: ViewToolConfig,\n handler: AnyViewToolHandler,\n ): () => void;\n}\n\n/**\n * Thrown when a host bridge method is called in a runtime that doesn't\n * support it (e.g. `uploadFile` outside the Apps SDK runtime).\n */\nexport class NotSupportedError extends Error {\n constructor(\n public readonly method: string,\n public readonly reason?: string,\n ) {\n super(\n `${method} is not supported in this runtime${reason ? `: ${reason}` : \"\"}`,\n );\n this.name = \"NotSupportedError\";\n }\n}\n"]}
@@ -20,7 +20,7 @@ describe("createStore", () => {
20
20
  setWidgetState: vi.fn().mockResolvedValue(undefined),
21
21
  };
22
22
  vi.stubGlobal("openai", OpenaiMock);
23
- vi.stubGlobal("skybridge", { hostType: "mcp-app" });
23
+ vi.stubGlobal("skybridge", {});
24
24
  });
25
25
  it("should create a store without default state", () => {
26
26
  const storeCreator = () => ({
@@ -81,7 +81,7 @@ describe("createStore", () => {
81
81
  describe("mcp-app mode", () => {
82
82
  beforeEach(() => {
83
83
  vi.stubGlobal("openai", undefined);
84
- vi.stubGlobal("skybridge", { hostType: "mcp-app" });
84
+ vi.stubGlobal("skybridge", {});
85
85
  vi.stubGlobal("ResizeObserver", MockResizeObserver);
86
86
  });
87
87
  afterEach(async () => {
@@ -1 +1 @@
1
- {"version":3,"file":"create-store.test.js","sourceRoot":"","sources":["../../src/web/create-store.test.ts"],"names":[],"mappings":"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","sourcesContent":["import { cleanup } from \"@testing-library/react\";\nimport {\n afterEach,\n beforeEach,\n describe,\n expect,\n it,\n type Mock,\n vi,\n} from \"vitest\";\nimport type { StateCreator } from \"zustand\";\nimport { HostAdaptor } from \"./bridges/adaptor.js\";\nimport { getAdaptor } from \"./bridges/get-adaptor.js\";\nimport { McpAppBridge } from \"./bridges/mcp-app/bridge.js\";\nimport { createStore } from \"./create-store.js\";\nimport { VIEW_CONTEXT_KEY } from \"./data-llm.js\";\nimport {\n getMcpAppHostPostMessageMock,\n MockResizeObserver,\n} from \"./hooks/test/utils.js\";\n\ndescribe(\"createStore\", () => {\n afterEach(() => {\n HostAdaptor.resetInstance();\n vi.unstubAllGlobals();\n vi.resetAllMocks();\n });\n\n describe(\"apps-sdk mode\", () => {\n let OpenaiMock: {\n widgetState: unknown;\n setWidgetState: Mock;\n };\n\n beforeEach(() => {\n OpenaiMock = {\n widgetState: null,\n setWidgetState: vi.fn().mockResolvedValue(undefined),\n };\n vi.stubGlobal(\"openai\", OpenaiMock);\n vi.stubGlobal(\"skybridge\", { hostType: \"mcp-app\" });\n });\n\n it(\"should create a store without default state\", () => {\n type TestState = { count: number };\n const storeCreator: StateCreator<TestState, [], [], TestState> = () => ({\n count: 0,\n });\n\n const store = createStore(storeCreator);\n\n expect(store.getState()).toEqual({ count: 0 });\n });\n\n it(\"should create a store with default state\", () => {\n type TestState = { count: number; name: string };\n const storeCreator: StateCreator<TestState, [], [], TestState> = () => ({\n count: 0,\n name: \"initial\",\n });\n const defaultState = { count: 5, name: \"default\" };\n\n const store = createStore(storeCreator, defaultState);\n\n expect(store.getState()).toEqual({ count: 5, name: \"default\" });\n });\n\n it(\"should initialize from window.openai.widgetState when available\", () => {\n type TestState = { count: number; name: string };\n const storeCreator: StateCreator<TestState, [], [], TestState> = () => ({\n count: 0,\n name: \"initial\",\n });\n const windowState = { count: 20, name: \"window\" };\n OpenaiMock.widgetState = { modelContent: windowState };\n\n const store = createStore(storeCreator);\n\n expect(store.getState()).toEqual({ count: 20, name: \"window\" });\n });\n\n it(\"should persist state changes to window.openai.setWidgetState\", async () => {\n type TestState = { count: number; increment: () => void };\n const storeCreator: StateCreator<TestState, [], [], TestState> = (\n set,\n ) => ({\n count: 0,\n increment: () => set((state) => ({ count: state.count + 1 })),\n });\n\n const store = createStore(storeCreator);\n store.getState().increment();\n\n await vi.waitFor(() => {\n expect(OpenaiMock.setWidgetState).toHaveBeenCalled();\n });\n\n const callArgs = OpenaiMock.setWidgetState.mock.calls[0]?.[0];\n expect(callArgs).toEqual({\n modelContent: { count: 1 },\n privateContent: {},\n });\n });\n\n it(\"should filter view context from initial state\", () => {\n type TestState = { count: number };\n const storeCreator: StateCreator<TestState, [], [], TestState> = () => ({\n count: 0,\n });\n const windowState = {\n count: 5,\n [VIEW_CONTEXT_KEY]: \"context-value\",\n };\n OpenaiMock.widgetState = { modelContent: windowState };\n\n const store = createStore(storeCreator);\n\n expect(store.getState()).toEqual({ count: 5 });\n expect(\n (store.getState() as Record<string, unknown>)[VIEW_CONTEXT_KEY],\n ).toBeUndefined();\n });\n });\n\n describe(\"mcp-app mode\", () => {\n beforeEach(() => {\n vi.stubGlobal(\"openai\", undefined);\n vi.stubGlobal(\"skybridge\", { hostType: \"mcp-app\" });\n vi.stubGlobal(\"ResizeObserver\", MockResizeObserver);\n });\n\n afterEach(async () => {\n cleanup();\n HostAdaptor.resetInstance();\n McpAppBridge.resetInstance();\n });\n\n it(\"should initialize with null viewState\", () => {\n const adaptor = getAdaptor();\n const viewState = adaptor.getHostContextStore(\"viewState\").getSnapshot();\n\n expect(viewState).toBeNull();\n });\n\n it(\"should create a store with default state when no persisted state exists\", () => {\n type TestState = { count: number };\n const storeCreator: StateCreator<TestState, [], [], TestState> = () => ({\n count: 0,\n });\n\n const store = createStore(storeCreator);\n\n expect(store.getState()).toEqual({ count: 0 });\n });\n\n it(\"should update in-memory state via setViewState\", async () => {\n const adaptor = getAdaptor();\n vi.spyOn(adaptor, \"setViewState\").mockResolvedValue(undefined);\n\n type TestState = { count: number; increment: () => void };\n const storeCreator: StateCreator<TestState, [], [], TestState> = (\n set,\n ) => ({\n count: 0,\n increment: () => set((state) => ({ count: state.count + 1 })),\n });\n\n const store = createStore(storeCreator);\n store.getState().increment();\n\n await vi.waitFor(() => {\n expect(adaptor.setViewState).toHaveBeenCalledWith({ count: 1 });\n });\n });\n\n it(\"should notify listeners when view state changes\", async () => {\n const postMessageMock = getMcpAppHostPostMessageMock();\n vi.stubGlobal(\"parent\", { postMessage: postMessageMock });\n\n const adaptor = getAdaptor();\n const listener = vi.fn();\n\n adaptor.getHostContextStore(\"viewState\").subscribe(listener);\n await adaptor.setViewState({ count: 42 });\n\n expect(postMessageMock).toHaveBeenCalledWith(\n expect.objectContaining({ method: \"ui/update-model-context\" }),\n \"*\",\n );\n expect(listener).toHaveBeenCalled();\n expect(adaptor.getHostContextStore(\"viewState\").getSnapshot()).toEqual({\n count: 42,\n });\n });\n });\n});\n"]}
1
+ {"version":3,"file":"create-store.test.js","sourceRoot":"","sources":["../../src/web/create-store.test.ts"],"names":[],"mappings":"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","sourcesContent":["import { cleanup } from \"@testing-library/react\";\nimport {\n afterEach,\n beforeEach,\n describe,\n expect,\n it,\n type Mock,\n vi,\n} from \"vitest\";\nimport type { StateCreator } from \"zustand\";\nimport { HostAdaptor } from \"./bridges/adaptor.js\";\nimport { getAdaptor } from \"./bridges/get-adaptor.js\";\nimport { McpAppBridge } from \"./bridges/mcp-app/bridge.js\";\nimport { createStore } from \"./create-store.js\";\nimport { VIEW_CONTEXT_KEY } from \"./data-llm.js\";\nimport {\n getMcpAppHostPostMessageMock,\n MockResizeObserver,\n} from \"./hooks/test/utils.js\";\n\ndescribe(\"createStore\", () => {\n afterEach(() => {\n HostAdaptor.resetInstance();\n vi.unstubAllGlobals();\n vi.resetAllMocks();\n });\n\n describe(\"apps-sdk mode\", () => {\n let OpenaiMock: {\n widgetState: unknown;\n setWidgetState: Mock;\n };\n\n beforeEach(() => {\n OpenaiMock = {\n widgetState: null,\n setWidgetState: vi.fn().mockResolvedValue(undefined),\n };\n vi.stubGlobal(\"openai\", OpenaiMock);\n vi.stubGlobal(\"skybridge\", {});\n });\n\n it(\"should create a store without default state\", () => {\n type TestState = { count: number };\n const storeCreator: StateCreator<TestState, [], [], TestState> = () => ({\n count: 0,\n });\n\n const store = createStore(storeCreator);\n\n expect(store.getState()).toEqual({ count: 0 });\n });\n\n it(\"should create a store with default state\", () => {\n type TestState = { count: number; name: string };\n const storeCreator: StateCreator<TestState, [], [], TestState> = () => ({\n count: 0,\n name: \"initial\",\n });\n const defaultState = { count: 5, name: \"default\" };\n\n const store = createStore(storeCreator, defaultState);\n\n expect(store.getState()).toEqual({ count: 5, name: \"default\" });\n });\n\n it(\"should initialize from window.openai.widgetState when available\", () => {\n type TestState = { count: number; name: string };\n const storeCreator: StateCreator<TestState, [], [], TestState> = () => ({\n count: 0,\n name: \"initial\",\n });\n const windowState = { count: 20, name: \"window\" };\n OpenaiMock.widgetState = { modelContent: windowState };\n\n const store = createStore(storeCreator);\n\n expect(store.getState()).toEqual({ count: 20, name: \"window\" });\n });\n\n it(\"should persist state changes to window.openai.setWidgetState\", async () => {\n type TestState = { count: number; increment: () => void };\n const storeCreator: StateCreator<TestState, [], [], TestState> = (\n set,\n ) => ({\n count: 0,\n increment: () => set((state) => ({ count: state.count + 1 })),\n });\n\n const store = createStore(storeCreator);\n store.getState().increment();\n\n await vi.waitFor(() => {\n expect(OpenaiMock.setWidgetState).toHaveBeenCalled();\n });\n\n const callArgs = OpenaiMock.setWidgetState.mock.calls[0]?.[0];\n expect(callArgs).toEqual({\n modelContent: { count: 1 },\n privateContent: {},\n });\n });\n\n it(\"should filter view context from initial state\", () => {\n type TestState = { count: number };\n const storeCreator: StateCreator<TestState, [], [], TestState> = () => ({\n count: 0,\n });\n const windowState = {\n count: 5,\n [VIEW_CONTEXT_KEY]: \"context-value\",\n };\n OpenaiMock.widgetState = { modelContent: windowState };\n\n const store = createStore(storeCreator);\n\n expect(store.getState()).toEqual({ count: 5 });\n expect(\n (store.getState() as Record<string, unknown>)[VIEW_CONTEXT_KEY],\n ).toBeUndefined();\n });\n });\n\n describe(\"mcp-app mode\", () => {\n beforeEach(() => {\n vi.stubGlobal(\"openai\", undefined);\n vi.stubGlobal(\"skybridge\", {});\n vi.stubGlobal(\"ResizeObserver\", MockResizeObserver);\n });\n\n afterEach(async () => {\n cleanup();\n HostAdaptor.resetInstance();\n McpAppBridge.resetInstance();\n });\n\n it(\"should initialize with null viewState\", () => {\n const adaptor = getAdaptor();\n const viewState = adaptor.getHostContextStore(\"viewState\").getSnapshot();\n\n expect(viewState).toBeNull();\n });\n\n it(\"should create a store with default state when no persisted state exists\", () => {\n type TestState = { count: number };\n const storeCreator: StateCreator<TestState, [], [], TestState> = () => ({\n count: 0,\n });\n\n const store = createStore(storeCreator);\n\n expect(store.getState()).toEqual({ count: 0 });\n });\n\n it(\"should update in-memory state via setViewState\", async () => {\n const adaptor = getAdaptor();\n vi.spyOn(adaptor, \"setViewState\").mockResolvedValue(undefined);\n\n type TestState = { count: number; increment: () => void };\n const storeCreator: StateCreator<TestState, [], [], TestState> = (\n set,\n ) => ({\n count: 0,\n increment: () => set((state) => ({ count: state.count + 1 })),\n });\n\n const store = createStore(storeCreator);\n store.getState().increment();\n\n await vi.waitFor(() => {\n expect(adaptor.setViewState).toHaveBeenCalledWith({ count: 1 });\n });\n });\n\n it(\"should notify listeners when view state changes\", async () => {\n const postMessageMock = getMcpAppHostPostMessageMock();\n vi.stubGlobal(\"parent\", { postMessage: postMessageMock });\n\n const adaptor = getAdaptor();\n const listener = vi.fn();\n\n adaptor.getHostContextStore(\"viewState\").subscribe(listener);\n await adaptor.setViewState({ count: 42 });\n\n expect(postMessageMock).toHaveBeenCalledWith(\n expect.objectContaining({ method: \"ui/update-model-context\" }),\n \"*\",\n );\n expect(listener).toHaveBeenCalled();\n expect(adaptor.getHostContextStore(\"viewState\").getSnapshot()).toEqual({\n count: 42,\n });\n });\n });\n});\n"]}
@@ -37,7 +37,7 @@ describe("DataLLM", () => {
37
37
  });
38
38
  }
39
39
  vi.stubGlobal("openai", OpenaiMock);
40
- vi.stubGlobal("skybridge", { hostType: "mcp-app" });
40
+ vi.stubGlobal("skybridge", {});
41
41
  });
42
42
  afterEach(() => {
43
43
  // Keep the mock available for React cleanup, but reset it
@@ -99,7 +99,7 @@ describe("DataLLM", () => {
99
99
  describe("mcp-app mode", () => {
100
100
  let postMessageMock;
101
101
  beforeEach(() => {
102
- vi.stubGlobal("skybridge", { hostType: "mcp-app" });
102
+ vi.stubGlobal("skybridge", {});
103
103
  vi.stubGlobal("openai", undefined);
104
104
  vi.stubGlobal("ResizeObserver", MockResizeObserver);
105
105
  postMessageMock = getMcpAppHostPostMessageMock();