skybridge 0.0.0-dev.d4b1915 → 0.0.0-dev.d4c47c7

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 (184) hide show
  1. package/README.md +1 -0
  2. package/dist/cli/build-helpers.d.ts +5 -1
  3. package/dist/cli/build-helpers.js +30 -10
  4. package/dist/cli/build-helpers.js.map +1 -1
  5. package/dist/cli/build-helpers.test.js +58 -3
  6. package/dist/cli/build-helpers.test.js.map +1 -1
  7. package/dist/cli/build-steps.d.ts +2 -0
  8. package/dist/cli/build-steps.js +73 -0
  9. package/dist/cli/build-steps.js.map +1 -0
  10. package/dist/cli/build-steps.test.d.ts +1 -0
  11. package/dist/cli/build-steps.test.js +52 -0
  12. package/dist/cli/build-steps.test.js.map +1 -0
  13. package/dist/cli/header.d.ts +1 -1
  14. package/dist/cli/run-plain.d.ts +18 -0
  15. package/dist/cli/run-plain.js +89 -0
  16. package/dist/cli/run-plain.js.map +1 -0
  17. package/dist/cli/use-nodemon.d.ts +14 -0
  18. package/dist/cli/use-nodemon.js +71 -60
  19. package/dist/cli/use-nodemon.js.map +1 -1
  20. package/dist/cli/use-typescript-check.d.ts +8 -2
  21. package/dist/cli/use-typescript-check.js +70 -67
  22. package/dist/cli/use-typescript-check.js.map +1 -1
  23. package/dist/commands/build.d.ts +0 -2
  24. package/dist/commands/build.js +2 -61
  25. package/dist/commands/build.js.map +1 -1
  26. package/dist/commands/dev.d.ts +1 -0
  27. package/dist/commands/dev.js +32 -0
  28. package/dist/commands/dev.js.map +1 -1
  29. package/dist/server/auth/discovery.d.ts +32 -0
  30. package/dist/server/auth/discovery.js +56 -0
  31. package/dist/server/auth/discovery.js.map +1 -0
  32. package/dist/server/auth/discovery.test.d.ts +1 -0
  33. package/dist/server/auth/discovery.test.js +93 -0
  34. package/dist/server/auth/discovery.test.js.map +1 -0
  35. package/dist/server/auth/index.d.ts +6 -4
  36. package/dist/server/auth/index.js.map +1 -1
  37. package/dist/server/auth/providers/auth0.d.ts +18 -0
  38. package/dist/server/auth/providers/auth0.js +31 -0
  39. package/dist/server/auth/providers/auth0.js.map +1 -0
  40. package/dist/server/auth/providers/auth0.test.d.ts +1 -0
  41. package/dist/server/auth/providers/auth0.test.js +48 -0
  42. package/dist/server/auth/providers/auth0.test.js.map +1 -0
  43. package/dist/server/auth/providers/clerk.d.ts +14 -0
  44. package/dist/server/auth/providers/clerk.js +16 -0
  45. package/dist/server/auth/providers/clerk.js.map +1 -0
  46. package/dist/server/auth/providers/clerk.test.d.ts +1 -0
  47. package/dist/server/auth/providers/clerk.test.js +28 -0
  48. package/dist/server/auth/providers/clerk.test.js.map +1 -0
  49. package/dist/server/auth/providers/custom.d.ts +30 -0
  50. package/dist/server/auth/providers/custom.js +40 -0
  51. package/dist/server/auth/providers/custom.js.map +1 -0
  52. package/dist/server/auth/providers/custom.test.d.ts +1 -0
  53. package/dist/server/auth/providers/custom.test.js +128 -0
  54. package/dist/server/auth/providers/custom.test.js.map +1 -0
  55. package/dist/server/auth/providers/descope.d.ts +15 -0
  56. package/dist/server/auth/providers/descope.js +35 -0
  57. package/dist/server/auth/providers/descope.js.map +1 -0
  58. package/dist/server/auth/providers/descope.test.d.ts +1 -0
  59. package/dist/server/auth/providers/descope.test.js +39 -0
  60. package/dist/server/auth/providers/descope.test.js.map +1 -0
  61. package/dist/server/auth/providers/shared.d.ts +2 -0
  62. package/dist/server/auth/providers/shared.js +6 -0
  63. package/dist/server/auth/providers/shared.js.map +1 -0
  64. package/dist/server/auth/providers/shared.test.d.ts +1 -0
  65. package/dist/server/auth/providers/shared.test.js +10 -0
  66. package/dist/server/auth/providers/shared.test.js.map +1 -0
  67. package/dist/server/auth/providers/stytch.d.ts +12 -0
  68. package/dist/server/auth/providers/stytch.js +13 -0
  69. package/dist/server/auth/providers/stytch.js.map +1 -0
  70. package/dist/server/auth/providers/workos.d.ts +11 -0
  71. package/dist/server/auth/providers/workos.js +12 -0
  72. package/dist/server/auth/providers/workos.js.map +1 -0
  73. package/dist/server/auth/setup.js +42 -19
  74. package/dist/server/auth/setup.js.map +1 -1
  75. package/dist/server/auth/setup.test.js +34 -41
  76. package/dist/server/auth/setup.test.js.map +1 -1
  77. package/dist/server/auth/verify.d.ts +3 -2
  78. package/dist/server/auth/verify.js +2 -1
  79. package/dist/server/auth/verify.js.map +1 -1
  80. package/dist/server/auth/verify.test.js +13 -12
  81. package/dist/server/auth/verify.test.js.map +1 -1
  82. package/dist/server/index.d.ts +8 -1
  83. package/dist/server/index.js +8 -1
  84. package/dist/server/index.js.map +1 -1
  85. package/dist/server/requestOrigin.d.ts +7 -0
  86. package/dist/server/requestOrigin.js +25 -0
  87. package/dist/server/requestOrigin.js.map +1 -0
  88. package/dist/server/server.d.ts +29 -5
  89. package/dist/server/server.js +135 -126
  90. package/dist/server/server.js.map +1 -1
  91. package/dist/server/skills-integration.test.d.ts +1 -0
  92. package/dist/server/skills-integration.test.js +63 -0
  93. package/dist/server/skills-integration.test.js.map +1 -0
  94. package/dist/server/skills.d.ts +23 -0
  95. package/dist/server/skills.js +178 -0
  96. package/dist/server/skills.js.map +1 -0
  97. package/dist/server/skills.test.d.ts +1 -0
  98. package/dist/server/skills.test.js +210 -0
  99. package/dist/server/skills.test.js.map +1 -0
  100. package/dist/server/view-name.test-d.d.ts +1 -0
  101. package/dist/server/view-name.test-d.js +8 -0
  102. package/dist/server/view-name.test-d.js.map +1 -0
  103. package/dist/server/view-resource-resolution.test.js +93 -10
  104. package/dist/server/view-resource-resolution.test.js.map +1 -1
  105. package/dist/test/view.test.js +70 -102
  106. package/dist/test/view.test.js.map +1 -1
  107. package/dist/web/bridges/adaptor.d.ts +51 -0
  108. package/dist/web/bridges/adaptor.js +330 -0
  109. package/dist/web/bridges/adaptor.js.map +1 -0
  110. package/dist/web/bridges/adaptor.test.d.ts +1 -0
  111. package/dist/web/bridges/adaptor.test.js +208 -0
  112. package/dist/web/bridges/adaptor.test.js.map +1 -0
  113. package/dist/web/bridges/apps-sdk/bridge.d.ts +6 -2
  114. package/dist/web/bridges/apps-sdk/bridge.js +15 -4
  115. package/dist/web/bridges/apps-sdk/bridge.js.map +1 -1
  116. package/dist/web/bridges/apps-sdk/index.d.ts +0 -1
  117. package/dist/web/bridges/apps-sdk/index.js +0 -1
  118. package/dist/web/bridges/apps-sdk/index.js.map +1 -1
  119. package/dist/web/bridges/get-adaptor.d.ts +4 -4
  120. package/dist/web/bridges/get-adaptor.js +6 -11
  121. package/dist/web/bridges/get-adaptor.js.map +1 -1
  122. package/dist/web/bridges/get-adaptor.test.d.ts +1 -0
  123. package/dist/web/bridges/get-adaptor.test.js +32 -0
  124. package/dist/web/bridges/get-adaptor.test.js.map +1 -0
  125. package/dist/web/bridges/mcp-app/bridge.d.ts +13 -2
  126. package/dist/web/bridges/mcp-app/bridge.js +46 -4
  127. package/dist/web/bridges/mcp-app/bridge.js.map +1 -1
  128. package/dist/web/bridges/mcp-app/bridge.test.d.ts +1 -0
  129. package/dist/web/bridges/mcp-app/bridge.test.js +17 -0
  130. package/dist/web/bridges/mcp-app/bridge.test.js.map +1 -0
  131. package/dist/web/bridges/mcp-app/index.d.ts +0 -1
  132. package/dist/web/bridges/mcp-app/index.js +0 -1
  133. package/dist/web/bridges/mcp-app/index.js.map +1 -1
  134. package/dist/web/bridges/mcp-app/view-tools.test.js +5 -6
  135. package/dist/web/bridges/mcp-app/view-tools.test.js.map +1 -1
  136. package/dist/web/bridges/types.d.ts +10 -0
  137. package/dist/web/bridges/types.js +14 -1
  138. package/dist/web/bridges/types.js.map +1 -1
  139. package/dist/web/bridges/types.test.d.ts +1 -0
  140. package/dist/web/bridges/types.test.js +19 -0
  141. package/dist/web/bridges/types.test.js.map +1 -0
  142. package/dist/web/components/modal-provider.d.ts +1 -1
  143. package/dist/web/components/modal-provider.js +4 -3
  144. package/dist/web/components/modal-provider.js.map +1 -1
  145. package/dist/web/create-store.test.js +8 -5
  146. package/dist/web/create-store.test.js.map +1 -1
  147. package/dist/web/data-llm.d.ts +1 -1
  148. package/dist/web/data-llm.test.js +17 -5
  149. package/dist/web/data-llm.test.js.map +1 -1
  150. package/dist/web/hooks/use-call-tool.test.js +37 -23
  151. package/dist/web/hooks/use-call-tool.test.js.map +1 -1
  152. package/dist/web/hooks/use-display-mode.test.js +56 -20
  153. package/dist/web/hooks/use-display-mode.test.js.map +1 -1
  154. package/dist/web/hooks/use-download.test.js +11 -3
  155. package/dist/web/hooks/use-download.test.js.map +1 -1
  156. package/dist/web/hooks/use-files.test.js +10 -2
  157. package/dist/web/hooks/use-files.test.js.map +1 -1
  158. package/dist/web/hooks/use-layout.test.js +59 -26
  159. package/dist/web/hooks/use-layout.test.js.map +1 -1
  160. package/dist/web/hooks/use-open-external.test.js +13 -3
  161. package/dist/web/hooks/use-open-external.test.js.map +1 -1
  162. package/dist/web/hooks/use-request-close.test.js +35 -40
  163. package/dist/web/hooks/use-request-close.test.js.map +1 -1
  164. package/dist/web/hooks/use-request-modal.test.js +10 -0
  165. package/dist/web/hooks/use-request-modal.test.js.map +1 -1
  166. package/dist/web/hooks/use-request-size.test.js +35 -53
  167. package/dist/web/hooks/use-request-size.test.js.map +1 -1
  168. package/dist/web/hooks/use-set-open-in-app-url.test.js +28 -8
  169. package/dist/web/hooks/use-set-open-in-app-url.test.js.map +1 -1
  170. package/dist/web/hooks/use-tool-info.test.js +37 -27
  171. package/dist/web/hooks/use-tool-info.test.js.map +1 -1
  172. package/dist/web/hooks/use-user.test.js +81 -35
  173. package/dist/web/hooks/use-user.test.js.map +1 -1
  174. package/dist/web/hooks/use-view-state.test.js +6 -2
  175. package/dist/web/hooks/use-view-state.test.js.map +1 -1
  176. package/dist/web/plugin/plugin.js +0 -1
  177. package/dist/web/plugin/plugin.js.map +1 -1
  178. package/package.json +21 -20
  179. package/dist/web/bridges/apps-sdk/adaptor.d.ts +0 -29
  180. package/dist/web/bridges/apps-sdk/adaptor.js +0 -117
  181. package/dist/web/bridges/apps-sdk/adaptor.js.map +0 -1
  182. package/dist/web/bridges/mcp-app/adaptor.d.ts +0 -53
  183. package/dist/web/bridges/mcp-app/adaptor.js +0 -283
  184. package/dist/web/bridges/mcp-app/adaptor.js.map +0 -1
