skybridge 0.0.0-dev.d5560ad → 0.0.0-dev.d55b422

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 (132) hide show
  1. package/README.md +1 -9
  2. package/dist/cli/build-helpers.d.ts +3 -0
  3. package/dist/cli/build-helpers.js +15 -4
  4. package/dist/cli/build-helpers.js.map +1 -1
  5. package/dist/cli/build-helpers.test.js +26 -1
  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 +68 -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-execute-steps.d.ts +1 -1
  18. package/dist/cli/use-nodemon.d.ts +14 -0
  19. package/dist/cli/use-nodemon.js +71 -60
  20. package/dist/cli/use-nodemon.js.map +1 -1
  21. package/dist/cli/use-typescript-check.d.ts +8 -2
  22. package/dist/cli/use-typescript-check.js +70 -67
  23. package/dist/cli/use-typescript-check.js.map +1 -1
  24. package/dist/commands/build.d.ts +0 -2
  25. package/dist/commands/build.js +2 -61
  26. package/dist/commands/build.js.map +1 -1
  27. package/dist/commands/dev.d.ts +1 -0
  28. package/dist/commands/dev.js +32 -0
  29. package/dist/commands/dev.js.map +1 -1
  30. package/dist/server/auth/providers/auth0.d.ts +8 -18
  31. package/dist/server/auth/providers/auth0.js +8 -19
  32. package/dist/server/auth/providers/auth0.js.map +1 -1
  33. package/dist/server/auth/providers/auth0.test.js +8 -0
  34. package/dist/server/auth/providers/auth0.test.js.map +1 -1
  35. package/dist/server/auth/providers/custom.d.ts +14 -1
  36. package/dist/server/auth/providers/custom.js +21 -9
  37. package/dist/server/auth/providers/custom.js.map +1 -1
  38. package/dist/server/auth/providers/custom.test.js +41 -4
  39. package/dist/server/auth/providers/custom.test.js.map +1 -1
  40. package/dist/server/auth/providers/descope.d.ts +5 -3
  41. package/dist/server/auth/providers/descope.js +9 -5
  42. package/dist/server/auth/providers/descope.js.map +1 -1
  43. package/dist/server/auth/providers/descope.test.js +12 -10
  44. package/dist/server/auth/providers/descope.test.js.map +1 -1
  45. package/dist/server/server.d.ts +21 -5
  46. package/dist/server/server.js +94 -103
  47. package/dist/server/server.js.map +1 -1
  48. package/dist/server/view-name.test-d.d.ts +1 -0
  49. package/dist/server/view-name.test-d.js +8 -0
  50. package/dist/server/view-name.test-d.js.map +1 -0
  51. package/dist/server/view-resource-resolution.test.js +93 -10
  52. package/dist/server/view-resource-resolution.test.js.map +1 -1
  53. package/dist/test/view.test.js +69 -101
  54. package/dist/test/view.test.js.map +1 -1
  55. package/dist/web/bridges/adaptor.d.ts +51 -0
  56. package/dist/web/bridges/adaptor.js +330 -0
  57. package/dist/web/bridges/adaptor.js.map +1 -0
  58. package/dist/web/bridges/adaptor.test.d.ts +1 -0
  59. package/dist/web/bridges/adaptor.test.js +208 -0
  60. package/dist/web/bridges/adaptor.test.js.map +1 -0
  61. package/dist/web/bridges/apps-sdk/bridge.d.ts +6 -2
  62. package/dist/web/bridges/apps-sdk/bridge.js +15 -4
  63. package/dist/web/bridges/apps-sdk/bridge.js.map +1 -1
  64. package/dist/web/bridges/apps-sdk/index.d.ts +0 -1
  65. package/dist/web/bridges/apps-sdk/index.js +0 -1
  66. package/dist/web/bridges/apps-sdk/index.js.map +1 -1
  67. package/dist/web/bridges/get-adaptor.d.ts +4 -4
  68. package/dist/web/bridges/get-adaptor.js +6 -11
  69. package/dist/web/bridges/get-adaptor.js.map +1 -1
  70. package/dist/web/bridges/get-adaptor.test.d.ts +1 -0
  71. package/dist/web/bridges/get-adaptor.test.js +32 -0
  72. package/dist/web/bridges/get-adaptor.test.js.map +1 -0
  73. package/dist/web/bridges/mcp-app/bridge.d.ts +13 -2
  74. package/dist/web/bridges/mcp-app/bridge.js +46 -4
  75. package/dist/web/bridges/mcp-app/bridge.js.map +1 -1
  76. package/dist/web/bridges/mcp-app/bridge.test.d.ts +1 -0
  77. package/dist/web/bridges/mcp-app/bridge.test.js +17 -0
  78. package/dist/web/bridges/mcp-app/bridge.test.js.map +1 -0
  79. package/dist/web/bridges/mcp-app/index.d.ts +0 -1
  80. package/dist/web/bridges/mcp-app/index.js +0 -1
  81. package/dist/web/bridges/mcp-app/index.js.map +1 -1
  82. package/dist/web/bridges/mcp-app/view-tools.test.js +5 -6
  83. package/dist/web/bridges/mcp-app/view-tools.test.js.map +1 -1
  84. package/dist/web/bridges/types.d.ts +10 -0
  85. package/dist/web/bridges/types.js +14 -1
  86. package/dist/web/bridges/types.js.map +1 -1
  87. package/dist/web/bridges/types.test.d.ts +1 -0
  88. package/dist/web/bridges/types.test.js +19 -0
  89. package/dist/web/bridges/types.test.js.map +1 -0
  90. package/dist/web/components/modal-provider.d.ts +1 -1
  91. package/dist/web/components/modal-provider.js +4 -3
  92. package/dist/web/components/modal-provider.js.map +1 -1
  93. package/dist/web/create-store.test.js +8 -5
  94. package/dist/web/create-store.test.js.map +1 -1
  95. package/dist/web/data-llm.d.ts +1 -1
  96. package/dist/web/data-llm.test.js +17 -5
  97. package/dist/web/data-llm.test.js.map +1 -1
  98. package/dist/web/hooks/use-call-tool.test.js +37 -23
  99. package/dist/web/hooks/use-call-tool.test.js.map +1 -1
  100. package/dist/web/hooks/use-display-mode.test.js +56 -20
  101. package/dist/web/hooks/use-display-mode.test.js.map +1 -1
  102. package/dist/web/hooks/use-download.test.js +11 -3
  103. package/dist/web/hooks/use-download.test.js.map +1 -1
  104. package/dist/web/hooks/use-files.test.js +10 -2
  105. package/dist/web/hooks/use-files.test.js.map +1 -1
  106. package/dist/web/hooks/use-layout.test.js +59 -26
  107. package/dist/web/hooks/use-layout.test.js.map +1 -1
  108. package/dist/web/hooks/use-open-external.test.js +13 -3
  109. package/dist/web/hooks/use-open-external.test.js.map +1 -1
  110. package/dist/web/hooks/use-request-close.test.js +35 -40
  111. package/dist/web/hooks/use-request-close.test.js.map +1 -1
  112. package/dist/web/hooks/use-request-modal.test.js +10 -0
  113. package/dist/web/hooks/use-request-modal.test.js.map +1 -1
  114. package/dist/web/hooks/use-request-size.test.js +35 -53
  115. package/dist/web/hooks/use-request-size.test.js.map +1 -1
  116. package/dist/web/hooks/use-set-open-in-app-url.test.js +28 -8
  117. package/dist/web/hooks/use-set-open-in-app-url.test.js.map +1 -1
  118. package/dist/web/hooks/use-tool-info.test.js +37 -27
  119. package/dist/web/hooks/use-tool-info.test.js.map +1 -1
  120. package/dist/web/hooks/use-user.test.js +81 -35
  121. package/dist/web/hooks/use-user.test.js.map +1 -1
  122. package/dist/web/hooks/use-view-state.test.js +6 -2
  123. package/dist/web/hooks/use-view-state.test.js.map +1 -1
  124. package/dist/web/plugin/plugin.js +0 -1
  125. package/dist/web/plugin/plugin.js.map +1 -1
  126. package/package.json +20 -20
  127. package/dist/web/bridges/apps-sdk/adaptor.d.ts +0 -29
  128. package/dist/web/bridges/apps-sdk/adaptor.js +0 -117
  129. package/dist/web/bridges/apps-sdk/adaptor.js.map +0 -1
  130. package/dist/web/bridges/mcp-app/adaptor.d.ts +0 -53
  131. package/dist/web/bridges/mcp-app/adaptor.js +0 -283
  132. package/dist/web/bridges/mcp-app/adaptor.js.map +0 -1
