@frontmcp/ui 0.5.0
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.
- package/LICENSE +201 -0
- package/README.md +438 -0
- package/package.json +147 -0
- package/src/adapters/index.d.ts +10 -0
- package/src/adapters/index.js +18 -0
- package/src/adapters/index.js.map +1 -0
- package/src/adapters/platform-meta.d.ts +165 -0
- package/src/adapters/platform-meta.js +310 -0
- package/src/adapters/platform-meta.js.map +1 -0
- package/src/base-template/bridge.d.ts +89 -0
- package/src/base-template/bridge.js +452 -0
- package/src/base-template/bridge.js.map +1 -0
- package/src/base-template/default-base-template.d.ts +91 -0
- package/src/base-template/default-base-template.js +435 -0
- package/src/base-template/default-base-template.js.map +1 -0
- package/src/base-template/index.d.ts +14 -0
- package/src/base-template/index.js +30 -0
- package/src/base-template/index.js.map +1 -0
- package/src/base-template/polyfills.d.ts +30 -0
- package/src/base-template/polyfills.js +190 -0
- package/src/base-template/polyfills.js.map +1 -0
- package/src/base-template/theme-styles.d.ts +73 -0
- package/src/base-template/theme-styles.js +95 -0
- package/src/base-template/theme-styles.js.map +1 -0
- package/src/bridge/adapters/base-adapter.d.ts +103 -0
- package/src/bridge/adapters/base-adapter.js +314 -0
- package/src/bridge/adapters/base-adapter.js.map +1 -0
- package/src/bridge/adapters/claude.adapter.d.ts +66 -0
- package/src/bridge/adapters/claude.adapter.js +145 -0
- package/src/bridge/adapters/claude.adapter.js.map +1 -0
- package/src/bridge/adapters/ext-apps.adapter.d.ts +142 -0
- package/src/bridge/adapters/ext-apps.adapter.js +416 -0
- package/src/bridge/adapters/ext-apps.adapter.js.map +1 -0
- package/src/bridge/adapters/gemini.adapter.d.ts +63 -0
- package/src/bridge/adapters/gemini.adapter.js +160 -0
- package/src/bridge/adapters/gemini.adapter.js.map +1 -0
- package/src/bridge/adapters/generic.adapter.d.ts +55 -0
- package/src/bridge/adapters/generic.adapter.js +108 -0
- package/src/bridge/adapters/generic.adapter.js.map +1 -0
- package/src/bridge/adapters/index.d.ts +25 -0
- package/src/bridge/adapters/index.js +65 -0
- package/src/bridge/adapters/index.js.map +1 -0
- package/src/bridge/adapters/openai.adapter.d.ts +64 -0
- package/src/bridge/adapters/openai.adapter.js +194 -0
- package/src/bridge/adapters/openai.adapter.js.map +1 -0
- package/src/bridge/core/adapter-registry.d.ts +121 -0
- package/src/bridge/core/adapter-registry.js +271 -0
- package/src/bridge/core/adapter-registry.js.map +1 -0
- package/src/bridge/core/bridge-factory.d.ts +198 -0
- package/src/bridge/core/bridge-factory.js +428 -0
- package/src/bridge/core/bridge-factory.js.map +1 -0
- package/src/bridge/core/index.d.ts +9 -0
- package/src/bridge/core/index.js +22 -0
- package/src/bridge/core/index.js.map +1 -0
- package/src/bridge/index.d.ts +61 -0
- package/src/bridge/index.js +94 -0
- package/src/bridge/index.js.map +1 -0
- package/src/bridge/runtime/iife-generator.d.ts +61 -0
- package/src/bridge/runtime/iife-generator.js +940 -0
- package/src/bridge/runtime/iife-generator.js.map +1 -0
- package/src/bridge/runtime/index.d.ts +8 -0
- package/src/bridge/runtime/index.js +16 -0
- package/src/bridge/runtime/index.js.map +1 -0
- package/src/bridge/types.d.ts +385 -0
- package/src/bridge/types.js +11 -0
- package/src/bridge/types.js.map +1 -0
- package/src/build/cdn-resources.d.ts +140 -0
- package/src/build/cdn-resources.js +314 -0
- package/src/build/cdn-resources.js.map +1 -0
- package/src/build/index.d.ts +294 -0
- package/src/build/index.js +325 -0
- package/src/build/index.js.map +1 -0
- package/src/build/widget-manifest.d.ts +212 -0
- package/src/build/widget-manifest.js +652 -0
- package/src/build/widget-manifest.js.map +1 -0
- package/src/bundler/bundler.d.ts +110 -0
- package/src/bundler/bundler.js +432 -0
- package/src/bundler/bundler.js.map +1 -0
- package/src/bundler/cache.d.ts +172 -0
- package/src/bundler/cache.js +250 -0
- package/src/bundler/cache.js.map +1 -0
- package/src/bundler/index.d.ts +41 -0
- package/src/bundler/index.js +73 -0
- package/src/bundler/index.js.map +1 -0
- package/src/bundler/sandbox/enclave-adapter.d.ts +120 -0
- package/src/bundler/sandbox/enclave-adapter.js +339 -0
- package/src/bundler/sandbox/enclave-adapter.js.map +1 -0
- package/src/bundler/sandbox/executor.d.ts +13 -0
- package/src/bundler/sandbox/executor.js +22 -0
- package/src/bundler/sandbox/executor.js.map +1 -0
- package/src/bundler/sandbox/policy.d.ts +61 -0
- package/src/bundler/sandbox/policy.js +238 -0
- package/src/bundler/sandbox/policy.js.map +1 -0
- package/src/bundler/types.d.ts +347 -0
- package/src/bundler/types.js +132 -0
- package/src/bundler/types.js.map +1 -0
- package/src/components/alert.d.ts +71 -0
- package/src/components/alert.js +189 -0
- package/src/components/alert.js.map +1 -0
- package/src/components/alert.schema.d.ts +114 -0
- package/src/components/alert.schema.js +105 -0
- package/src/components/alert.schema.js.map +1 -0
- package/src/components/avatar.d.ts +76 -0
- package/src/components/avatar.js +176 -0
- package/src/components/avatar.js.map +1 -0
- package/src/components/avatar.schema.d.ts +169 -0
- package/src/components/avatar.schema.js +103 -0
- package/src/components/avatar.schema.js.map +1 -0
- package/src/components/badge.d.ts +70 -0
- package/src/components/badge.js +149 -0
- package/src/components/badge.js.map +1 -0
- package/src/components/badge.schema.d.ts +109 -0
- package/src/components/badge.schema.js +96 -0
- package/src/components/badge.schema.js.map +1 -0
- package/src/components/button.d.ts +111 -0
- package/src/components/button.js +336 -0
- package/src/components/button.js.map +1 -0
- package/src/components/button.schema.d.ts +148 -0
- package/src/components/button.schema.js +121 -0
- package/src/components/button.schema.js.map +1 -0
- package/src/components/card.d.ts +60 -0
- package/src/components/card.js +117 -0
- package/src/components/card.js.map +1 -0
- package/src/components/card.schema.d.ts +113 -0
- package/src/components/card.schema.js +98 -0
- package/src/components/card.schema.js.map +1 -0
- package/src/components/form.d.ts +239 -0
- package/src/components/form.js +420 -0
- package/src/components/form.js.map +1 -0
- package/src/components/form.schema.d.ts +441 -0
- package/src/components/form.schema.js +406 -0
- package/src/components/form.schema.js.map +1 -0
- package/src/components/index.d.ts +29 -0
- package/src/components/index.js +98 -0
- package/src/components/index.js.map +1 -0
- package/src/components/list.d.ts +127 -0
- package/src/components/list.js +279 -0
- package/src/components/list.js.map +1 -0
- package/src/components/list.schema.d.ts +134 -0
- package/src/components/list.schema.js +168 -0
- package/src/components/list.schema.js.map +1 -0
- package/src/components/modal.d.ts +111 -0
- package/src/components/modal.js +260 -0
- package/src/components/modal.js.map +1 -0
- package/src/components/modal.schema.d.ts +186 -0
- package/src/components/modal.schema.js +167 -0
- package/src/components/modal.schema.js.map +1 -0
- package/src/components/table.d.ts +105 -0
- package/src/components/table.js +283 -0
- package/src/components/table.js.map +1 -0
- package/src/components/table.schema.d.ts +159 -0
- package/src/components/table.schema.js +173 -0
- package/src/components/table.schema.js.map +1 -0
- package/src/handlebars/helpers.d.ts +348 -0
- package/src/handlebars/helpers.js +605 -0
- package/src/handlebars/helpers.js.map +1 -0
- package/src/handlebars/index.d.ts +193 -0
- package/src/handlebars/index.js +350 -0
- package/src/handlebars/index.js.map +1 -0
- package/src/index.d.ts +50 -0
- package/src/index.js +192 -0
- package/src/index.js.map +1 -0
- package/src/layouts/base.d.ts +88 -0
- package/src/layouts/base.js +227 -0
- package/src/layouts/base.js.map +1 -0
- package/src/layouts/index.d.ts +7 -0
- package/src/layouts/index.js +25 -0
- package/src/layouts/index.js.map +1 -0
- package/src/layouts/presets.d.ts +133 -0
- package/src/layouts/presets.js +277 -0
- package/src/layouts/presets.js.map +1 -0
- package/src/pages/consent.d.ts +116 -0
- package/src/pages/consent.js +218 -0
- package/src/pages/consent.js.map +1 -0
- package/src/pages/error.d.ts +100 -0
- package/src/pages/error.js +263 -0
- package/src/pages/error.js.map +1 -0
- package/src/pages/index.d.ts +8 -0
- package/src/pages/index.js +27 -0
- package/src/pages/index.js.map +1 -0
- package/src/react/Alert.d.ts +101 -0
- package/src/react/Alert.js +51 -0
- package/src/react/Alert.js.map +1 -0
- package/src/react/Badge.d.ts +100 -0
- package/src/react/Badge.js +55 -0
- package/src/react/Badge.js.map +1 -0
- package/src/react/Button.d.ts +108 -0
- package/src/react/Button.js +52 -0
- package/src/react/Button.js.map +1 -0
- package/src/react/Card.d.ts +103 -0
- package/src/react/Card.js +55 -0
- package/src/react/Card.js.map +1 -0
- package/src/react/hooks/context.d.ts +178 -0
- package/src/react/hooks/context.js +287 -0
- package/src/react/hooks/context.js.map +1 -0
- package/src/react/hooks/index.d.ts +41 -0
- package/src/react/hooks/index.js +61 -0
- package/src/react/hooks/index.js.map +1 -0
- package/src/react/hooks/tools.d.ts +283 -0
- package/src/react/hooks/tools.js +465 -0
- package/src/react/hooks/tools.js.map +1 -0
- package/src/react/index.d.ts +80 -0
- package/src/react/index.js +113 -0
- package/src/react/index.js.map +1 -0
- package/src/react/types.d.ts +105 -0
- package/src/react/types.js +12 -0
- package/src/react/types.js.map +1 -0
- package/src/react/utils.d.ts +42 -0
- package/src/react/utils.js +99 -0
- package/src/react/utils.js.map +1 -0
- package/src/registry/index.d.ts +45 -0
- package/src/registry/index.js +67 -0
- package/src/registry/index.js.map +1 -0
- package/src/registry/render-template.d.ts +86 -0
- package/src/registry/render-template.js +239 -0
- package/src/registry/render-template.js.map +1 -0
- package/src/registry/tool-ui.registry.d.ts +260 -0
- package/src/registry/tool-ui.registry.js +438 -0
- package/src/registry/tool-ui.registry.js.map +1 -0
- package/src/registry/uri-utils.d.ts +55 -0
- package/src/registry/uri-utils.js +97 -0
- package/src/registry/uri-utils.js.map +1 -0
- package/src/render/index.d.ts +7 -0
- package/src/render/index.js +14 -0
- package/src/render/index.js.map +1 -0
- package/src/render/prerender.d.ts +56 -0
- package/src/render/prerender.js +98 -0
- package/src/render/prerender.js.map +1 -0
- package/src/renderers/cache.d.ts +144 -0
- package/src/renderers/cache.js +240 -0
- package/src/renderers/cache.js.map +1 -0
- package/src/renderers/html.renderer.d.ts +122 -0
- package/src/renderers/html.renderer.js +204 -0
- package/src/renderers/html.renderer.js.map +1 -0
- package/src/renderers/index.d.ts +35 -0
- package/src/renderers/index.js +70 -0
- package/src/renderers/index.js.map +1 -0
- package/src/renderers/mdx.renderer.d.ts +119 -0
- package/src/renderers/mdx.renderer.js +305 -0
- package/src/renderers/mdx.renderer.js.map +1 -0
- package/src/renderers/react.renderer.d.ts +95 -0
- package/src/renderers/react.renderer.js +260 -0
- package/src/renderers/react.renderer.js.map +1 -0
- package/src/renderers/registry.d.ts +133 -0
- package/src/renderers/registry.js +232 -0
- package/src/renderers/registry.js.map +1 -0
- package/src/renderers/types.d.ts +341 -0
- package/src/renderers/types.js +9 -0
- package/src/renderers/types.js.map +1 -0
- package/src/renderers/utils/detect.d.ts +106 -0
- package/src/renderers/utils/detect.js +267 -0
- package/src/renderers/utils/detect.js.map +1 -0
- package/src/renderers/utils/hash.d.ts +39 -0
- package/src/renderers/utils/hash.js +75 -0
- package/src/renderers/utils/hash.js.map +1 -0
- package/src/renderers/utils/index.d.ts +8 -0
- package/src/renderers/utils/index.js +28 -0
- package/src/renderers/utils/index.js.map +1 -0
- package/src/renderers/utils/transpiler.d.ts +88 -0
- package/src/renderers/utils/transpiler.js +215 -0
- package/src/renderers/utils/transpiler.js.map +1 -0
- package/src/runtime/adapters/html.adapter.d.ts +58 -0
- package/src/runtime/adapters/html.adapter.js +131 -0
- package/src/runtime/adapters/html.adapter.js.map +1 -0
- package/src/runtime/adapters/index.d.ts +25 -0
- package/src/runtime/adapters/index.js +54 -0
- package/src/runtime/adapters/index.js.map +1 -0
- package/src/runtime/adapters/mdx.adapter.d.ts +72 -0
- package/src/runtime/adapters/mdx.adapter.js +241 -0
- package/src/runtime/adapters/mdx.adapter.js.map +1 -0
- package/src/runtime/adapters/react.adapter.d.ts +69 -0
- package/src/runtime/adapters/react.adapter.js +245 -0
- package/src/runtime/adapters/react.adapter.js.map +1 -0
- package/src/runtime/adapters/types.d.ts +94 -0
- package/src/runtime/adapters/types.js +11 -0
- package/src/runtime/adapters/types.js.map +1 -0
- package/src/runtime/csp.d.ts +37 -0
- package/src/runtime/csp.js +140 -0
- package/src/runtime/csp.js.map +1 -0
- package/src/runtime/index.d.ts +16 -0
- package/src/runtime/index.js +72 -0
- package/src/runtime/index.js.map +1 -0
- package/src/runtime/mcp-bridge.d.ts +100 -0
- package/src/runtime/mcp-bridge.js +581 -0
- package/src/runtime/mcp-bridge.js.map +1 -0
- package/src/runtime/renderer-runtime.d.ts +132 -0
- package/src/runtime/renderer-runtime.js +389 -0
- package/src/runtime/renderer-runtime.js.map +1 -0
- package/src/runtime/sanitizer.d.ts +171 -0
- package/src/runtime/sanitizer.js +318 -0
- package/src/runtime/sanitizer.js.map +1 -0
- package/src/runtime/types.d.ts +414 -0
- package/src/runtime/types.js +12 -0
- package/src/runtime/types.js.map +1 -0
- package/src/runtime/wrapper.d.ts +375 -0
- package/src/runtime/wrapper.js +1793 -0
- package/src/runtime/wrapper.js.map +1 -0
- package/src/styles/index.d.ts +7 -0
- package/src/styles/index.js +11 -0
- package/src/styles/index.js.map +1 -0
- package/src/styles/variants.d.ts +50 -0
- package/src/styles/variants.js +175 -0
- package/src/styles/variants.js.map +1 -0
- package/src/theme/cdn.d.ts +194 -0
- package/src/theme/cdn.js +375 -0
- package/src/theme/cdn.js.map +1 -0
- package/src/theme/index.d.ts +17 -0
- package/src/theme/index.js +57 -0
- package/src/theme/index.js.map +1 -0
- package/src/theme/platforms.d.ts +106 -0
- package/src/theme/platforms.js +161 -0
- package/src/theme/platforms.js.map +1 -0
- package/src/theme/presets/github-openai.d.ts +49 -0
- package/src/theme/presets/github-openai.js +189 -0
- package/src/theme/presets/github-openai.js.map +1 -0
- package/src/theme/presets/index.d.ts +10 -0
- package/src/theme/presets/index.js +17 -0
- package/src/theme/presets/index.js.map +1 -0
- package/src/theme/theme.d.ts +395 -0
- package/src/theme/theme.js +332 -0
- package/src/theme/theme.js.map +1 -0
- package/src/tool-template/builder.d.ts +212 -0
- package/src/tool-template/builder.js +397 -0
- package/src/tool-template/builder.js.map +1 -0
- package/src/tool-template/index.d.ts +15 -0
- package/src/tool-template/index.js +38 -0
- package/src/tool-template/index.js.map +1 -0
- package/src/types/index.d.ts +13 -0
- package/src/types/index.js +26 -0
- package/src/types/index.js.map +1 -0
- package/src/types/ui-config.d.ts +357 -0
- package/src/types/ui-config.js +12 -0
- package/src/types/ui-config.js.map +1 -0
- package/src/types/ui-runtime.d.ts +965 -0
- package/src/types/ui-runtime.js +117 -0
- package/src/types/ui-runtime.js.map +1 -0
- package/src/validation/error-box.d.ts +55 -0
- package/src/validation/error-box.js +75 -0
- package/src/validation/error-box.js.map +1 -0
- package/src/validation/index.d.ts +12 -0
- package/src/validation/index.js +21 -0
- package/src/validation/index.js.map +1 -0
- package/src/validation/wrapper.d.ts +96 -0
- package/src/validation/wrapper.js +117 -0
- package/src/validation/wrapper.js.map +1 -0
- package/src/web-components/core/attribute-parser.d.ts +85 -0
- package/src/web-components/core/attribute-parser.js +189 -0
- package/src/web-components/core/attribute-parser.js.map +1 -0
- package/src/web-components/core/base-element.d.ts +197 -0
- package/src/web-components/core/base-element.js +289 -0
- package/src/web-components/core/base-element.js.map +1 -0
- package/src/web-components/core/index.d.ts +8 -0
- package/src/web-components/core/index.js +18 -0
- package/src/web-components/core/index.js.map +1 -0
- package/src/web-components/elements/fmcp-alert.d.ts +45 -0
- package/src/web-components/elements/fmcp-alert.js +93 -0
- package/src/web-components/elements/fmcp-alert.js.map +1 -0
- package/src/web-components/elements/fmcp-badge.d.ts +46 -0
- package/src/web-components/elements/fmcp-badge.js +99 -0
- package/src/web-components/elements/fmcp-badge.js.map +1 -0
- package/src/web-components/elements/fmcp-button.d.ts +124 -0
- package/src/web-components/elements/fmcp-button.js +233 -0
- package/src/web-components/elements/fmcp-button.js.map +1 -0
- package/src/web-components/elements/fmcp-card.d.ts +52 -0
- package/src/web-components/elements/fmcp-card.js +115 -0
- package/src/web-components/elements/fmcp-card.js.map +1 -0
- package/src/web-components/elements/fmcp-input.d.ts +95 -0
- package/src/web-components/elements/fmcp-input.js +248 -0
- package/src/web-components/elements/fmcp-input.js.map +1 -0
- package/src/web-components/elements/fmcp-select.d.ts +99 -0
- package/src/web-components/elements/fmcp-select.js +243 -0
- package/src/web-components/elements/fmcp-select.js.map +1 -0
- package/src/web-components/elements/index.d.ts +12 -0
- package/src/web-components/elements/index.js +34 -0
- package/src/web-components/elements/index.js.map +1 -0
- package/src/web-components/index.d.ts +49 -0
- package/src/web-components/index.js +75 -0
- package/src/web-components/index.js.map +1 -0
- package/src/web-components/register.d.ts +56 -0
- package/src/web-components/register.js +80 -0
- package/src/web-components/register.js.map +1 -0
- package/src/web-components/types.d.ts +121 -0
- package/src/web-components/types.js +25 -0
- package/src/web-components/types.js.map +1 -0
- package/src/widgets/index.d.ts +7 -0
- package/src/widgets/index.js +24 -0
- package/src/widgets/index.js.map +1 -0
- package/src/widgets/progress.d.ts +132 -0
- package/src/widgets/progress.js +303 -0
- package/src/widgets/progress.js.map +1 -0
- package/src/widgets/resource.d.ts +162 -0
- package/src/widgets/resource.js +340 -0
- package/src/widgets/resource.js.map +1 -0
|
@@ -0,0 +1,652 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Widget Manifest Builder
|
|
4
|
+
*
|
|
5
|
+
* Builds static widget wrappers with embedded manifests for tool UIs.
|
|
6
|
+
* Produces complete HTML documents with FrontMCP Bridge runtime.
|
|
7
|
+
*
|
|
8
|
+
* @packageDocumentation
|
|
9
|
+
*/
|
|
10
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
11
|
+
exports.detectUIType = detectUIType;
|
|
12
|
+
exports.buildCSPForType = buildCSPForType;
|
|
13
|
+
exports.buildCSPMetaContent = buildCSPMetaContent;
|
|
14
|
+
exports.getRendererAssets = getRendererAssets;
|
|
15
|
+
exports.buildToolWidgetManifest = buildToolWidgetManifest;
|
|
16
|
+
exports.batchBuildWidgets = batchBuildWidgets;
|
|
17
|
+
exports.buildToolResponseMeta = buildToolResponseMeta;
|
|
18
|
+
exports.getOutputModeForClient = getOutputModeForClient;
|
|
19
|
+
const ui_runtime_1 = require("../types/ui-runtime");
|
|
20
|
+
const cdn_resources_1 = require("./cdn-resources");
|
|
21
|
+
const wrapper_1 = require("../runtime/wrapper");
|
|
22
|
+
const registry_1 = require("../renderers/registry");
|
|
23
|
+
const renderers_1 = require("../renderers");
|
|
24
|
+
// ============================================
|
|
25
|
+
// UI Type Detection
|
|
26
|
+
// ============================================
|
|
27
|
+
/**
|
|
28
|
+
* Detect the UI type from a template.
|
|
29
|
+
*
|
|
30
|
+
* @param template - Widget template
|
|
31
|
+
* @returns Detected UI type
|
|
32
|
+
*/
|
|
33
|
+
function detectUIType(template) {
|
|
34
|
+
const detection = (0, renderers_1.detectTemplateType)(template);
|
|
35
|
+
switch (detection.type) {
|
|
36
|
+
case 'react':
|
|
37
|
+
return 'react';
|
|
38
|
+
case 'mdx':
|
|
39
|
+
return 'mdx';
|
|
40
|
+
case 'jsx-string':
|
|
41
|
+
return 'react'; // JSX strings are rendered as React
|
|
42
|
+
case 'html-string':
|
|
43
|
+
case 'html-function':
|
|
44
|
+
return 'html';
|
|
45
|
+
default:
|
|
46
|
+
return 'auto';
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
// ============================================
|
|
50
|
+
// CSP Building
|
|
51
|
+
// ============================================
|
|
52
|
+
/**
|
|
53
|
+
* Build CSP directives for a UI type.
|
|
54
|
+
*
|
|
55
|
+
* @param uiType - UI renderer type
|
|
56
|
+
* @param userCsp - User-provided CSP overrides
|
|
57
|
+
* @returns Complete CSP directives
|
|
58
|
+
*/
|
|
59
|
+
function buildCSPForType(uiType, userCsp) {
|
|
60
|
+
const baseCsp = ui_runtime_1.DEFAULT_CSP_BY_TYPE[uiType] ?? ui_runtime_1.DEFAULT_CSP_BY_TYPE['auto'];
|
|
61
|
+
if (!userCsp) {
|
|
62
|
+
return { ...baseCsp };
|
|
63
|
+
}
|
|
64
|
+
// Merge user CSP with base CSP
|
|
65
|
+
return {
|
|
66
|
+
scriptSrc: mergeCspArray(baseCsp.scriptSrc, userCsp.scriptSrc),
|
|
67
|
+
styleSrc: mergeCspArray(baseCsp.styleSrc, userCsp.styleSrc),
|
|
68
|
+
connectSrc: mergeCspArray(baseCsp.connectSrc, userCsp.connectSrc),
|
|
69
|
+
imgSrc: userCsp.imgSrc ?? baseCsp.imgSrc,
|
|
70
|
+
fontSrc: userCsp.fontSrc ?? baseCsp.fontSrc,
|
|
71
|
+
defaultSrc: userCsp.defaultSrc ?? baseCsp.defaultSrc,
|
|
72
|
+
frameSrc: userCsp.frameSrc ?? baseCsp.frameSrc,
|
|
73
|
+
objectSrc: userCsp.objectSrc ?? baseCsp.objectSrc,
|
|
74
|
+
};
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* Merge CSP arrays, preserving uniqueness.
|
|
78
|
+
*/
|
|
79
|
+
function mergeCspArray(base, override) {
|
|
80
|
+
if (!override)
|
|
81
|
+
return [...base];
|
|
82
|
+
const merged = new Set([...base, ...override]);
|
|
83
|
+
return Array.from(merged);
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* Build CSP meta tag content.
|
|
87
|
+
*/
|
|
88
|
+
function buildCSPMetaContent(csp) {
|
|
89
|
+
const directives = [];
|
|
90
|
+
if (csp.defaultSrc?.length) {
|
|
91
|
+
directives.push(`default-src ${csp.defaultSrc.join(' ')}`);
|
|
92
|
+
}
|
|
93
|
+
if (csp.scriptSrc.length) {
|
|
94
|
+
directives.push(`script-src ${csp.scriptSrc.join(' ')}`);
|
|
95
|
+
}
|
|
96
|
+
if (csp.styleSrc.length) {
|
|
97
|
+
directives.push(`style-src ${csp.styleSrc.join(' ')}`);
|
|
98
|
+
}
|
|
99
|
+
if (csp.connectSrc.length) {
|
|
100
|
+
directives.push(`connect-src ${csp.connectSrc.join(' ')}`);
|
|
101
|
+
}
|
|
102
|
+
if (csp.imgSrc?.length) {
|
|
103
|
+
directives.push(`img-src ${csp.imgSrc.join(' ')}`);
|
|
104
|
+
}
|
|
105
|
+
if (csp.fontSrc?.length) {
|
|
106
|
+
directives.push(`font-src ${csp.fontSrc.join(' ')}`);
|
|
107
|
+
}
|
|
108
|
+
if (csp.frameSrc?.length) {
|
|
109
|
+
directives.push(`frame-src ${csp.frameSrc.join(' ')}`);
|
|
110
|
+
}
|
|
111
|
+
if (csp.objectSrc?.length) {
|
|
112
|
+
directives.push(`object-src ${csp.objectSrc.join(' ')}`);
|
|
113
|
+
}
|
|
114
|
+
return directives.join('; ');
|
|
115
|
+
}
|
|
116
|
+
// ============================================
|
|
117
|
+
// Renderer Assets
|
|
118
|
+
// ============================================
|
|
119
|
+
/**
|
|
120
|
+
* Get renderer assets for a UI type.
|
|
121
|
+
*
|
|
122
|
+
* @param uiType - UI renderer type
|
|
123
|
+
* @param resourceMode - Resource loading mode (cdn or inline)
|
|
124
|
+
* @returns Required renderer assets with CDN URLs or inline placeholders
|
|
125
|
+
*
|
|
126
|
+
* @example
|
|
127
|
+
* ```typescript
|
|
128
|
+
* // Get CDN-based assets for React
|
|
129
|
+
* const assets = getRendererAssets('react', 'cdn');
|
|
130
|
+
* console.log(assets.react?.url);
|
|
131
|
+
* // "https://unpkg.com/react@18/umd/react.production.min.js"
|
|
132
|
+
*
|
|
133
|
+
* // Get inline-mode assets
|
|
134
|
+
* const inlineAssets = getRendererAssets('react', 'inline');
|
|
135
|
+
* // inlineAssets.mode === 'inline'
|
|
136
|
+
* ```
|
|
137
|
+
*/
|
|
138
|
+
function getRendererAssets(uiType, resourceMode = 'cdn') {
|
|
139
|
+
// Use the CDN resources helper which provides proper CDN URLs
|
|
140
|
+
return (0, cdn_resources_1.getDefaultAssets)(uiType, resourceMode);
|
|
141
|
+
}
|
|
142
|
+
// ============================================
|
|
143
|
+
// Hash Generation
|
|
144
|
+
// ============================================
|
|
145
|
+
/**
|
|
146
|
+
* Generate a hash for content.
|
|
147
|
+
*/
|
|
148
|
+
async function generateHash(content) {
|
|
149
|
+
// Use Web Crypto API if available
|
|
150
|
+
if (typeof crypto !== 'undefined' && crypto.subtle) {
|
|
151
|
+
const encoder = new TextEncoder();
|
|
152
|
+
const data = encoder.encode(content);
|
|
153
|
+
const hashBuffer = await crypto.subtle.digest('SHA-256', data);
|
|
154
|
+
const hashArray = Array.from(new Uint8Array(hashBuffer));
|
|
155
|
+
return hashArray.map((b) => b.toString(16).padStart(2, '0')).join('');
|
|
156
|
+
}
|
|
157
|
+
// Fallback: simple hash
|
|
158
|
+
let hash = 0;
|
|
159
|
+
for (let i = 0; i < content.length; i++) {
|
|
160
|
+
const char = content.charCodeAt(i);
|
|
161
|
+
hash = (hash << 5) - hash + char;
|
|
162
|
+
hash = hash & hash;
|
|
163
|
+
}
|
|
164
|
+
return Math.abs(hash).toString(16).padStart(8, '0');
|
|
165
|
+
}
|
|
166
|
+
// ============================================
|
|
167
|
+
// Main Builder
|
|
168
|
+
// ============================================
|
|
169
|
+
/**
|
|
170
|
+
* Build a widget manifest for a tool.
|
|
171
|
+
*
|
|
172
|
+
* Creates a static widget wrapper with embedded manifest.
|
|
173
|
+
* The widget can be cached and reused across tool invocations.
|
|
174
|
+
*
|
|
175
|
+
* @param options - Build options
|
|
176
|
+
* @returns Build result with HTML, manifest, and metadata
|
|
177
|
+
*
|
|
178
|
+
* @example
|
|
179
|
+
* ```typescript
|
|
180
|
+
* import { buildToolWidgetManifest } from '@frontmcp/ui/build';
|
|
181
|
+
*
|
|
182
|
+
* const result = await buildToolWidgetManifest({
|
|
183
|
+
* toolName: 'weather.get',
|
|
184
|
+
* uiConfig: {
|
|
185
|
+
* template: WeatherWidget,
|
|
186
|
+
* uiType: 'react',
|
|
187
|
+
* displayMode: 'inline',
|
|
188
|
+
* widgetAccessible: true,
|
|
189
|
+
* },
|
|
190
|
+
* schema: { type: 'object', properties: { ... } },
|
|
191
|
+
* });
|
|
192
|
+
*
|
|
193
|
+
* // Cache the HTML
|
|
194
|
+
* cache.set(`ui://widget/${result.manifest.tool}.html`, result.html);
|
|
195
|
+
* ```
|
|
196
|
+
*/
|
|
197
|
+
async function buildToolWidgetManifest(options) {
|
|
198
|
+
const { toolName, uiConfig, schema, theme, sampleInput, sampleOutput } = options;
|
|
199
|
+
// Resolve UI type
|
|
200
|
+
// Use type assertion to handle complex generic template types
|
|
201
|
+
const uiType = (0, ui_runtime_1.isUIType)(uiConfig.uiType)
|
|
202
|
+
? uiConfig.uiType
|
|
203
|
+
: detectUIType(uiConfig.template);
|
|
204
|
+
// Resolve display mode
|
|
205
|
+
const displayMode = uiConfig.displayMode ?? 'inline';
|
|
206
|
+
// Resolve bundling mode
|
|
207
|
+
const bundlingMode = uiConfig.bundlingMode ?? 'static';
|
|
208
|
+
// Resolve resource mode (cdn or inline)
|
|
209
|
+
const resourceMode = (0, ui_runtime_1.isResourceMode)(uiConfig.resourceMode)
|
|
210
|
+
? uiConfig.resourceMode
|
|
211
|
+
: 'cdn';
|
|
212
|
+
// Build CSP
|
|
213
|
+
const csp = buildCSPForType(uiType, uiConfig.csp);
|
|
214
|
+
// Get renderer assets based on resource mode
|
|
215
|
+
const rendererAssets = getRendererAssets(uiType, resourceMode);
|
|
216
|
+
// Render the template to get initial content
|
|
217
|
+
// Cast to generic type to avoid complex type inference issues
|
|
218
|
+
const templateConfig = uiConfig;
|
|
219
|
+
const content = await renderTemplate(templateConfig, {
|
|
220
|
+
input: (sampleInput ?? {}),
|
|
221
|
+
output: (sampleOutput ?? {}),
|
|
222
|
+
uiType,
|
|
223
|
+
});
|
|
224
|
+
// Generate component code for client-side rendering (React/MDX only)
|
|
225
|
+
const componentCode = (uiType === 'react' || uiType === 'mdx')
|
|
226
|
+
? buildComponentCode(uiConfig.template, toolName)
|
|
227
|
+
: undefined;
|
|
228
|
+
// Build manifest
|
|
229
|
+
// Note: We intentionally omit 'uri' since we're not using resource-based serving.
|
|
230
|
+
// The content/html is returned directly in the tool response.
|
|
231
|
+
const manifestBase = {
|
|
232
|
+
tool: toolName,
|
|
233
|
+
uiType,
|
|
234
|
+
bundlingMode,
|
|
235
|
+
displayMode,
|
|
236
|
+
widgetAccessible: uiConfig.widgetAccessible ?? false,
|
|
237
|
+
schema: schema ?? {},
|
|
238
|
+
csp,
|
|
239
|
+
rendererAssets,
|
|
240
|
+
createdAt: new Date().toISOString(),
|
|
241
|
+
description: uiConfig.widgetDescription,
|
|
242
|
+
};
|
|
243
|
+
// Convert CSPDirectives to UIContentSecurityPolicy for wrapper
|
|
244
|
+
const wrapperCsp = uiConfig.csp ? {
|
|
245
|
+
connectDomains: uiConfig.csp.connectSrc,
|
|
246
|
+
resourceDomains: [
|
|
247
|
+
...(uiConfig.csp.scriptSrc ?? []),
|
|
248
|
+
...(uiConfig.csp.styleSrc ?? []),
|
|
249
|
+
...(uiConfig.csp.imgSrc ?? []),
|
|
250
|
+
].filter((d) => d !== "'self'" && d !== "'unsafe-inline'"),
|
|
251
|
+
} : undefined;
|
|
252
|
+
// Build manifest script to embed in the page
|
|
253
|
+
const manifestScript = buildManifestScript(manifestBase);
|
|
254
|
+
// Prepend manifest script to content so it's available before the UI renders
|
|
255
|
+
const contentWithManifest = manifestScript + '\n' + content;
|
|
256
|
+
// Build the complete HTML with manifest embedded
|
|
257
|
+
// Use inlineScripts when resourceMode is 'inline' (for blocked-network environments)
|
|
258
|
+
const html = (0, wrapper_1.wrapToolUIUniversal)({
|
|
259
|
+
content: contentWithManifest,
|
|
260
|
+
toolName,
|
|
261
|
+
input: (sampleInput ?? {}),
|
|
262
|
+
output: sampleOutput,
|
|
263
|
+
csp: wrapperCsp,
|
|
264
|
+
widgetAccessible: uiConfig.widgetAccessible,
|
|
265
|
+
includeBridge: true,
|
|
266
|
+
rendererType: uiType,
|
|
267
|
+
inlineScripts: resourceMode === 'inline',
|
|
268
|
+
resourceMode, // Pass resource mode for CDN script generation
|
|
269
|
+
});
|
|
270
|
+
// Generate hash based on tool name + content (for cache invalidation)
|
|
271
|
+
// Including toolName ensures different tools have different hashes even if content is similar
|
|
272
|
+
const hash = await generateHash(`${toolName}:${content}`);
|
|
273
|
+
// Complete manifest with hash
|
|
274
|
+
const manifest = {
|
|
275
|
+
...manifestBase,
|
|
276
|
+
hash: `sha256-${hash}`,
|
|
277
|
+
};
|
|
278
|
+
// Calculate sizes
|
|
279
|
+
const contentSize = new TextEncoder().encode(content).length;
|
|
280
|
+
const htmlSize = new TextEncoder().encode(html).length;
|
|
281
|
+
const gzipSize = Math.round(htmlSize * 0.25); // Rough estimate
|
|
282
|
+
return {
|
|
283
|
+
// Transpiled content only (for capable clients like OpenAI)
|
|
284
|
+
content,
|
|
285
|
+
// Full HTML document (for limited/unknown MCP clients)
|
|
286
|
+
html,
|
|
287
|
+
manifest,
|
|
288
|
+
hash,
|
|
289
|
+
rendererType: uiType,
|
|
290
|
+
// Component code for client-side rendering (React/MDX only)
|
|
291
|
+
componentCode,
|
|
292
|
+
contentSize,
|
|
293
|
+
htmlSize,
|
|
294
|
+
gzipSize,
|
|
295
|
+
// Deprecated, kept for backwards compatibility
|
|
296
|
+
size: htmlSize,
|
|
297
|
+
};
|
|
298
|
+
}
|
|
299
|
+
/**
|
|
300
|
+
* Build the manifest script tag for embedding in HTML.
|
|
301
|
+
*/
|
|
302
|
+
function buildManifestScript(manifest) {
|
|
303
|
+
// Escape for embedding in script tag
|
|
304
|
+
const json = JSON.stringify(manifest)
|
|
305
|
+
.replace(/</g, '\\u003c')
|
|
306
|
+
.replace(/>/g, '\\u003e')
|
|
307
|
+
.replace(/&/g, '\\u0026');
|
|
308
|
+
return `
|
|
309
|
+
<script type="application/json" id="frontmcp-widget-manifest">
|
|
310
|
+
${json}
|
|
311
|
+
</script>
|
|
312
|
+
<script>
|
|
313
|
+
(function() {
|
|
314
|
+
try {
|
|
315
|
+
var manifest = JSON.parse(document.getElementById('frontmcp-widget-manifest').textContent);
|
|
316
|
+
window.__frontmcp = window.__frontmcp || {};
|
|
317
|
+
window.__frontmcp.widget = window.__frontmcp.widget || {};
|
|
318
|
+
window.__frontmcp.widget.manifest = manifest;
|
|
319
|
+
} catch (e) {
|
|
320
|
+
console.error('[FrontMCP] Failed to parse widget manifest:', e);
|
|
321
|
+
}
|
|
322
|
+
})();
|
|
323
|
+
</script>
|
|
324
|
+
`.trim();
|
|
325
|
+
}
|
|
326
|
+
// ============================================
|
|
327
|
+
// Component Code Builder (for Client-Side Rendering)
|
|
328
|
+
// ============================================
|
|
329
|
+
/**
|
|
330
|
+
* Build client-side component code for React templates.
|
|
331
|
+
*
|
|
332
|
+
* This converts the server-side React component into a string that can be
|
|
333
|
+
* embedded in the widget HTML for client-side rendering when tool output
|
|
334
|
+
* becomes available.
|
|
335
|
+
*
|
|
336
|
+
* @param template - The React component or template
|
|
337
|
+
* @param toolName - Tool name for naming the component
|
|
338
|
+
* @returns JavaScript code string that defines the component
|
|
339
|
+
*/
|
|
340
|
+
function buildComponentCode(template, toolName) {
|
|
341
|
+
if (typeof template !== 'function') {
|
|
342
|
+
return undefined;
|
|
343
|
+
}
|
|
344
|
+
// Get the component function as a string
|
|
345
|
+
const componentSource = template.toString();
|
|
346
|
+
// Sanitize tool name for JavaScript variable name
|
|
347
|
+
const safeName = toolName.replace(/[^a-zA-Z0-9_]/g, '_');
|
|
348
|
+
// Build the component code that will be embedded in the HTML
|
|
349
|
+
// The component is registered on window.__frontmcp_component for the render script
|
|
350
|
+
return `
|
|
351
|
+
// FrontMCP Widget Component: ${toolName}
|
|
352
|
+
(function() {
|
|
353
|
+
// The component function
|
|
354
|
+
var ${safeName} = ${componentSource};
|
|
355
|
+
|
|
356
|
+
// Register component globally for client-side rendering
|
|
357
|
+
window.__frontmcp_component = ${safeName};
|
|
358
|
+
|
|
359
|
+
// Also register in __frontmcp_components for multiple components
|
|
360
|
+
window.__frontmcp_components = window.__frontmcp_components || {};
|
|
361
|
+
window.__frontmcp_components['${toolName}'] = ${safeName};
|
|
362
|
+
})();
|
|
363
|
+
`.trim();
|
|
364
|
+
}
|
|
365
|
+
/**
|
|
366
|
+
* Ensure renderers are registered before use.
|
|
367
|
+
* This is called once and ensures React/MDX renderers are available.
|
|
368
|
+
*/
|
|
369
|
+
let renderersInitialized = false;
|
|
370
|
+
function ensureRenderersRegistered() {
|
|
371
|
+
if (renderersInitialized) {
|
|
372
|
+
return;
|
|
373
|
+
}
|
|
374
|
+
// Register React renderer if not already registered
|
|
375
|
+
if (!registry_1.rendererRegistry.has('react')) {
|
|
376
|
+
registry_1.rendererRegistry.register(renderers_1.reactRenderer);
|
|
377
|
+
}
|
|
378
|
+
// Register MDX renderer if not already registered
|
|
379
|
+
if (!registry_1.rendererRegistry.has('mdx')) {
|
|
380
|
+
registry_1.rendererRegistry.register(renderers_1.mdxRenderer);
|
|
381
|
+
}
|
|
382
|
+
renderersInitialized = true;
|
|
383
|
+
}
|
|
384
|
+
/**
|
|
385
|
+
* Render template content.
|
|
386
|
+
*/
|
|
387
|
+
async function renderTemplate(uiConfig, options) {
|
|
388
|
+
const { input, output, uiType } = options;
|
|
389
|
+
const template = uiConfig.template;
|
|
390
|
+
// Ensure renderers are available
|
|
391
|
+
ensureRenderersRegistered();
|
|
392
|
+
// Build context for template
|
|
393
|
+
const context = {
|
|
394
|
+
input: input,
|
|
395
|
+
output,
|
|
396
|
+
structuredContent: undefined,
|
|
397
|
+
helpers: {
|
|
398
|
+
escapeHtml: (str) => str.replace(/[&<>"']/g, (c) => ({
|
|
399
|
+
'&': '&',
|
|
400
|
+
'<': '<',
|
|
401
|
+
'>': '>',
|
|
402
|
+
'"': '"',
|
|
403
|
+
"'": ''',
|
|
404
|
+
})[c] ?? c),
|
|
405
|
+
formatDate: (date) => new Date(date).toLocaleDateString(),
|
|
406
|
+
formatCurrency: (amount, currency = 'USD') => new Intl.NumberFormat('en-US', { style: 'currency', currency }).format(amount),
|
|
407
|
+
uniqueId: (prefix = 'id') => `${prefix}-${Math.random().toString(36).slice(2, 9)}`,
|
|
408
|
+
jsonEmbed: (data) => JSON.stringify(data).replace(/</g, '\\u003c').replace(/>/g, '\\u003e'),
|
|
409
|
+
},
|
|
410
|
+
};
|
|
411
|
+
// Try to render using the registry
|
|
412
|
+
try {
|
|
413
|
+
// Use the detected uiType to render with the correct renderer
|
|
414
|
+
if (uiType === 'react' && registry_1.rendererRegistry.has('react')) {
|
|
415
|
+
const result = await registry_1.rendererRegistry.renderWith('react', template, context, {
|
|
416
|
+
mdxComponents: uiConfig.mdxComponents,
|
|
417
|
+
});
|
|
418
|
+
return result.html;
|
|
419
|
+
}
|
|
420
|
+
if (uiType === 'mdx' && registry_1.rendererRegistry.has('mdx')) {
|
|
421
|
+
const result = await registry_1.rendererRegistry.renderWith('mdx', template, context, {
|
|
422
|
+
mdxComponents: uiConfig.mdxComponents,
|
|
423
|
+
});
|
|
424
|
+
return result.html;
|
|
425
|
+
}
|
|
426
|
+
// Auto-detect and render
|
|
427
|
+
const result = await registry_1.rendererRegistry.render(template, context, {
|
|
428
|
+
mdxComponents: uiConfig.mdxComponents,
|
|
429
|
+
});
|
|
430
|
+
return result.html;
|
|
431
|
+
}
|
|
432
|
+
catch (error) {
|
|
433
|
+
// Fallback: simple rendering
|
|
434
|
+
if (typeof template === 'string') {
|
|
435
|
+
return template;
|
|
436
|
+
}
|
|
437
|
+
if (typeof template === 'function') {
|
|
438
|
+
// Check if it's a React component (has prototype or is class)
|
|
439
|
+
const isReact = template.prototype?.isReactComponent ||
|
|
440
|
+
template.$$typeof !== undefined;
|
|
441
|
+
if (!isReact) {
|
|
442
|
+
// It's a template builder function
|
|
443
|
+
return template(context);
|
|
444
|
+
}
|
|
445
|
+
}
|
|
446
|
+
// Log the error for debugging
|
|
447
|
+
console.warn('[FrontMCP] Template rendering failed:', error);
|
|
448
|
+
return `<div class="error">Template rendering failed</div>`;
|
|
449
|
+
}
|
|
450
|
+
}
|
|
451
|
+
/**
|
|
452
|
+
* Build multiple widget manifests in batch.
|
|
453
|
+
*
|
|
454
|
+
* @param options - Batch build options
|
|
455
|
+
* @returns Batch build results
|
|
456
|
+
*
|
|
457
|
+
* @example
|
|
458
|
+
* ```typescript
|
|
459
|
+
* const { results, errors } = await batchBuildWidgets({
|
|
460
|
+
* tools: [
|
|
461
|
+
* { toolName: 'weather.get', uiConfig: weatherConfig },
|
|
462
|
+
* { toolName: 'stock.quote', uiConfig: stockConfig },
|
|
463
|
+
* ],
|
|
464
|
+
* });
|
|
465
|
+
*
|
|
466
|
+
* for (const [name, result] of results) {
|
|
467
|
+
* cache.set(result.manifest.uri, result.html);
|
|
468
|
+
* }
|
|
469
|
+
* ```
|
|
470
|
+
*/
|
|
471
|
+
async function batchBuildWidgets(options) {
|
|
472
|
+
const startTime = performance.now();
|
|
473
|
+
const results = new Map();
|
|
474
|
+
const errors = new Map();
|
|
475
|
+
const { tools, theme, parallel = true } = options;
|
|
476
|
+
if (parallel) {
|
|
477
|
+
// Build in parallel
|
|
478
|
+
const promises = tools.map(async (tool) => {
|
|
479
|
+
try {
|
|
480
|
+
const result = await buildToolWidgetManifest({
|
|
481
|
+
toolName: tool.toolName,
|
|
482
|
+
uiConfig: tool.uiConfig,
|
|
483
|
+
schema: tool.schema,
|
|
484
|
+
theme,
|
|
485
|
+
});
|
|
486
|
+
return { toolName: tool.toolName, result, error: null };
|
|
487
|
+
}
|
|
488
|
+
catch (error) {
|
|
489
|
+
return {
|
|
490
|
+
toolName: tool.toolName,
|
|
491
|
+
result: null,
|
|
492
|
+
error: error instanceof Error ? error : new Error(String(error)),
|
|
493
|
+
};
|
|
494
|
+
}
|
|
495
|
+
});
|
|
496
|
+
const outcomes = await Promise.all(promises);
|
|
497
|
+
for (const outcome of outcomes) {
|
|
498
|
+
if (outcome.result) {
|
|
499
|
+
results.set(outcome.toolName, outcome.result);
|
|
500
|
+
}
|
|
501
|
+
else if (outcome.error) {
|
|
502
|
+
errors.set(outcome.toolName, outcome.error);
|
|
503
|
+
}
|
|
504
|
+
}
|
|
505
|
+
}
|
|
506
|
+
else {
|
|
507
|
+
// Build sequentially
|
|
508
|
+
for (const tool of tools) {
|
|
509
|
+
try {
|
|
510
|
+
const result = await buildToolWidgetManifest({
|
|
511
|
+
toolName: tool.toolName,
|
|
512
|
+
uiConfig: tool.uiConfig,
|
|
513
|
+
schema: tool.schema,
|
|
514
|
+
theme,
|
|
515
|
+
});
|
|
516
|
+
results.set(tool.toolName, result);
|
|
517
|
+
}
|
|
518
|
+
catch (error) {
|
|
519
|
+
errors.set(tool.toolName, error instanceof Error ? error : new Error(String(error)));
|
|
520
|
+
}
|
|
521
|
+
}
|
|
522
|
+
}
|
|
523
|
+
return {
|
|
524
|
+
results,
|
|
525
|
+
totalTime: performance.now() - startTime,
|
|
526
|
+
successCount: results.size,
|
|
527
|
+
errors,
|
|
528
|
+
};
|
|
529
|
+
}
|
|
530
|
+
/**
|
|
531
|
+
* Build _meta fields for a tool response.
|
|
532
|
+
*
|
|
533
|
+
* This function generates the proper _meta structure for UI widgets.
|
|
534
|
+
* All UI-related data goes in _meta, NOT in the content array.
|
|
535
|
+
*
|
|
536
|
+
* @param options - Build options
|
|
537
|
+
* @returns _meta fields object to spread into tool response
|
|
538
|
+
*
|
|
539
|
+
* @example
|
|
540
|
+
* ```typescript
|
|
541
|
+
* const buildResult = await buildToolWidgetManifest({...});
|
|
542
|
+
*
|
|
543
|
+
* // For OpenAI (code-only mode)
|
|
544
|
+
* const meta = buildToolResponseMeta({
|
|
545
|
+
* buildResult,
|
|
546
|
+
* outputMode: 'code-only',
|
|
547
|
+
* });
|
|
548
|
+
*
|
|
549
|
+
* return {
|
|
550
|
+
* content: [{ type: 'text', text: 'Weather retrieved' }],
|
|
551
|
+
* _meta: meta,
|
|
552
|
+
* };
|
|
553
|
+
*
|
|
554
|
+
* // For unknown clients (full-ssr mode)
|
|
555
|
+
* const meta = buildToolResponseMeta({
|
|
556
|
+
* buildResult,
|
|
557
|
+
* outputMode: 'full-ssr',
|
|
558
|
+
* });
|
|
559
|
+
* ```
|
|
560
|
+
*/
|
|
561
|
+
function buildToolResponseMeta(options) {
|
|
562
|
+
const { buildResult, outputMode = 'code-only', includeOpenAI = true } = options;
|
|
563
|
+
const { manifest, content, html, hash, rendererType } = buildResult;
|
|
564
|
+
// Base UI meta fields
|
|
565
|
+
const meta = {
|
|
566
|
+
'ui/type': rendererType,
|
|
567
|
+
'ui/hash': hash,
|
|
568
|
+
};
|
|
569
|
+
// Add content based on output mode
|
|
570
|
+
if (outputMode === 'code-only') {
|
|
571
|
+
meta['ui/content'] = content;
|
|
572
|
+
}
|
|
573
|
+
else {
|
|
574
|
+
meta['ui/html'] = html;
|
|
575
|
+
}
|
|
576
|
+
// Add optional fields from manifest
|
|
577
|
+
if (manifest.displayMode && manifest.displayMode !== 'inline') {
|
|
578
|
+
meta['ui/displayMode'] = manifest.displayMode;
|
|
579
|
+
}
|
|
580
|
+
if (manifest.widgetAccessible) {
|
|
581
|
+
meta['ui/widgetAccessible'] = true;
|
|
582
|
+
}
|
|
583
|
+
if (manifest.description) {
|
|
584
|
+
meta['ui/description'] = manifest.description;
|
|
585
|
+
}
|
|
586
|
+
// Add resource mode if not default
|
|
587
|
+
if (manifest.rendererAssets?.mode && manifest.rendererAssets.mode !== 'cdn') {
|
|
588
|
+
meta['ui/resourceMode'] = manifest.rendererAssets.mode;
|
|
589
|
+
}
|
|
590
|
+
// Add OpenAI-specific fields
|
|
591
|
+
if (includeOpenAI) {
|
|
592
|
+
if (manifest.widgetAccessible) {
|
|
593
|
+
meta['openai/widgetAccessible'] = true;
|
|
594
|
+
}
|
|
595
|
+
if (manifest.description) {
|
|
596
|
+
meta['openai/widgetDescription'] = manifest.description;
|
|
597
|
+
}
|
|
598
|
+
if (manifest.displayMode && manifest.displayMode !== 'inline') {
|
|
599
|
+
meta['openai/displayMode'] = manifest.displayMode;
|
|
600
|
+
}
|
|
601
|
+
// Build OpenAI CSP from manifest CSP
|
|
602
|
+
const csp = manifest.csp;
|
|
603
|
+
if (csp) {
|
|
604
|
+
const openaiCsp = {};
|
|
605
|
+
if (csp.connectSrc && csp.connectSrc.length > 0) {
|
|
606
|
+
openaiCsp.connect_domains = csp.connectSrc.filter((d) => d !== "'self'" && d !== "'none'");
|
|
607
|
+
}
|
|
608
|
+
const resourceDomains = [
|
|
609
|
+
...(csp.scriptSrc || []),
|
|
610
|
+
...(csp.styleSrc || []),
|
|
611
|
+
...(csp.imgSrc || []),
|
|
612
|
+
].filter((d) => d !== "'self'" && d !== "'unsafe-inline'" && d !== "'none'");
|
|
613
|
+
if (resourceDomains.length > 0) {
|
|
614
|
+
openaiCsp.resource_domains = [...new Set(resourceDomains)];
|
|
615
|
+
}
|
|
616
|
+
if (openaiCsp.connect_domains?.length || openaiCsp.resource_domains?.length) {
|
|
617
|
+
meta['openai/widgetCSP'] = openaiCsp;
|
|
618
|
+
}
|
|
619
|
+
}
|
|
620
|
+
}
|
|
621
|
+
return meta;
|
|
622
|
+
}
|
|
623
|
+
/**
|
|
624
|
+
* Determine the appropriate output mode based on client info.
|
|
625
|
+
*
|
|
626
|
+
* @param clientInfo - MCP client information
|
|
627
|
+
* @returns Recommended output mode
|
|
628
|
+
*
|
|
629
|
+
* @example
|
|
630
|
+
* ```typescript
|
|
631
|
+
* const outputMode = getOutputModeForClient(request.clientInfo);
|
|
632
|
+
* const meta = buildToolResponseMeta({ buildResult, outputMode });
|
|
633
|
+
* ```
|
|
634
|
+
*/
|
|
635
|
+
function getOutputModeForClient(clientInfo) {
|
|
636
|
+
if (!clientInfo?.name) {
|
|
637
|
+
// Unknown client - use full SSR for safety
|
|
638
|
+
return 'full-ssr';
|
|
639
|
+
}
|
|
640
|
+
const name = clientInfo.name.toLowerCase();
|
|
641
|
+
// Capable clients that provide their own runtime
|
|
642
|
+
if (name.includes('openai') ||
|
|
643
|
+
name.includes('chatgpt') ||
|
|
644
|
+
name.includes('cursor') ||
|
|
645
|
+
name.includes('claude') // Claude Desktop might support code-only in future
|
|
646
|
+
) {
|
|
647
|
+
return 'code-only';
|
|
648
|
+
}
|
|
649
|
+
// Default to full SSR for unknown clients
|
|
650
|
+
return 'full-ssr';
|
|
651
|
+
}
|
|
652
|
+
//# sourceMappingURL=widget-manifest.js.map
|