@@ -10,6 +10,8 @@ import { setupOAuth } from "./auth/setup.js";
10
10
  import { createApp } from "./express.js";
11
11
  import { createMiddlewareEntry } from "./metric.js";
12
12
  import { buildMiddlewareChain, getHandlerMaps } from "./middleware.js";
13
+ import { resolveServerOrigin } from "./requestOrigin.js";
14
+ import { discoverSkills, registerSkills, SKILLS_EXTENSION_KEY, } from "./skills.js";
13
15
  import { templateHelper } from "./templateHelper.js";
14
16
  const mergeWithUnion = (target, source) => {
15
17
  return mergeWith(target, source, (targetVal, sourceVal) => {
@@ -18,6 +20,20 @@ const mergeWithUnion = (target, source) => {
18
20
  }
19
21
  });
20
22
  };
23
+ const SKILLS_DIR = "src/skills";
24
+ /**
25
+ * Normalize an `x-forwarded-prefix` value into a leading-slash, no-trailing-slash
26
+ * path. Takes the first hop of a comma-separated proxy chain.
27
+ * "/v1/", "v1", "/v1, /internal" → "/v1"; "", "/", undefined → "".
28
+ */
29
+ function normalizeForwardedPrefix(raw) {
30
+ const firstHop = raw?.split(",")[0]?.trim() ?? "";
31
+ const trimmed = firstHop.replace(/\/+$/, "");
32
+ if (trimmed === "") {
33
+ return "";
34
+ }
35
+ return trimmed.startsWith("/") ? trimmed : `/${trimmed}`;
36
+ }
21
37
  /**
22
38
  * Drop the query string from a `ui://` view URI, leaving the bare path. The
23
39
  * `?v=` cache key is the only query we append, so a plain split is enough and
@@ -91,6 +107,27 @@ let pendingBuildManifest = null;
91
107
  export function __setBuildManifest(manifest) {
92
108
  pendingBuildManifest = manifest;
93
109
  }
110
+ let pendingSkillsManifest = null;
111
+ export function __setSkillsManifest(manifest) {
112
+ pendingSkillsManifest = manifest;
113
+ }
114
+ // Pure and `this`-free so it can run inside the `super(...)` call, before `this`
115
+ // exists — the capability must be present for the `initialize` response.
116
+ function withSkillsCapability(options, skybridgeOptions) {
117
+ if (!skybridgeOptions?.skills) {
118
+ return options;
119
+ }
120
+ return {
121
+ ...options,
122
+ capabilities: {
123
+ ...options?.capabilities,
124
+ extensions: {
125
+ ...options?.capabilities?.extensions,
126
+ [SKILLS_EXTENSION_KEY]: { directoryRead: true },
127
+ },
128
+ },
129
+ };
130
+ }
94
131
  export class McpServer extends McpServerBaseOmitted {
95
132
  /**
96
133
  * The underlying Express app. Use this to extend the HTTP server with
@@ -123,9 +160,10 @@ export class McpServer extends McpServerBaseOmitted {
123
160
  serverInfo;
124
161
  serverOptions;
125
162
  constructor(serverInfo, options, skybridgeOptions) {
126
- super(serverInfo, options);
163
+ const mergedOptions = withSkillsCapability(options, skybridgeOptions);
164
+ super(serverInfo, mergedOptions);
127
165
  this.serverInfo = serverInfo;
128
- this.serverOptions = options;
166
+ this.serverOptions = mergedOptions;
129
167
  this.express = express();
130
168
  this.express.use(express.json(skybridgeOptions?.json));
131
169
  if (skybridgeOptions?.oauth) {
@@ -140,6 +178,19 @@ export class McpServer extends McpServerBaseOmitted {
140
178
  this.setViteManifest(pendingBuildManifest);
141
179
  pendingBuildManifest = null;
142
180
  }
181
+ this.setupSkills(Boolean(skybridgeOptions?.skills));
182
+ }
183
+ setupSkills(enabled) {
184
+ const manifest = pendingSkillsManifest;
185
+ pendingSkillsManifest = null;
186
+ if (!enabled) {
187
+ return;
188
+ }
189
+ const skills = manifest ?? discoverSkills(SKILLS_DIR);
190
+ if (skills.length === 0) {
191
+ console.warn(`skybridge: the "skills" option is enabled but no skills were found in "${SKILLS_DIR}". Add a <name>/SKILL.md there, or remove the option.`);
192
+ }
193
+ registerSkills(this, skills);
143
194
  }
144
195
  use(pathOrHandler, ...handlers) {
145
196
  // Branching is load-bearing: Express's `app.use` overloads can't be
@@ -235,7 +286,7 @@ export class McpServer extends McpServerBaseOmitted {
235
286
  const result = (await next());
236
287
  for (const content of result.contents ?? []) {
237
288
  if (typeof content.uri === "string" &&
238
- stripQuery(content.uri) === path) {
289
+ stripQuery(content.uri) === stripQuery(canonical)) {
239
290
  content.uri = requested;
240
291
  }
241
292
  }
@@ -381,27 +432,12 @@ export class McpServer extends McpServerBaseOmitted {
381
432
  return Array.isArray(val) ? val[0] : val;
382
433
  };
383
434
  const isClaude = header("user-agent") === "Claude-User";
384
- let serverUrl;
385
- const forwardedHost = header("x-forwarded-host");
386
- const origin = header("origin");
387
- const host = header("host");
388
- if (forwardedHost) {
389
- const proto = header("x-forwarded-proto") || "https";
390
- serverUrl = `${proto}://${forwardedHost}`;
391
- }
392
- else if (origin) {
393
- serverUrl = origin;
394
- }
395
- else if (host) {
396
- const proto = ["127.0.0.1:", "localhost:"].some((p) => host.startsWith(p))
397
- ? "http"
398
- : "https";
399
- serverUrl = `${proto}://${host}`;
400
- }
401
- else {
402
- const devPort = process.env.__PORT || "3000";
403
- serverUrl = `http://localhost:${devPort}`;
404
- }
435
+ const serverUrl = resolveServerOrigin(header);
436
+ // Path prefix the proxy routed this request under (e.g. `foo.com/v1`). Read
437
+ // per-request so one process can serve many hosts/prefixes at once: the
438
+ // origin is recovered from x-forwarded-host, the prefix from
439
+ // x-forwarded-prefix. Empty when served at the origin root.
440
+ const assetsBasePath = normalizeForwardedPrefix(header("x-forwarded-prefix"));
405
441
  const connectDomains = [serverUrl];
406
442
  if (!isProduction) {
407
443
  const wsUrl = new URL(serverUrl);
@@ -422,114 +458,77 @@ export class McpServer extends McpServerBaseOmitted {
422
458
  .slice(0, 32);
423
459
  contentMetaOverrides = { domain: `${hash}.claudemcpcontent.com` };
424
460
  }
425
- return { serverUrl, connectDomains, contentMetaOverrides };
461
+ return { serverUrl, assetsBasePath, connectDomains, contentMetaOverrides };
426
462
  }
427
463
  registerViewResources(toolName, view, toolMeta) {
428
- const hosts = view.hosts ?? ["apps-sdk", "mcp-app"];
429
464
  // Append a content-derived version param so hosts (e.g. ChatGPT) bust
430
465
  // their cache when the bundle changes, but keep the URI stable across
431
466
  // `tools/list` calls when the bundle hasn't changed.
432
467
  const versionParam = this.computeViewVersionParam(view.component);
433
- if (hosts.includes("apps-sdk")) {
434
- const viewResource = {
435
- hostType: "apps-sdk",
436
- uri: `ui://views/apps-sdk/${view.component}.html${versionParam}`,
437
- mimeType: "text/html+skybridge",
438
- buildContentMeta: ({ resourceDomains, connectDomains, domain }, overrides) => {
439
- const defaults = {
440
- "openai/widgetCSP": {
441
- resource_domains: resourceDomains,
442
- connect_domains: connectDomains,
468
+ const viewResource = {
469
+ hostType: "mcp-app",
470
+ uri: `ui://views/ext-apps/${view.component}.html${versionParam}`,
471
+ mimeType: "text/html;profile=mcp-app",
472
+ buildContentMeta: ({ resourceDomains, connectDomains, domain, baseUriDomains }, overrides) => {
473
+ const defaults = {
474
+ ui: {
475
+ csp: {
476
+ resourceDomains,
477
+ connectDomains,
478
+ baseUriDomains,
443
479
  },
444
- "openai/widgetDomain": domain,
445
- "openai/widgetDescription": view.description,
446
- };
447
- const fromView = {
448
- "openai/widgetCSP": {
449
- resource_domains: view.csp?.resourceDomains,
450
- connect_domains: view.csp?.connectDomains,
451
- frame_domains: view.csp?.frameDomains,
452
- redirect_domains: view.csp?.redirectDomains,
453
- },
454
- "openai/widgetDomain": view.domain,
455
- "openai/widgetPrefersBorder": view.prefersBorder,
456
- };
457
- const base = mergeWithUnion(mergeWithUnion(defaults, fromView), {
458
- "openai/widgetDomain": overrides.domain,
459
- });
460
- if (view._meta) {
461
- return { ...base, ...view._meta };
462
- }
463
- return base;
464
- },
465
- };
466
- this.registerViewResource({
467
- name: toolName,
468
- viewResource,
469
- view,
470
- });
471
- toolMeta["openai/outputTemplate"] = viewResource.uri;
472
- }
473
- if (hosts.includes("mcp-app")) {
474
- const viewResource = {
475
- hostType: "mcp-app",
476
- uri: `ui://views/ext-apps/${view.component}.html${versionParam}`,
477
- mimeType: "text/html;profile=mcp-app",
478
- buildContentMeta: ({ resourceDomains, connectDomains, domain, baseUriDomains }, overrides) => {
479
- const defaults = {
480
- ui: {
481
- csp: {
482
- resourceDomains,
483
- connectDomains,
484
- baseUriDomains,
485
- },
486
- domain,
487
- },
488
- };
489
- const fromView = {
490
- ui: {
491
- ...(view.description && { description: view.description }),
492
- ...(view.prefersBorder !== undefined && {
493
- prefersBorder: view.prefersBorder,
480
+ domain,
481
+ },
482
+ };
483
+ const fromView = {
484
+ ui: {
485
+ ...(view.description && { description: view.description }),
486
+ ...(view.prefersBorder !== undefined && {
487
+ prefersBorder: view.prefersBorder,
488
+ }),
489
+ ...(view.domain && { domain: view.domain }),
490
+ csp: {
491
+ ...(view.csp?.resourceDomains && {
492
+ resourceDomains: view.csp.resourceDomains,
493
+ }),
494
+ ...(view.csp?.connectDomains && {
495
+ connectDomains: view.csp.connectDomains,
496
+ }),
497
+ ...(view.csp?.frameDomains && {
498
+ frameDomains: view.csp.frameDomains,
499
+ }),
500
+ ...(view.csp?.baseUriDomains && {
501
+ baseUriDomains: view.csp.baseUriDomains,
494
502
  }),
495
- ...(view.domain && { domain: view.domain }),
496
- csp: {
497
- ...(view.csp?.resourceDomains && {
498
- resourceDomains: view.csp.resourceDomains,
499
- }),
500
- ...(view.csp?.connectDomains && {
501
- connectDomains: view.csp.connectDomains,
502
- }),
503
- ...(view.csp?.frameDomains && {
504
- frameDomains: view.csp.frameDomains,
505
- }),
506
- ...(view.csp?.baseUriDomains && {
507
- baseUriDomains: view.csp.baseUriDomains,
508
- }),
509
- ...(view.csp?.redirectDomains && {
510
- redirectDomains: view.csp.redirectDomains,
511
- }),
512
- },
513
503
  },
514
- };
515
- const base = mergeWithUnion(mergeWithUnion(defaults, fromView), {
516
- ui: overrides,
517
- });
518
- if (view._meta) {
519
- return { ...base, ...view._meta };
520
- }
521
- return base;
522
- },
523
- };
524
- this.registerViewResource({
525
- name: toolName,
526
- viewResource,
527
- view,
528
- });
529
- // @ts-expect-error - For backwards compatibility with Claude current implementation of the specs
530
- toolMeta["ui/resourceUri"] = viewResource.uri;
531
- toolMeta.ui = { ...toolMeta.ui, resourceUri: viewResource.uri };
532
- }
504
+ },
505
+ };
506
+ const ui = mergeWithUnion(mergeWithUnion(defaults, fromView), {
507
+ ui: overrides,
508
+ });
509
+ const base = {
510
+ ...ui,
511
+ ...(view.description && {
512
+ "openai/widgetDescription": view.description,
513
+ }),
514
+ ...(view.csp?.redirectDomains && {
515
+ "openai/widgetCSP": { redirect_domains: view.csp.redirectDomains },
516
+ }),
517
+ };
518
+ if (view._meta) {
519
+ return { ...base, ...view._meta };
520
+ }
521
+ return base;
522
+ },
523
+ };
524
+ this.registerViewResource({ name: toolName, viewResource, view });
525
+ // Advertise via the MCP Apps standard pointer only — ChatGPT renders from
526
+ // ui.resourceUri (verified), and not emitting openai/outputTemplate lets us
527
+ // retire the legacy apps-sdk resource later. The legacy apps-sdk URL is still
528
+ // served (see registerViewResource) so already-published apps keep resolving.
529
+ // @ts-expect-error - For backwards compatibility with Claude current implementation of the specs
530
+ toolMeta["ui/resourceUri"] = viewResource.uri;
531
+ toolMeta.ui = { ...toolMeta.ui, resourceUri: viewResource.uri };
533
532
  }
534
533
  registerViewResource({ name, viewResource, view, }) {
535
534
  const { hostType, uri: viewUri, mimeType, buildContentMeta } = viewResource;
@@ -544,19 +543,24 @@ export class McpServer extends McpServerBaseOmitted {
544
543
  };
545
544
  this.viewMetaBuilders.set(viewUri, buildMeta);
546
545
  this.viewUriByPath.set(stripQuery(viewUri), viewUri);
546
+ this.serveLegacyAppsSdkUrl(view.component, viewUri);
547
547
  this.registerResource(name, viewUri, { description: view.description }, async (uri, extra) => {
548
548
  const isProduction = process.env.NODE_ENV === "production";
549
- const { serverUrl } = this.resolveViewRequestContext(extra);
549
+ const { serverUrl, assetsBasePath } = this.resolveViewRequestContext(extra);
550
+ // The view resolves all assets (template imports + runtime lazy chunks
551
+ // via `window.skybridge.serverUrl`) against this base, so it carries the
552
+ // proxy path prefix. CSP domains in `buildMeta` stay the bare origin.
553
+ const viewBase = `${serverUrl}${assetsBasePath}`;
550
554
  const html = isProduction
551
555
  ? templateHelper.renderProduction({
552
556
  hostType,
553
- serverUrl,
557
+ serverUrl: viewBase,
554
558
  viewFile: this.lookupViewFile(view.component),
555
559
  styleFile: this.lookupDistFile("style.css") ?? "",
556
560
  })
557
561
  : templateHelper.renderDevelopment({
558
562
  hostType,
559
- serverUrl,
563
+ serverUrl: viewBase,
560
564
  viewName: view.component,
561
565
  });
562
566
  return {
@@ -566,6 +570,11 @@ export class McpServer extends McpServerBaseOmitted {
566
570
  };
567
571
  });
568
572
  }
573
+ serveLegacyAppsSdkUrl(component, canonicalUri) {
574
+ this.viewUriByPath.set(`ui://views/apps-sdk/${component}.html`, canonicalUri);
575
+ this.viewUriByPath.set(`ui://widgets/apps-sdk/${component}.html`, canonicalUri);
576
+ this.viewUriByPath.set(`ui://widgets/ext-apps/${component}.html`, canonicalUri);
577
+ }
569
578
  wrapHandler(cb, { attachViewUUID }) {
570
579
  return async (args, extra) => {
571
580
  const result = await cb(args, extra);