@@ -19,7 +19,11 @@ export type ToolDef<TInput = unknown, TOutput = unknown, TResponseMetadata = unk
19
19
  output: TOutput;
20
20
  responseMetadata: TResponseMetadata;
21
21
  };
22
- /** Which host runtime a view targets — `"apps-sdk"` (ChatGPT) or `"mcp-app"` (MCP Apps spec). */
22
+ /**
23
+ * @deprecated Views now always emit a single ext-apps resource; host targeting
24
+ * no longer applies. Retained for backwards compatibility; will be removed in a
25
+ * future major.
26
+ */
23
27
  export type ViewHostType = "apps-sdk" | "mcp-app";
24
28
  /**
25
29
  * Content Security Policy origins attached to a view's resource. Each list is
@@ -45,8 +49,16 @@ export interface ViewCsp {
45
49
  */
46
50
  export interface ViewNameRegistry {
47
51
  }
52
+ /**
53
+ * Resolve view component names from a registry: the union of its keys, or
54
+ * `string` when the registry is empty. The empty case happens before
55
+ * `.skybridge/views.d.ts` is generated; falling back to `string` keeps valid
56
+ * view names from erroring on a fresh checkout, and narrowing kicks in once
57
+ * the generated file augments the registry.
58
+ */
59
+ export type ViewNameFor<Registry> = [keyof Registry & string] extends [never] ? string : keyof Registry & string;
48
60
  /** Union of valid view component names. Narrowed by {@link ViewNameRegistry}. */
49
- export type ViewName = keyof ViewNameRegistry & string;
61
+ export type ViewName = ViewNameFor<ViewNameRegistry>;
50
62
  /**
51
63
  * Pass under `view` in a tool's `registerTool` config to render the tool's
52
64
  * result through a Skybridge view instead of a plain text response.
@@ -56,11 +68,14 @@ export interface ViewConfig {
56
68
  component: ViewName;
57
69
  /** Human-readable label the host may show alongside the view. */
58
70
  description?: string;
59
- /** Restrict where the view is rendered. Defaults to all known hosts. */
71
+ /**
72
+ * @deprecated No-op. Every view emits a single ext-apps resource regardless
73
+ * of this value. Will be removed in a future major.
74
+ */
60
75
  hosts?: ViewHostType[];
61
- /** Apps SDK only: request a visible border around the widget. */
76
+ /** Request a visible border around the view (forwarded as `ui.prefersBorder`). */
62
77
  prefersBorder?: boolean;
63
- /** Apps SDK only: override the iframe's served domain (advanced). */
78
+ /** Override the iframe's served domain (advanced; forwarded as `ui.domain`). */
64
79
  domain?: string;
65
80
  /** Per-view CSP overrides — see {@link ViewCsp}. */
66
81
  csp?: ViewCsp;
@@ -350,6 +365,7 @@ export declare class McpServer<TTools extends Record<string, ToolDef> = Record<n
350
365
  private resolveViewRequestContext;
351
366
  private registerViewResources;
352
367
  private registerViewResource;
368
+ private serveLegacyAppsSdkUrl;
353
369
  private wrapHandler;
354
370
  private computeViewVersionParam;
355
371
  private lookupViewFile;
@@ -19,6 +19,19 @@ const mergeWithUnion = (target, source) => {
19
19
  }
20
20
  });
21
21
  };
22
+ /**
23
+ * Normalize an `x-forwarded-prefix` value into a leading-slash, no-trailing-slash
24
+ * path. Takes the first hop of a comma-separated proxy chain.
25
+ * "/v1/", "v1", "/v1, /internal" → "/v1"; "", "/", undefined → "".
26
+ */
27
+ function normalizeForwardedPrefix(raw) {
28
+ const firstHop = raw?.split(",")[0]?.trim() ?? "";
29
+ const trimmed = firstHop.replace(/\/+$/, "");
30
+ if (trimmed === "") {
31
+ return "";
32
+ }
33
+ return trimmed.startsWith("/") ? trimmed : `/${trimmed}`;
34
+ }
22
35
  /**
23
36
  * Drop the query string from a `ui://` view URI, leaving the bare path. The
24
37
  * `?v=` cache key is the only query we append, so a plain split is enough and
@@ -236,7 +249,7 @@ export class McpServer extends McpServerBaseOmitted {
236
249
  const result = (await next());
237
250
  for (const content of result.contents ?? []) {
238
251
  if (typeof content.uri === "string" &&
239
- stripQuery(content.uri) === path) {
252
+ stripQuery(content.uri) === stripQuery(canonical)) {
240
253
  content.uri = requested;
241
254
  }
242
255
  }
@@ -383,6 +396,11 @@ export class McpServer extends McpServerBaseOmitted {
383
396
  };
384
397
  const isClaude = header("user-agent") === "Claude-User";
385
398
  const serverUrl = resolveServerOrigin(header);
399
+ // Path prefix the proxy routed this request under (e.g. `foo.com/v1`). Read
400
+ // per-request so one process can serve many hosts/prefixes at once: the
401
+ // origin is recovered from x-forwarded-host, the prefix from
402
+ // x-forwarded-prefix. Empty when served at the origin root.
403
+ const assetsBasePath = normalizeForwardedPrefix(header("x-forwarded-prefix"));
386
404
  const connectDomains = [serverUrl];
387
405
  if (!isProduction) {
388
406
  const wsUrl = new URL(serverUrl);
@@ -403,114 +421,77 @@ export class McpServer extends McpServerBaseOmitted {
403
421
  .slice(0, 32);
404
422
  contentMetaOverrides = { domain: `${hash}.claudemcpcontent.com` };
405
423
  }
406
- return { serverUrl, connectDomains, contentMetaOverrides };
424
+ return { serverUrl, assetsBasePath, connectDomains, contentMetaOverrides };
407
425
  }
408
426
  registerViewResources(toolName, view, toolMeta) {
409
- const hosts = view.hosts ?? ["apps-sdk", "mcp-app"];
410
427
  // Append a content-derived version param so hosts (e.g. ChatGPT) bust
411
428
  // their cache when the bundle changes, but keep the URI stable across
412
429
  // `tools/list` calls when the bundle hasn't changed.
413
430
  const versionParam = this.computeViewVersionParam(view.component);
414
- if (hosts.includes("apps-sdk")) {
415
- const viewResource = {
416
- hostType: "apps-sdk",
417
- uri: `ui://views/apps-sdk/${view.component}.html${versionParam}`,
418
- mimeType: "text/html+skybridge",
419
- buildContentMeta: ({ resourceDomains, connectDomains, domain }, overrides) => {
420
- const defaults = {
421
- "openai/widgetCSP": {
422
- resource_domains: resourceDomains,
423
- connect_domains: connectDomains,
431
+ const viewResource = {
432
+ hostType: "mcp-app",
433
+ uri: `ui://views/ext-apps/${view.component}.html${versionParam}`,
434
+ mimeType: "text/html;profile=mcp-app",
435
+ buildContentMeta: ({ resourceDomains, connectDomains, domain, baseUriDomains }, overrides) => {
436
+ const defaults = {
437
+ ui: {
438
+ csp: {
439
+ resourceDomains,
440
+ connectDomains,
441
+ baseUriDomains,
424
442
  },
425
- "openai/widgetDomain": domain,
426
- "openai/widgetDescription": view.description,
427
- };
428
- const fromView = {
429
- "openai/widgetCSP": {
430
- resource_domains: view.csp?.resourceDomains,
431
- connect_domains: view.csp?.connectDomains,
432
- frame_domains: view.csp?.frameDomains,
433
- redirect_domains: view.csp?.redirectDomains,
434
- },
435
- "openai/widgetDomain": view.domain,
436
- "openai/widgetPrefersBorder": view.prefersBorder,
437
- };
438
- const base = mergeWithUnion(mergeWithUnion(defaults, fromView), {
439
- "openai/widgetDomain": overrides.domain,
440
- });
441
- if (view._meta) {
442
- return { ...base, ...view._meta };
443
- }
444
- return base;
445
- },
446
- };
447
- this.registerViewResource({
448
- name: toolName,
449
- viewResource,
450
- view,
451
- });
452
- toolMeta["openai/outputTemplate"] = viewResource.uri;
453
- }
454
- if (hosts.includes("mcp-app")) {
455
- const viewResource = {
456
- hostType: "mcp-app",
457
- uri: `ui://views/ext-apps/${view.component}.html${versionParam}`,
458
- mimeType: "text/html;profile=mcp-app",
459
- buildContentMeta: ({ resourceDomains, connectDomains, domain, baseUriDomains }, overrides) => {
460
- const defaults = {
461
- ui: {
462
- csp: {
463
- resourceDomains,
464
- connectDomains,
465
- baseUriDomains,
466
- },
467
- domain,
468
- },
469
- };
470
- const fromView = {
471
- ui: {
472
- ...(view.description && { description: view.description }),
473
- ...(view.prefersBorder !== undefined && {
474
- prefersBorder: view.prefersBorder,
443
+ domain,
444
+ },
445
+ };
446
+ const fromView = {
447
+ ui: {
448
+ ...(view.description && { description: view.description }),
449
+ ...(view.prefersBorder !== undefined && {
450
+ prefersBorder: view.prefersBorder,
451
+ }),
452
+ ...(view.domain && { domain: view.domain }),
453
+ csp: {
454
+ ...(view.csp?.resourceDomains && {
455
+ resourceDomains: view.csp.resourceDomains,
456
+ }),
457
+ ...(view.csp?.connectDomains && {
458
+ connectDomains: view.csp.connectDomains,
459
+ }),
460
+ ...(view.csp?.frameDomains && {
461
+ frameDomains: view.csp.frameDomains,
462
+ }),
463
+ ...(view.csp?.baseUriDomains && {
464
+ baseUriDomains: view.csp.baseUriDomains,
475
465
  }),
476
- ...(view.domain && { domain: view.domain }),
477
- csp: {
478
- ...(view.csp?.resourceDomains && {
479
- resourceDomains: view.csp.resourceDomains,
480
- }),
481
- ...(view.csp?.connectDomains && {
482
- connectDomains: view.csp.connectDomains,
483
- }),
484
- ...(view.csp?.frameDomains && {
485
- frameDomains: view.csp.frameDomains,
486
- }),
487
- ...(view.csp?.baseUriDomains && {
488
- baseUriDomains: view.csp.baseUriDomains,
489
- }),
490
- ...(view.csp?.redirectDomains && {
491
- redirectDomains: view.csp.redirectDomains,
492
- }),
493
- },
494
466
  },
495
- };
496
- const base = mergeWithUnion(mergeWithUnion(defaults, fromView), {
497
- ui: overrides,
498
- });
499
- if (view._meta) {
500
- return { ...base, ...view._meta };
501
- }
502
- return base;
503
- },
504
- };
505
- this.registerViewResource({
506
- name: toolName,
507
- viewResource,
508
- view,
509
- });
510
- // @ts-expect-error - For backwards compatibility with Claude current implementation of the specs
511
- toolMeta["ui/resourceUri"] = viewResource.uri;
512
- toolMeta.ui = { ...toolMeta.ui, resourceUri: viewResource.uri };
513
- }
467
+ },
468
+ };
469
+ const ui = mergeWithUnion(mergeWithUnion(defaults, fromView), {
470
+ ui: overrides,
471
+ });
472
+ const base = {
473
+ ...ui,
474
+ ...(view.description && {
475
+ "openai/widgetDescription": view.description,
476
+ }),
477
+ ...(view.csp?.redirectDomains && {
478
+ "openai/widgetCSP": { redirect_domains: view.csp.redirectDomains },
479
+ }),
480
+ };
481
+ if (view._meta) {
482
+ return { ...base, ...view._meta };
483
+ }
484
+ return base;
485
+ },
486
+ };
487
+ this.registerViewResource({ name: toolName, viewResource, view });
488
+ // Advertise via the MCP Apps standard pointer only — ChatGPT renders from
489
+ // ui.resourceUri (verified), and not emitting openai/outputTemplate lets us
490
+ // retire the legacy apps-sdk resource later. The legacy apps-sdk URL is still
491
+ // served (see registerViewResource) so already-published apps keep resolving.
492
+ // @ts-expect-error - For backwards compatibility with Claude current implementation of the specs
493
+ toolMeta["ui/resourceUri"] = viewResource.uri;
494
+ toolMeta.ui = { ...toolMeta.ui, resourceUri: viewResource.uri };
514
495
  }
515
496
  registerViewResource({ name, viewResource, view, }) {
516
497
  const { hostType, uri: viewUri, mimeType, buildContentMeta } = viewResource;
@@ -525,19 +506,24 @@ export class McpServer extends McpServerBaseOmitted {
525
506
  };
526
507
  this.viewMetaBuilders.set(viewUri, buildMeta);
527
508
  this.viewUriByPath.set(stripQuery(viewUri), viewUri);
509
+ this.serveLegacyAppsSdkUrl(view.component, viewUri);
528
510
  this.registerResource(name, viewUri, { description: view.description }, async (uri, extra) => {
529
511
  const isProduction = process.env.NODE_ENV === "production";
530
- const { serverUrl } = this.resolveViewRequestContext(extra);
512
+ const { serverUrl, assetsBasePath } = this.resolveViewRequestContext(extra);
513
+ // The view resolves all assets (template imports + runtime lazy chunks
514
+ // via `window.skybridge.serverUrl`) against this base, so it carries the
515
+ // proxy path prefix. CSP domains in `buildMeta` stay the bare origin.
516
+ const viewBase = `${serverUrl}${assetsBasePath}`;
531
517
  const html = isProduction
532
518
  ? templateHelper.renderProduction({
533
519
  hostType,
534
- serverUrl,
520
+ serverUrl: viewBase,
535
521
  viewFile: this.lookupViewFile(view.component),
536
522
  styleFile: this.lookupDistFile("style.css") ?? "",
537
523
  })
538
524
  : templateHelper.renderDevelopment({
539
525
  hostType,
540
- serverUrl,
526
+ serverUrl: viewBase,
541
527
  viewName: view.component,
542
528
  });
543
529
  return {
@@ -547,6 +533,11 @@ export class McpServer extends McpServerBaseOmitted {
547
533
  };
548
534
  });
549
535
  }
536
+ serveLegacyAppsSdkUrl(component, canonicalUri) {
537
+ this.viewUriByPath.set(`ui://views/apps-sdk/${component}.html`, canonicalUri);
538
+ this.viewUriByPath.set(`ui://widgets/apps-sdk/${component}.html`, canonicalUri);
539
+ this.viewUriByPath.set(`ui://widgets/ext-apps/${component}.html`, canonicalUri);
540
+ }
550
541
  wrapHandler(cb, { attachViewUUID }) {
551
542
  return async (args, extra) => {
552
543
  const result = await cb(args, extra);