@tylertech/forge-ai 0.15.1 → 0.15.3-dev.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/custom-elements.json +8152 -6309
- package/dist/ai-artifact-card/ai-artifact-card.d.ts +75 -0
- package/dist/ai-artifact-card/ai-artifact-card.mjs +142 -0
- package/dist/ai-artifact-card/ai-artifact-card.scss.mjs +283 -0
- package/dist/ai-artifact-card/index.d.ts +1 -0
- package/dist/ai-artifact-card/index.mjs +5 -0
- package/dist/ai-artifact-card-group/ai-artifact-card-group.d.ts +57 -0
- package/dist/ai-artifact-card-group/ai-artifact-card-group.mjs +137 -0
- package/dist/ai-artifact-card-group/ai-artifact-card-group.scss.mjs +173 -0
- package/dist/ai-artifact-card-group/index.d.ts +1 -0
- package/dist/ai-artifact-card-group/index.mjs +5 -0
- package/dist/ai-assistant-response/ai-assistant-response.mjs +25 -2
- package/dist/ai-chatbot/ag-ui-adapter.mjs +1 -1
- package/dist/ai-chatbot/agent-adapter.d.ts +108 -1
- package/dist/ai-chatbot/agent-adapter.mjs +22 -0
- package/dist/ai-chatbot/ai-chatbot-base.d.ts +26 -0
- package/dist/ai-chatbot/ai-chatbot-base.mjs +136 -3
- package/dist/ai-chatbot/index.d.ts +2 -2
- package/dist/ai-chatbot/message-state-controller.d.ts +10 -1
- package/dist/ai-chatbot/message-state-controller.mjs +78 -20
- package/dist/ai-chatbot/types.d.ts +199 -1
- package/dist/ai-chatbot/utils.mjs +3 -1
- package/dist/ai-chatbot-launcher/ai-chatbot-launcher.mjs +14 -4
- package/dist/ai-message-thread/ai-message-thread.d.ts +1 -0
- package/dist/ai-message-thread/ai-message-thread.mjs +35 -3
- package/dist/ai-sidebar/ai-sidebar.d.ts +17 -6
- package/dist/ai-sidebar/ai-sidebar.mjs +78 -36
- package/dist/ai-sidebar/ai-sidebar.scss.mjs +1 -1
- package/dist/ai-sidebar/index.d.ts +1 -0
- package/dist/ai-sidebar-chat/ai-sidebar-chat.d.ts +14 -1
- package/dist/ai-sidebar-chat/ai-sidebar-chat.mjs +18 -14
- package/dist/ai-steps/ai-steps.d.ts +67 -0
- package/dist/ai-steps/ai-steps.mjs +163 -0
- package/dist/ai-steps/ai-steps.scss.mjs +4 -0
- package/dist/ai-steps/index.d.ts +1 -0
- package/dist/ai-steps/index.mjs +5 -0
- package/dist/ai-tool-call-indicator/ai-tool-call-indicator.mjs +1 -1
- package/dist/core/chatbot-core-controller.d.ts +2 -0
- package/dist/core/chatbot-core-controller.mjs +99 -4
- package/dist/core/resize-controller.d.ts +1 -0
- package/dist/core/resize-controller.mjs +11 -2
- package/dist/index.d.ts +3 -0
- package/dist/index.mjs +9 -0
- package/dist/tools/ai-mcp-app/ai-mcp-app.d.ts +69 -0
- package/dist/tools/ai-mcp-app/ai-mcp-app.mjs +574 -0
- package/dist/tools/ai-mcp-app/ai-mcp-app.scss.mjs +4 -0
- package/dist/tools/ai-mcp-app/index.d.ts +3 -0
- package/dist/tools/ai-mcp-app/index.mjs +6 -0
- package/dist/tools/ai-mcp-app/mcp-app-bridge-controller.d.ts +61 -0
- package/dist/tools/ai-mcp-app/mcp-app-bridge-controller.mjs +156 -0
- package/dist/tools/ai-mcp-app/mcp-app-bridge-types.d.ts +123 -0
- package/dist/tools/ai-mcp-app/mcp-app-bridge.d.ts +41 -0
- package/dist/tools/ai-mcp-app/mcp-app-bridge.mjs +293 -0
- package/dist/tools/ai-mcp-app/mcp-app-context.d.ts +46 -0
- package/dist/tools/ai-mcp-app/mcp-app-context.mjs +5 -0
- package/dist/tools/ai-steps/ai-steps-definition.d.ts +2 -0
- package/dist/tools/ai-steps/ai-steps-definition.mjs +50 -0
- package/dist/tools/ai-steps/ai-steps.d.ts +28 -0
- package/dist/tools/ai-steps/ai-steps.mjs +49 -0
- package/dist/tools/ai-steps/ai-steps.scss.mjs +4 -0
- package/dist/tools/ai-steps/index.d.ts +2 -0
- package/dist/tools/ai-steps/index.mjs +6 -0
- package/dist/tools/index.d.ts +1 -0
- package/dist/tools/index.mjs +5 -1
- package/dist/utils/index.d.ts +1 -0
- package/dist/utils/index.mjs +5 -1
- package/dist/utils/inert-outside.d.ts +7 -0
- package/dist/utils/inert-outside.mjs +28 -0
- package/dist/utils/storage-utils.d.ts +26 -0
- package/dist/utils/storage-utils.mjs +32 -0
- package/package.json +4 -1
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
const styles = '/* prettier-ignore */\n/**\n * @license\n * Copyright Tyler Technologies, Inc.\n * License: Apache-2.0\n */\n.forge-icon-button {\n --_icon-button-display:var(--forge-icon-button-display, inline-flex);\n --_icon-button-size:var(--forge-icon-button-size, 48px);\n --_icon-button-gap:var(--forge-icon-button-gap, 0);\n --_icon-button-icon-color:var(--forge-icon-button-icon-color, currentColor);\n --_icon-button-background-color:var(--forge-icon-button-background-color, none);\n --_icon-button-icon-size:var(--forge-icon-button-icon-size, calc(var(--forge-typography-font-size, 1rem) * 1.5));\n --_icon-button-cursor:var(--forge-icon-button-cursor, pointer);\n --_icon-button-padding:var(--forge-icon-button-padding, var(--forge-spacing-xxsmall, 4px));\n --_icon-button-border:var(--forge-icon-button-border, none);\n --_icon-button-shadow:var(--forge-icon-button-shadow, none);\n --_icon-button-transition-duration:var(--forge-icon-button-transition-duration, var(--forge-animation-duration-short3, 150ms));\n --_icon-button-transition-timing:var(--forge-icon-button-transition-timing, var(--forge-animation-easing-standard, cubic-bezier(0.2, 0, 0, 1)));\n --_icon-button-shape:var(--forge-icon-button-shape, calc(var(--forge-shape-full, 9999px) * var(--forge-shape-factor, 1)));\n --_icon-button-shape-start-start:var(--forge-icon-button-shape-start-start, var(--_icon-button-shape));\n --_icon-button-shape-start-end:var(--forge-icon-button-shape-start-end, var(--_icon-button-shape));\n --_icon-button-shape-end-start:var(--forge-icon-button-shape-end-start, var(--_icon-button-shape));\n --_icon-button-shape-end-end:var(--forge-icon-button-shape-end-end, var(--_icon-button-shape));\n --_icon-button-shape-squared:var(--forge-icon-button-shape-squared, calc(var(--forge-shape-medium, 4px) * var(--forge-shape-factor, 1)));\n --_icon-button-outlined-border-width:var(--forge-icon-button-outlined-border-width, 1px);\n --_icon-button-outlined-border-style:var(--forge-icon-button-outlined-border-style, solid);\n --_icon-button-outlined-border-color:var(--forge-icon-button-outlined-border-color, var(--_icon-button-icon-color));\n --_icon-button-tonal-icon-color:var(--forge-icon-button-tonal-icon-color, var(--forge-theme-on-primary-container, #222c62));\n --_icon-button-tonal-background-color:var(--forge-icon-button-tonal-background-color, var(--forge-theme-primary-container, #d1d5ed));\n --_icon-button-filled-icon-color:var(--forge-icon-button-filled-icon-color, var(--forge-theme-on-primary, #ffffff));\n --_icon-button-filled-background-color:var(--forge-icon-button-filled-background-color, var(--forge-theme-primary, #3f51b5));\n --_icon-button-raised-shadow:var(--forge-icon-button-raised-shadow, 0px 3px 1px -2px rgba(0, 0, 0, 0.2), 0px 2px 2px 0px rgba(0, 0, 0, 0.14), 0px 1px 5px 0px rgba(0, 0, 0, 0.12));\n --_icon-button-raised-hover-shadow:var(--forge-icon-button-raised-hover-shadow, 0px 2px 4px -1px rgba(0, 0, 0, 0.2), 0px 4px 5px 0px rgba(0, 0, 0, 0.14), 0px 1px 10px 0px rgba(0, 0, 0, 0.12));\n --_icon-button-raised-active-shadow:var(--forge-icon-button-raised-active-shadow, 0px 5px 5px -3px rgba(0, 0, 0, 0.2), 0px 8px 10px 1px rgba(0, 0, 0, 0.14), 0px 3px 14px 2px rgba(0, 0, 0, 0.12));\n --_icon-button-raised-disabled-shadow:var(--forge-icon-button-raised-disabled-shadow, none);\n --_icon-button-density-small-size:var(--forge-icon-button-density-small-size, 24px);\n --_icon-button-density-small-padding:var(--forge-icon-button-density-small-padding, var(--forge-spacing-xxxsmall, 2px));\n --_icon-button-density-small-icon-size:var(--forge-icon-button-density-small-icon-size, calc(var(--forge-typography-font-size, 1rem) * 1.125));\n --_icon-button-density-medium-size:var(--forge-icon-button-density-medium-size, 36px);\n --_icon-button-density-medium-padding:var(--forge-icon-button-density-medium-padding, var(--forge-spacing-xxsmall, 4px));\n --_icon-button-density-large-size:var(--forge-icon-button-density-large-size, var(--_icon-button-size));\n --_icon-button-toggle-on-background-color:var(--forge-icon-button-toggle-on-background-color, var(--forge-theme-primary-container, #d1d5ed));\n --_icon-button-toggle-on-icon-color:var(--forge-icon-button-toggle-on-icon-color, var(--forge-theme-primary, #3f51b5));\n --_icon-button-outlined-toggle-on-background-color:var(--forge-icon-button-outlined-toggle-on-background-color, var(--forge-theme-primary-container, #d1d5ed));\n --_icon-button-outlined-toggle-on-icon-color:var(--forge-icon-button-outlined-toggle-on-icon-color, var(--forge-theme-primary, #3f51b5));\n --_icon-button-tonal-toggle-background-color:var(--forge-icon-button-tonal-toggle-background-color, var(--forge-theme-surface-container-low, #ebebeb));\n --_icon-button-tonal-toggle-on-background-color:var(--forge-icon-button-tonal-toggle-on-background-color, var(--forge-theme-primary-container, #d1d5ed));\n --_icon-button-tonal-toggle-on-icon-color:var(--forge-icon-button-tonal-toggle-on-icon-color, var(--forge-theme-primary, #3f51b5));\n --_icon-button-filled-toggle-background-color:var(--forge-icon-button-filled-toggle-background-color, var(--forge-theme-surface-container-low, #ebebeb));\n --_icon-button-filled-toggle-icon-color:var(--forge-icon-button-filled-toggle-icon-color, var(--forge-theme-primary, #3f51b5));\n --_icon-button-filled-toggle-on-background-color:var(--forge-icon-button-filled-toggle-on-background-color, var(--forge-theme-primary, #3f51b5));\n --_icon-button-filled-toggle-on-icon-color:var(--forge-icon-button-filled-toggle-on-icon-color, var(--forge-theme-on-primary, #ffffff));\n --_icon-button-disabled-cursor:var(--forge-icon-button-disabled-cursor, not-allowed);\n --_icon-button-disabled-opacity:var(--forge-icon-button-disabled-opacity, 0.38);\n --_icon-button-popover-icon-padding:var(--forge-icon-button-popover-icon-padding, var(--forge-spacing-xsmall, 8px));\n --_icon-button-focus-indicator-color:var(--forge-icon-button-focus-indicator-color, var(--forge-theme-primary, #3f51b5));\n}\n\n.forge-icon-button {\n display: var(--_icon-button-display);\n position: relative;\n outline: none;\n -webkit-tap-highlight-color: rgba(0, 0, 0, 0);\n position: relative;\n z-index: 0;\n display: var(--_icon-button-display);\n align-items: center;\n justify-content: center;\n gap: var(--_icon-button-gap);\n box-sizing: border-box;\n height: var(--_icon-button-density-large-size);\n min-width: var(--_icon-button-density-large-size);\n border: var(--_icon-button-border);\n border-start-start-radius: var(--_icon-button-shape-start-start);\n border-start-end-radius: var(--_icon-button-shape-start-end);\n border-end-start-radius: var(--_icon-button-shape-end-start);\n border-end-end-radius: var(--_icon-button-shape-end-end);\n padding: var(--_icon-button-padding);\n box-shadow: var(--_icon-button-shadow);\n color: var(--_icon-button-icon-color);\n background: var(--_icon-button-background-color);\n font-size: var(--_icon-button-icon-size);\n cursor: var(--_icon-button-cursor);\n user-select: none;\n transition-property: box-shadow, background;\n transition-duration: var(--_icon-button-transition-duration);\n transition-timing-function: var(--_icon-button-transition-timing);\n}\n\n.forge-icon-button img, .forge-icon-button svg {\n height: var(--_icon-button-icon-size);\n width: var(--_icon-button-icon-size);\n}\n\n.forge-icon-button svg {\n fill: currentColor;\n}\n\n.forge-icon-button:not(:disabled) {\n --_state-layer-display:var(--forge-state-layer-display, flex);\n --_state-layer-color:var(--forge-state-layer-color, var(--forge-theme-on-surface, #000000));\n --_state-layer-hover-color:var(--forge-state-layer-hover-color, var(--_state-layer-color));\n --_state-layer-hover-opacity:var(--forge-state-layer-hover-opacity, 0.08);\n --_state-layer-pressed-color:var(--forge-state-layer-pressed-color, var(--_state-layer-color));\n --_state-layer-pressed-opacity:var(--forge-state-layer-pressed-opacity, 0.12);\n --_state-layer-hover-duration:var(--forge-state-layer-hover-duration, 15ms);\n --_state-layer-pressed-duration:var(--forge-state-layer-pressed-duration, 105ms);\n --_state-layer-animation-duration:var(--forge-state-layer-animation-duration, 375ms);\n}\n\n.forge-icon-button:not(:disabled)::before {\n opacity: 0;\n position: absolute;\n backface-visibility: hidden;\n transform: translateZ(0);\n background-color: var(--_state-layer-hover-color);\n inset: 0;\n transition: opacity var(--_state-layer-hover-duration) linear, background-color var(--_state-layer-hover-duration) linear;\n --_state-layer-hover-duration:var(--forge-state-layer-hover-duration, 100ms);\n content: "";\n opacity: 0;\n border-radius: inherit;\n}\n\n.forge-icon-button:not(:disabled):hover::before {\n background-color: var(--_state-layer-hover-color);\n opacity: var(--_state-layer-hover-opacity);\n}\n\n.forge-icon-button:not(:disabled):active::before {\n opacity: var(--_state-layer-pressed-opacity);\n transition-duration: var(--_state-layer-pressed-duration);\n --_state-layer-pressed-opacity:var(--forge-state-layer-pressed-opacity, 0.18);\n}\n\n.forge-icon-button:not(:disabled) {\n --forge-state-layer-color: var(--_icon-button-icon-color);\n}\n\n@keyframes forge-focus-indicator-outward-grow {\n from {\n outline-width: 0;\n }\n to {\n outline-width: var(--_focus-indicator-active-width);\n }\n}\n@keyframes forge-focus-indicator-outward-shrink {\n from {\n outline-width: var(--_focus-indicator-active-width);\n }\n}\n@keyframes forge-focus-indicator-inward-grow {\n from {\n border-width: 0;\n }\n to {\n border-width: var(--_focus-indicator-active-width);\n }\n}\n@keyframes forge-focus-indicator-inward-shrink {\n from {\n border-width: var(--_focus-indicator-active-width);\n }\n}\n.forge-icon-button:not(:disabled) {\n outline: none;\n}\n\n.forge-icon-button:not(:disabled):focus-visible::after {\n --_focus-indicator-display:var(--forge-focus-indicator-display, flex);\n --_focus-indicator-width:var(--forge-focus-indicator-width, var(--forge-border-medium, 2px));\n --_focus-indicator-active-width:var(--forge-focus-indicator-active-width, 6px);\n --_focus-indicator-color:var(--forge-focus-indicator-color, var(--forge-theme-primary, #3f51b5));\n --_focus-indicator-shape:var(--forge-focus-indicator-shape, calc(var(--forge-shape-extra-small, 1px) * var(--forge-shape-factor, 1)));\n --_focus-indicator-duration:var(--forge-focus-indicator-duration, var(--forge-animation-duration-long4, 600ms));\n --_focus-indicator-easing:var(--forge-focus-indicator-easing, var(--forge-animation-easing-emphasized, cubic-bezier(0.2, 0, 0, 1)));\n --_focus-indicator-shape-start-start:var(--forge-focus-indicator-shape-start-start, var(--_focus-indicator-shape));\n --_focus-indicator-shape-start-end:var(--forge-focus-indicator-shape-start-end, var(--_focus-indicator-shape));\n --_focus-indicator-shape-end-end:var(--forge-focus-indicator-shape-end-end, var(--_focus-indicator-shape));\n --_focus-indicator-shape-end-start:var(--forge-focus-indicator-shape-end-start, var(--_focus-indicator-shape));\n --_focus-indicator-outward-offset:var(--forge-focus-indicator-outward-offset, var(--forge-spacing-xxsmall, 4px));\n --_focus-indicator-inward-offset:var(--forge-focus-indicator-inward-offset, 0px);\n --_focus-indicator-offset-block:var(--forge-focus-indicator-offset-block, 0);\n --_focus-indicator-offset-inline:var(--forge-focus-indicator-offset-inline, 0);\n}\n\n.forge-icon-button:not(:disabled):focus-visible::after {\n animation-delay: 0s, calc(var(--_focus-indicator-duration) * 0.25);\n animation-duration: calc(var(--_focus-indicator-duration) * 0.25), calc(var(--_focus-indicator-duration) * 0.75);\n animation-timing-function: var(--_focus-indicator-easing);\n box-sizing: border-box;\n color: var(--_focus-indicator-color);\n display: none;\n pointer-events: none;\n position: absolute;\n margin-block: var(--_focus-indicator-offset-block);\n margin-inline: var(--_focus-indicator-offset-inline);\n animation-name: forge-focus-indicator-outward-grow, forge-focus-indicator-outward-shrink;\n border-end-end-radius: calc(var(--_focus-indicator-shape-end-end) + var(--_focus-indicator-outward-offset));\n border-end-start-radius: calc(var(--_focus-indicator-shape-end-start) + var(--_focus-indicator-outward-offset));\n border-start-end-radius: calc(var(--_focus-indicator-shape-start-end) + var(--_focus-indicator-outward-offset));\n border-start-start-radius: calc(var(--_focus-indicator-shape-start-start) + var(--_focus-indicator-outward-offset));\n inset: calc(-1 * var(--_focus-indicator-outward-offset));\n outline: var(--_focus-indicator-width) solid currentColor;\n content: "";\n display: block;\n}\n\n.forge-icon-button:not(:disabled) {\n --forge-focus-indicator-color: var(--_icon-button-focus-indicator-color);\n --forge-focus-indicator-shape: var(--_icon-button-shape);\n}\n\n.forge-icon-button:not(:disabled):where(.forge-icon-button--text, :not(:where(.forge-icon-button--outlined, .forge-icon-button--tonal, .forge-icon-button--filled, .forge-icon-button--raised))) {\n --forge-focus-indicator-outward-offset: 0px;\n}\n\n.forge-icon-button--outlined {\n border-width: var(--_icon-button-outlined-border-width);\n border-style: var(--_icon-button-outlined-border-style);\n border-color: var(--_icon-button-outlined-border-color);\n}\n\n.forge-icon-button--tonal {\n --_icon-button-icon-color:var(--_icon-button-tonal-icon-color);\n --_icon-button-background-color:var(--_icon-button-tonal-background-color);\n}\n\n.forge-icon-button--filled, .forge-icon-button--raised {\n --_icon-button-icon-color:var(--_icon-button-filled-icon-color);\n --_icon-button-background-color:var(--_icon-button-filled-background-color);\n}\n\n.forge-icon-button--raised {\n --_icon-button-shadow:var(--_icon-button-raised-shadow);\n}\n\n.forge-icon-button--raised:hover {\n --_icon-button-raised-shadow:var(--_icon-button-raised-hover-shadow);\n}\n\n.forge-icon-button--raised:active {\n --_icon-button-raised-shadow:var(--_icon-button-raised-active-shadow);\n}\n\n.forge-icon-button--squared {\n --_icon-button-shape:var(--_icon-button-shape-squared);\n}\n\n.forge-icon-button--small {\n --_icon-button-size:var(--_icon-button-density-small-size);\n --_icon-button-icon-size:var(--_icon-button-density-small-icon-size);\n --_icon-button-padding:var(--_icon-button-density-small-padding);\n}\n\n.forge-icon-button--small > * {\n font-size: var(--_icon-button-density-small-icon-size);\n}\n\n.forge-icon-button--medium {\n --_icon-button-size:var(--_icon-button-density-medium-size);\n --_icon-button-padding:var(--_icon-button-density-medium-padding);\n}\n\n.forge-icon-button:disabled {\n pointer-events: none;\n opacity: var(--_icon-button-disabled-opacity);\n pointer-events: auto;\n cursor: not-allowed;\n}\n\n.forge-icon-button forge-circular-progress {\n --forge-circular-progress-indicator-color: var(--_icon-button-icon-color);\n --forge-circular-progress-track-color: transparent;\n --forge-circular-progress-size: 1em;\n}\n\n:host {\n display: block;\n margin-block: var(--forge-spacing-medium, 16px);\n --_mcp-host-color-background-primary: var(--forge-theme-surface, #ffffff);\n --_mcp-host-color-background-secondary: var(--forge-theme-surface-container-minimum, #f5f5f5);\n --_mcp-host-color-background-tertiary: var(--forge-theme-surface-container-low, #ebebeb);\n --_mcp-host-color-background-inverse: var(--forge-theme-surface-inverse, #333333);\n --_mcp-host-color-background-info: var(--forge-theme-info-container-minimum, #f6f9fc);\n --_mcp-host-color-background-danger: var(--forge-theme-error-container-minimum, #fcf5f6);\n --_mcp-host-color-background-success: var(--forge-theme-success-container-minimum, #f7faf7);\n --_mcp-host-color-background-warning: var(--forge-theme-warning-container-minimum, #fdf8f5);\n --_mcp-host-color-text-primary: var(--forge-theme-text-high, rgba(0, 0, 0, 0.87));\n --_mcp-host-color-text-secondary: var(--forge-theme-text-medium, rgba(0, 0, 0, 0.6));\n --_mcp-host-color-text-tertiary: var(--forge-theme-text-low, rgba(0, 0, 0, 0.38));\n --_mcp-host-color-text-inverse: var(--forge-theme-on-surface-inverse, #ffffff);\n --_mcp-host-color-text-info: var(--forge-theme-info, #1565c0);\n --_mcp-host-color-text-danger: var(--forge-theme-error, #b00020);\n --_mcp-host-color-text-success: var(--forge-theme-success, #2e7d32);\n --_mcp-host-color-text-warning: var(--forge-theme-warning, #d14900);\n --_mcp-host-color-text-disabled: var(--forge-theme-text-lowest, rgba(0, 0, 0, 0.12));\n --_mcp-host-color-border-primary: var(--forge-theme-outline, #e0e0e0);\n --_mcp-host-color-border-secondary: var(--forge-theme-outline-low, #9e9e9e);\n --_mcp-host-color-border-info: var(--forge-theme-info, #1565c0);\n --_mcp-host-color-border-danger: var(--forge-theme-error, #b00020);\n --_mcp-host-color-border-success: var(--forge-theme-success, #2e7d32);\n --_mcp-host-color-border-warning: var(--forge-theme-warning, #d14900);\n --_mcp-host-color-ring-primary: var(--forge-theme-primary, #3f51b5);\n --_mcp-host-font-sans: var(--forge-typography-body2-font-family, var(--forge-typography-font-family, "Roboto", sans-serif));\n --_mcp-host-font-weight-normal: 400;\n --_mcp-host-font-weight-medium: 500;\n --_mcp-host-font-weight-bold: 700;\n --_mcp-host-font-text-xs-size: var(--forge-typography-label1-font-size, calc(var(--forge-typography-font-size, 1rem) * var(--forge-typography-label-font-size-scale, 0.75)));\n --_mcp-host-font-text-sm-size: var(--forge-typography-body1-font-size, calc(var(--forge-typography-font-size, 1rem) * var(--forge-typography-body-font-size-scale, 0.875)));\n --_mcp-host-font-text-md-size: var(--forge-typography-body2-font-size, calc(var(--forge-typography-font-size, 1rem) * var(--forge-typography-body-font-size-scale, 1)));\n --_mcp-host-font-text-lg-size: var(--forge-typography-body3-font-size, calc(var(--forge-typography-font-size, 1rem) * var(--forge-typography-body-font-size-scale, 1.125)));\n --_mcp-host-font-text-xs-line-height: var(--forge-typography-label1-line-height, calc(var(--forge-typography-font-size, 1rem) * var(--forge-typography-label-line-height-scale, 1.25)));\n --_mcp-host-font-text-sm-line-height: var(--forge-typography-body1-line-height, calc(var(--forge-typography-font-size, 1rem) * var(--forge-typography-body-line-height-scale, 1.125)));\n --_mcp-host-font-text-md-line-height: var(--forge-typography-body2-line-height, calc(var(--forge-typography-font-size, 1rem) * var(--forge-typography-body-line-height-scale, 1.375)));\n --_mcp-host-font-text-lg-line-height: var(--forge-typography-body3-line-height, calc(var(--forge-typography-font-size, 1rem) * var(--forge-typography-body-line-height-scale, 1.5)));\n --_mcp-host-font-heading-sm-size: var(--forge-typography-heading1-font-size, calc(var(--forge-typography-font-size, 1rem) * var(--forge-typography-heading1-font-size-scale, 0.875)));\n --_mcp-host-font-heading-md-size: var(--forge-typography-heading2-font-size, calc(var(--forge-typography-font-size, 1rem) * var(--forge-typography-heading2-font-size-scale, 1)));\n --_mcp-host-font-heading-lg-size: var(--forge-typography-heading4-font-size, calc(var(--forge-typography-font-size, 1rem) * var(--forge-typography-heading4-font-size-scale, 1.25)));\n --_mcp-host-font-heading-xl-size: var(--forge-typography-heading5-font-size, calc(var(--forge-typography-font-size, 1rem) * var(--forge-typography-heading5-font-size-scale, 1.5)));\n --_mcp-host-font-heading-2xl-size: var(--forge-typography-heading7-font-size, calc(var(--forge-typography-font-size, 1rem) * var(--forge-typography-heading7-font-size-scale, 2)));\n --_mcp-host-font-heading-3xl-size: var(--forge-typography-heading8-font-size, calc(var(--forge-typography-font-size, 1rem) * var(--forge-typography-heading8-font-size-scale, 2.25)));\n --_mcp-host-font-heading-sm-line-height: var(--forge-typography-heading1-line-height, calc(var(--forge-typography-font-size, 1rem) * var(--forge-typography-heading1-line-height-scale, 1.125)));\n --_mcp-host-font-heading-md-line-height: var(--forge-typography-heading2-line-height, calc(var(--forge-typography-font-size, 1rem) * var(--forge-typography-heading2-line-height-scale, 1.25)));\n --_mcp-host-font-heading-lg-line-height: var(--forge-typography-heading4-line-height, calc(var(--forge-typography-font-size, 1rem) * var(--forge-typography-heading4-line-height-scale, 1.5)));\n --_mcp-host-font-heading-xl-line-height: var(--forge-typography-heading5-line-height, calc(var(--forge-typography-font-size, 1rem) * var(--forge-typography-heading5-line-height-scale, 1.75)));\n --_mcp-host-font-heading-2xl-line-height: var(--forge-typography-heading7-line-height, calc(var(--forge-typography-font-size, 1rem) * var(--forge-typography-heading7-line-height-scale, 2.5)));\n --_mcp-host-font-heading-3xl-line-height: var(--forge-typography-heading8-line-height, calc(var(--forge-typography-font-size, 1rem) * var(--forge-typography-heading8-line-height-scale, 2.625)));\n --_mcp-host-border-radius-xs: calc(var(--forge-shape-extra-small, 1px) * var(--forge-shape-factor, 1));\n --_mcp-host-border-radius-sm: calc(var(--forge-shape-small, 2px) * var(--forge-shape-factor, 1));\n --_mcp-host-border-radius-md: calc(var(--forge-shape-medium, 4px) * var(--forge-shape-factor, 1));\n --_mcp-host-border-radius-lg: calc(var(--forge-shape-large, 8px) * var(--forge-shape-factor, 1));\n --_mcp-host-border-radius-xl: calc(var(--forge-shape-extra-large, 16px) * var(--forge-shape-factor, 1));\n --_mcp-host-border-radius-full: calc(var(--forge-shape-full, 9999px) * var(--forge-shape-factor, 1));\n --_mcp-host-border-width-regular: var(--forge-border-thin, 1px);\n --_mcp-host-shadow-sm: 0px 2px 1px -1px rgba(0, 0, 0, 0.2), 0px 1px 1px 0px rgba(0, 0, 0, 0.14), 0px 1px 3px 0px rgba(0, 0, 0, 0.12);\n --_mcp-host-shadow-md: 0px 2px 4px -1px rgba(0, 0, 0, 0.2), 0px 4px 5px 0px rgba(0, 0, 0, 0.14), 0px 1px 10px 0px rgba(0, 0, 0, 0.12);\n --_mcp-host-shadow-lg: 0px 5px 5px -3px rgba(0, 0, 0, 0.2), 0px 8px 10px 1px rgba(0, 0, 0, 0.14), 0px 3px 14px 2px rgba(0, 0, 0, 0.12);\n}\n\n.container {\n position: relative;\n inset: auto;\n margin: 0;\n width: 100%;\n height: 100%;\n display: block;\n padding: 0;\n border: none;\n background: inherit;\n color: inherit;\n}\n.container.bordered {\n border: 1px solid var(--forge-theme-outline, #e0e0e0);\n border-radius: calc(var(--forge-shape-large, 8px) * var(--forge-shape-factor, 1));\n overflow: hidden;\n}\n.container:popover-open {\n position: fixed;\n display: flex;\n flex-direction: column;\n background: var(--forge-theme-surface, #ffffff);\n color: var(--forge-theme-text-high, rgba(0, 0, 0, 0.87));\n}\n.container:popover-open iframe {\n flex: 1;\n min-height: 0;\n}\n.container.fullscreen:popover-open {\n inset: 0;\n width: 100vw;\n height: 100dvh;\n border: none;\n border-radius: 0;\n}\n.container.fullscreen:popover-open::backdrop {\n background: var(--forge-theme-surface, #ffffff);\n}\n.container.pip:popover-open {\n inset: auto;\n inset-block-end: var(--forge-spacing-medium, 16px);\n inset-inline-end: var(--forge-spacing-medium, 16px);\n width: min(360px, 100vw - 2 * var(--forge-spacing-medium, 16px));\n height: 280px;\n border: 1px solid var(--forge-theme-outline, #e0e0e0);\n border-radius: calc(var(--forge-shape-large, 8px) * var(--forge-shape-factor, 1));\n overflow: hidden;\n box-shadow: 0px 2px 4px -1px rgba(0, 0, 0, 0.2), 0px 4px 5px 0px rgba(0, 0, 0, 0.14), 0px 1px 10px 0px rgba(0, 0, 0, 0.12);\n}\n\n.promoted-toolbar {\n display: flex;\n justify-content: flex-end;\n flex: none;\n padding: var(--forge-spacing-xxsmall, 4px);\n border-block-end: 1px solid var(--forge-theme-outline, #e0e0e0);\n}\n\n.exit-button,\n.exit-button > svg {\n fill: var(--forge-theme-text-medium, rgba(0, 0, 0, 0.6));\n}\n\niframe {\n display: block;\n border: 0;\n width: 100%;\n height: 100%;\n}\n\n.error-icon {\n width: 48px;\n height: 48px;\n fill: var(--forge-theme-outline, #e0e0e0);\n}';
|
|
2
|
+
export {
|
|
3
|
+
styles as default
|
|
4
|
+
};
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import { McpAppHostCapabilities, McpAppHostContext, McpAppResourceCsp, McpAppResourcePermissions, ToolCall } from '../../ai-chatbot';
|
|
2
|
+
import { IMcpAppBridge, McpAppViewToolResult } from './mcp-app-bridge-types.js';
|
|
3
|
+
/**
|
|
4
|
+
* The view→host handler slots the element supplies; registered on the bridge BEFORE
|
|
5
|
+
* `connect()` so they are live for the very first widget request.
|
|
6
|
+
*/
|
|
7
|
+
export type McpAppHandlers = Pick<IMcpAppBridge, 'oncalltool' | 'onreadresource' | 'onopenlink' | 'onmessage' | 'onloggingmessage' | 'onsizechange' | 'onrequestdisplaymode' | 'onupdatemodelcontext' | 'onrequestteardown'>;
|
|
8
|
+
export interface McpAppConnectConfig {
|
|
9
|
+
iframe: HTMLIFrameElement;
|
|
10
|
+
sandboxUrl: string;
|
|
11
|
+
html: string;
|
|
12
|
+
hostContext: McpAppHostContext;
|
|
13
|
+
hostCapabilities: McpAppHostCapabilities;
|
|
14
|
+
csp: McpAppResourceCsp;
|
|
15
|
+
permissions: McpAppResourcePermissions;
|
|
16
|
+
sandbox?: string;
|
|
17
|
+
handlers: McpAppHandlers;
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Compose the CSP header value the sandbox server expects on `?csp=` (spec "CSP
|
|
21
|
+
* Construction from Metadata"). Empty allow-lists produce the restrictive default:
|
|
22
|
+
* `connect-src 'none'`, `frame-src 'none'`, `base-uri 'self'`, `object-src 'none'`.
|
|
23
|
+
*/
|
|
24
|
+
export declare function buildCspHeader(csp: McpAppResourceCsp): string;
|
|
25
|
+
/**
|
|
26
|
+
* The requested permissions the sandbox can enforce, normalized to the spec shape. A key
|
|
27
|
+
* is requested when present (`{ camera: {} }`); `false`/`null` values are ignored.
|
|
28
|
+
*/
|
|
29
|
+
export declare function enforcedPermissions(permissions: McpAppResourcePermissions): McpAppResourcePermissions;
|
|
30
|
+
/** Map requested permissions onto the iframe `allow` attribute. */
|
|
31
|
+
export declare function buildAllowAttribute(permissions: McpAppResourcePermissions): string;
|
|
32
|
+
/**
|
|
33
|
+
* Build the View's `CallToolResult` from the raw MCP result + structured content — never
|
|
34
|
+
* from the model-facing `result`. When the backend omitted a field (size caps) the View
|
|
35
|
+
* gets what remains; when both are missing it gets an empty `content` array, flagged
|
|
36
|
+
* `isError` only if the tool call itself errored.
|
|
37
|
+
*/
|
|
38
|
+
export declare function buildViewToolResult(toolCall: ToolCall): McpAppViewToolResult;
|
|
39
|
+
/**
|
|
40
|
+
* Drives an {@link IMcpAppBridge} through the sandbox handshake and the streaming
|
|
41
|
+
* tool-call lifecycle. Bridge-agnostic — talks to the interface only.
|
|
42
|
+
*
|
|
43
|
+
* The controller owns the OUTER sandbox iframe DOM (its `src` with the `?csp=` param and
|
|
44
|
+
* its `allow` attribute); the bridge owns the postMessage lifecycle.
|
|
45
|
+
*/
|
|
46
|
+
export declare class McpAppBridgeController {
|
|
47
|
+
#private;
|
|
48
|
+
constructor(bridge: IMcpAppBridge);
|
|
49
|
+
connect(config: McpAppConnectConfig): Promise<void>;
|
|
50
|
+
/**
|
|
51
|
+
* Reconcile the widget with the current tool-call state. Streams partial args, sends
|
|
52
|
+
* final args once, then exactly one of `tool-result` (complete/error) or
|
|
53
|
+
* `tool-cancelled` — all gated on the `initialized` handshake. Safe to call on every
|
|
54
|
+
* `toolCall` update.
|
|
55
|
+
*/
|
|
56
|
+
syncToolCall(toolCall: ToolCall): void;
|
|
57
|
+
/** Forward a host-context change; the bridge holds it until the View is initialized. */
|
|
58
|
+
sendHostContextChange(hostContext: McpAppHostContext): void;
|
|
59
|
+
/** Send `ui/resource-teardown` and release the bridge; the iframe is removed without waiting for the ack. */
|
|
60
|
+
teardown(): void;
|
|
61
|
+
}
|
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
const PERMISSION_FEATURES = {
|
|
2
|
+
camera: "camera",
|
|
3
|
+
microphone: "microphone",
|
|
4
|
+
geolocation: "geolocation",
|
|
5
|
+
clipboardWrite: "clipboard-write"
|
|
6
|
+
};
|
|
7
|
+
const UNSAFE_CSP_SOURCE = /[\s;,'"]/;
|
|
8
|
+
function cspSources(domains, fallback) {
|
|
9
|
+
const safe = (domains ?? []).filter((domain) => !!domain && !UNSAFE_CSP_SOURCE.test(domain));
|
|
10
|
+
return safe.length ? safe.join(" ") : fallback;
|
|
11
|
+
}
|
|
12
|
+
function buildCspHeader(csp) {
|
|
13
|
+
const resource = cspSources(csp.resourceDomains, "");
|
|
14
|
+
const extra = resource ? ` ${resource}` : "";
|
|
15
|
+
return [
|
|
16
|
+
"default-src 'none'",
|
|
17
|
+
`script-src 'self' 'unsafe-inline'${extra}`,
|
|
18
|
+
`style-src 'self' 'unsafe-inline'${extra}`,
|
|
19
|
+
`img-src 'self' data:${extra}`,
|
|
20
|
+
`media-src 'self' data:${extra}`,
|
|
21
|
+
`font-src 'self' data:${extra}`,
|
|
22
|
+
`connect-src ${cspSources(csp.connectDomains, "'none'")}`,
|
|
23
|
+
`frame-src ${cspSources(csp.frameDomains, "'none'")}`,
|
|
24
|
+
`base-uri ${cspSources(csp.baseUriDomains, "'self'")}`,
|
|
25
|
+
"object-src 'none'"
|
|
26
|
+
].join("; ");
|
|
27
|
+
}
|
|
28
|
+
function enforcedPermissions(permissions) {
|
|
29
|
+
const keys = Object.keys(PERMISSION_FEATURES).filter((key) => {
|
|
30
|
+
const value = permissions[key];
|
|
31
|
+
return value !== void 0 && value !== null && value !== false;
|
|
32
|
+
});
|
|
33
|
+
return Object.fromEntries(keys.map((key) => [key, {}]));
|
|
34
|
+
}
|
|
35
|
+
function buildAllowAttribute(permissions) {
|
|
36
|
+
return Object.keys(enforcedPermissions(permissions)).map((key) => PERMISSION_FEATURES[key]).join("; ");
|
|
37
|
+
}
|
|
38
|
+
function buildViewToolResult(toolCall) {
|
|
39
|
+
const mcpResult = toolCall.mcpResultOmitted ? void 0 : toolCall.mcpResult;
|
|
40
|
+
const structuredContent = toolCall.structuredContentOmitted ? void 0 : toolCall.structuredContent;
|
|
41
|
+
const isError = mcpResult?.isError ?? (toolCall.status === "error" || void 0);
|
|
42
|
+
return {
|
|
43
|
+
content: Array.isArray(mcpResult?.content) ? mcpResult.content : [],
|
|
44
|
+
...structuredContent !== void 0 ? { structuredContent } : {},
|
|
45
|
+
...isError !== void 0 ? { isError } : {},
|
|
46
|
+
...mcpResult?._meta !== void 0 ? { _meta: mcpResult._meta } : {}
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
class McpAppBridgeController {
|
|
50
|
+
#bridge;
|
|
51
|
+
#connected = null;
|
|
52
|
+
#lastArgsSent = null;
|
|
53
|
+
#inputSent = false;
|
|
54
|
+
#outcome = "pending";
|
|
55
|
+
constructor(bridge) {
|
|
56
|
+
this.#bridge = bridge;
|
|
57
|
+
}
|
|
58
|
+
connect(config) {
|
|
59
|
+
if (this.#connected) {
|
|
60
|
+
return this.#connected;
|
|
61
|
+
}
|
|
62
|
+
Object.assign(this.#bridge, config.handlers);
|
|
63
|
+
const { iframe, sandboxUrl, csp, permissions } = config;
|
|
64
|
+
const outerAllow = buildAllowAttribute(permissions);
|
|
65
|
+
if (outerAllow) {
|
|
66
|
+
iframe.setAttribute("allow", outerAllow);
|
|
67
|
+
} else {
|
|
68
|
+
iframe.removeAttribute("allow");
|
|
69
|
+
}
|
|
70
|
+
const sandboxWindow = iframe.contentWindow;
|
|
71
|
+
if (!sandboxWindow) {
|
|
72
|
+
return Promise.reject(new Error("sandbox iframe has no contentWindow"));
|
|
73
|
+
}
|
|
74
|
+
let sandboxOrigin;
|
|
75
|
+
try {
|
|
76
|
+
sandboxOrigin = new URL(sandboxUrl, window.location.href).origin;
|
|
77
|
+
} catch {
|
|
78
|
+
return Promise.reject(new Error(`invalid sandbox URL: ${sandboxUrl}`));
|
|
79
|
+
}
|
|
80
|
+
if (sandboxOrigin === "null" || sandboxOrigin === window.location.origin) {
|
|
81
|
+
return Promise.reject(new Error("sandbox URL must be on a distinct, non-opaque origin"));
|
|
82
|
+
}
|
|
83
|
+
this.#connected = this.#bridge.connect({
|
|
84
|
+
sandboxWindow,
|
|
85
|
+
sandboxOrigin,
|
|
86
|
+
html: config.html,
|
|
87
|
+
hostContext: config.hostContext,
|
|
88
|
+
hostCapabilities: config.hostCapabilities,
|
|
89
|
+
permissions: enforcedPermissions(permissions),
|
|
90
|
+
sandbox: config.sandbox
|
|
91
|
+
});
|
|
92
|
+
const separator = sandboxUrl.includes("?") ? "&" : "?";
|
|
93
|
+
iframe.src = `${sandboxUrl}${separator}csp=${encodeURIComponent(buildCspHeader(csp))}`;
|
|
94
|
+
return this.#connected;
|
|
95
|
+
}
|
|
96
|
+
/**
|
|
97
|
+
* Reconcile the widget with the current tool-call state. Streams partial args, sends
|
|
98
|
+
* final args once, then exactly one of `tool-result` (complete/error) or
|
|
99
|
+
* `tool-cancelled` — all gated on the `initialized` handshake. Safe to call on every
|
|
100
|
+
* `toolCall` update.
|
|
101
|
+
*/
|
|
102
|
+
syncToolCall(toolCall) {
|
|
103
|
+
if (!this.#connected || this.#outcome !== "pending") {
|
|
104
|
+
return;
|
|
105
|
+
}
|
|
106
|
+
void this.#connected.then(() => {
|
|
107
|
+
if (this.#outcome !== "pending") {
|
|
108
|
+
return;
|
|
109
|
+
}
|
|
110
|
+
if (toolCall.status === "cancelled") {
|
|
111
|
+
this.#outcome = "cancel-sent";
|
|
112
|
+
this.#bridge.sendToolCancelled();
|
|
113
|
+
return;
|
|
114
|
+
}
|
|
115
|
+
const partialArgs = this.#coerceArgs(toolCall.args);
|
|
116
|
+
const partialString = JSON.stringify(partialArgs);
|
|
117
|
+
if (!this.#inputSent && Object.keys(partialArgs).length && partialString !== this.#lastArgsSent) {
|
|
118
|
+
this.#lastArgsSent = partialString;
|
|
119
|
+
this.#bridge.sendToolInputPartial(partialArgs);
|
|
120
|
+
}
|
|
121
|
+
const isFinished = toolCall.status === "complete" || toolCall.status === "error";
|
|
122
|
+
if ((toolCall.status === "executing" || isFinished) && !this.#inputSent) {
|
|
123
|
+
this.#inputSent = true;
|
|
124
|
+
this.#bridge.sendToolInput(this.#coerceArgs(toolCall.args));
|
|
125
|
+
}
|
|
126
|
+
if (isFinished) {
|
|
127
|
+
this.#outcome = "result-sent";
|
|
128
|
+
this.#bridge.sendToolResult(buildViewToolResult(toolCall));
|
|
129
|
+
}
|
|
130
|
+
});
|
|
131
|
+
}
|
|
132
|
+
/** Forward a host-context change; the bridge holds it until the View is initialized. */
|
|
133
|
+
sendHostContextChange(hostContext) {
|
|
134
|
+
if (this.#connected) {
|
|
135
|
+
this.#bridge.sendHostContextChange(hostContext);
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
/** Send `ui/resource-teardown` and release the bridge; the iframe is removed without waiting for the ack. */
|
|
139
|
+
teardown() {
|
|
140
|
+
if (!this.#connected) {
|
|
141
|
+
return;
|
|
142
|
+
}
|
|
143
|
+
this.#outcome = "torn-down";
|
|
144
|
+
this.#bridge.sendTeardown();
|
|
145
|
+
}
|
|
146
|
+
#coerceArgs(args) {
|
|
147
|
+
return args && typeof args === "object" ? args : {};
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
export {
|
|
151
|
+
McpAppBridgeController,
|
|
152
|
+
buildAllowAttribute,
|
|
153
|
+
buildCspHeader,
|
|
154
|
+
buildViewToolResult,
|
|
155
|
+
enforcedPermissions
|
|
156
|
+
};
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
import { McpAppDisplayMode, McpAppHostCapabilities, McpAppHostContext, McpAppResourcePermissions, McpModelContextUpdateParams, McpResourceReadParams, McpToolCallParams, McpToolResult } from '../../ai-chatbot';
|
|
2
|
+
/**
|
|
3
|
+
* The host-side bridge surface `mcp-app-bridge.ts` (`McpAppBridge`) implements. The
|
|
4
|
+
* element + controller depend on this interface only.
|
|
5
|
+
*/
|
|
6
|
+
/**
|
|
7
|
+
* A View's `tools/call` as received by the bridge. The bridge has no widget identity;
|
|
8
|
+
* the element adds its `toolCallId` before forwarding to the host.
|
|
9
|
+
*/
|
|
10
|
+
export type McpAppToolCallRequest = Omit<McpToolCallParams, 'toolCallId'>;
|
|
11
|
+
/** A View's `resources/read` as received by the bridge (no widget identity). */
|
|
12
|
+
export type McpAppResourceReadRequest = Omit<McpResourceReadParams, 'toolCallId'>;
|
|
13
|
+
/** The `CallToolResult` delivered to the View via `ui/notifications/tool-result`. */
|
|
14
|
+
export interface McpAppViewToolResult extends McpToolResult {
|
|
15
|
+
structuredContent?: unknown;
|
|
16
|
+
}
|
|
17
|
+
/** Parameters a widget passes to `ui/open-link`. */
|
|
18
|
+
export interface McpAppOpenLinkParams {
|
|
19
|
+
url: string;
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Result of a View request the host may decline (`ui/open-link`, `ui/message`): `{}` on
|
|
23
|
+
* success, `{ isError: true }` when the host refused or could not deliver it.
|
|
24
|
+
*/
|
|
25
|
+
export interface McpAppRequestResult {
|
|
26
|
+
isError?: boolean;
|
|
27
|
+
}
|
|
28
|
+
/** Parameters a widget passes to `ui/message`. `role` is always `user` (never trusted from the View). */
|
|
29
|
+
export interface McpAppMessageParams {
|
|
30
|
+
/** The View's MCP content blocks, unvalidated. */
|
|
31
|
+
content: unknown[];
|
|
32
|
+
role: 'user';
|
|
33
|
+
}
|
|
34
|
+
/** Parameters a widget passes to `ui/update-model-context` (no widget identity). */
|
|
35
|
+
export type McpAppUpdateModelContextParams = Omit<McpModelContextUpdateParams, 'toolCallId'>;
|
|
36
|
+
/** Parameters a widget passes to `ui/request-display-mode`. */
|
|
37
|
+
export interface McpAppRequestDisplayModeParams {
|
|
38
|
+
mode: McpAppDisplayMode;
|
|
39
|
+
/** Modes the View declared in `ui/initialize` (`appCapabilities.availableDisplayModes`). */
|
|
40
|
+
appDisplayModes: McpAppDisplayMode[];
|
|
41
|
+
}
|
|
42
|
+
/** Result the host returns for a display-mode request — echoes the mode actually set. */
|
|
43
|
+
export interface McpAppRequestDisplayModeResult {
|
|
44
|
+
mode: McpAppDisplayMode;
|
|
45
|
+
}
|
|
46
|
+
/** Payload for a widget `notifications/message` (logging). */
|
|
47
|
+
export interface McpAppLoggingMessage {
|
|
48
|
+
level: string;
|
|
49
|
+
data?: unknown;
|
|
50
|
+
logger?: string;
|
|
51
|
+
}
|
|
52
|
+
/** Payload for a widget `size-changed` notification. */
|
|
53
|
+
export interface McpAppSizeChange {
|
|
54
|
+
width?: number;
|
|
55
|
+
height: number;
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* Config for the `ui/initialize` handshake and injecting the resource into the sandbox.
|
|
59
|
+
*
|
|
60
|
+
* The controller owns the outer sandbox iframe DOM (setting `src` with the `?csp=` query
|
|
61
|
+
* param + the outer `allow` attribute); it hands the bridge the loaded iframe's
|
|
62
|
+
* `contentWindow` so the bridge can scope its transport to that window and drive the
|
|
63
|
+
* `sandbox-proxy-ready` → `sandbox-resource-ready` → `initialized` handshake. All fields
|
|
64
|
+
* are bridge-agnostic (no ext-apps types leak through this surface).
|
|
65
|
+
*/
|
|
66
|
+
export interface McpAppBridgeConnectConfig {
|
|
67
|
+
/** The loaded outer sandbox proxy iframe's window; the bridge scopes its transport here. */
|
|
68
|
+
sandboxWindow: Window;
|
|
69
|
+
/** Origin of the sandbox proxy; only messages from it are accepted and all posts target it. */
|
|
70
|
+
sandboxOrigin: string;
|
|
71
|
+
/** The complete standalone app HTML to inject into the inner sandboxed frame. */
|
|
72
|
+
html: string;
|
|
73
|
+
/** Initial host context sent to the widget on `ui/initialize`. */
|
|
74
|
+
hostContext: McpAppHostContext;
|
|
75
|
+
/** Iframe permission policy forwarded to the inner frame via `sandbox-resource-ready`. */
|
|
76
|
+
permissions?: McpAppResourcePermissions;
|
|
77
|
+
/** Optional override for the inner iframe's `sandbox` attribute. */
|
|
78
|
+
sandbox?: string;
|
|
79
|
+
/** Advertised to the View in the `ui/initialize` result. */
|
|
80
|
+
hostCapabilities: McpAppHostCapabilities;
|
|
81
|
+
}
|
|
82
|
+
export interface IMcpAppBridge {
|
|
83
|
+
/**
|
|
84
|
+
* Connect to the loaded sandbox proxy: perform the `ui/initialize` handshake and send
|
|
85
|
+
* the resource in via `sandbox-resource-ready`. Resolves once the widget has sent
|
|
86
|
+
* `initialized`. The host MUST NOT send tool input before this resolves.
|
|
87
|
+
*/
|
|
88
|
+
connect(config: McpAppBridgeConnectConfig): Promise<void>;
|
|
89
|
+
/** Streaming partial tool arguments (each args delta). */
|
|
90
|
+
sendToolInputPartial(args: Record<string, unknown>): void;
|
|
91
|
+
/** Final, complete tool arguments (args complete). */
|
|
92
|
+
sendToolInput(args: Record<string, unknown>): void;
|
|
93
|
+
/** The final tool result, built from the raw MCP result + structured content. */
|
|
94
|
+
sendToolResult(result: McpAppViewToolResult): void;
|
|
95
|
+
/** Notify the widget its tool call was cancelled. */
|
|
96
|
+
sendToolCancelled(reason?: string): void;
|
|
97
|
+
/**
|
|
98
|
+
* Push an updated host context (e.g. theme or container-size change). Before the View
|
|
99
|
+
* is initialized the change is held and returned from `ui/initialize` / flushed on
|
|
100
|
+
* `initialized` instead of being sent.
|
|
101
|
+
*/
|
|
102
|
+
sendHostContextChange(hostContext: McpAppHostContext): void;
|
|
103
|
+
/** Notify the widget it is being torn down (`ui/resource-teardown`) and release the bridge without waiting for the ack. */
|
|
104
|
+
sendTeardown(): void;
|
|
105
|
+
/** Widget forwarded a `tools/call` to the server. Rejections become JSON-RPC errors. */
|
|
106
|
+
oncalltool?: (params: McpAppToolCallRequest) => Promise<unknown>;
|
|
107
|
+
/** Widget forwarded a `resources/read` to the server. Rejections become JSON-RPC errors. */
|
|
108
|
+
onreadresource?: (params: McpAppResourceReadRequest) => Promise<unknown>;
|
|
109
|
+
/** Widget requested opening an external link. Absent → `{ isError: true }`. */
|
|
110
|
+
onopenlink?: (params: McpAppOpenLinkParams) => Promise<McpAppRequestResult>;
|
|
111
|
+
/** Widget sent a chat message (`ui/message`). Absent → `{ isError: true }`. */
|
|
112
|
+
onmessage?: (params: McpAppMessageParams) => Promise<McpAppRequestResult>;
|
|
113
|
+
/** Widget emitted a logging message (`notifications/message`). */
|
|
114
|
+
onloggingmessage?: (params: McpAppLoggingMessage) => void;
|
|
115
|
+
/** Widget reported a size change. */
|
|
116
|
+
onsizechange?: (params: McpAppSizeChange) => void;
|
|
117
|
+
/** Widget requested a display-mode change; the host echoes the resulting mode. */
|
|
118
|
+
onrequestdisplaymode?: (params: McpAppRequestDisplayModeParams) => Promise<McpAppRequestDisplayModeResult>;
|
|
119
|
+
/** Widget requested a silent model-context update. */
|
|
120
|
+
onupdatemodelcontext?: (params: McpAppUpdateModelContextParams) => Promise<void>;
|
|
121
|
+
/** Widget asked the host to tear it down (`ui/notifications/request-teardown`). */
|
|
122
|
+
onrequestteardown?: () => void;
|
|
123
|
+
}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { McpAppHostContext } from '../../ai-chatbot';
|
|
2
|
+
import { IMcpAppBridge, McpAppBridgeConnectConfig, McpAppViewToolResult } from './mcp-app-bridge-types.js';
|
|
3
|
+
/**
|
|
4
|
+
* Hand-rolled, zero-dependency {@link IMcpAppBridge}. A `postMessage` listener speaking
|
|
5
|
+
* JSON-RPC 2.0 directly: no SDK, no zod, no schema validation. The cross-origin sandbox
|
|
6
|
+
* iframe is the security boundary here, not message-schema checking — every inbound
|
|
7
|
+
* field is narrowed defensively before use.
|
|
8
|
+
*
|
|
9
|
+
* Drives the handshake against the sandbox proxy (`packages/ai/sandbox/sandbox.js`):
|
|
10
|
+
* `sandbox-proxy-ready` -> `sandbox-resource-ready` -> `ui/initialize` ->
|
|
11
|
+
* `ui/notifications/initialized`, then streaming `ui/notifications/*`. Forwards
|
|
12
|
+
* non-`ui/` methods (`tools/call`, `resources/read`) to the same handler slots the
|
|
13
|
+
* interface defines.
|
|
14
|
+
*/
|
|
15
|
+
export declare class McpAppBridge implements IMcpAppBridge {
|
|
16
|
+
#private;
|
|
17
|
+
oncalltool?: IMcpAppBridge['oncalltool'];
|
|
18
|
+
onreadresource?: IMcpAppBridge['onreadresource'];
|
|
19
|
+
onopenlink?: IMcpAppBridge['onopenlink'];
|
|
20
|
+
onmessage?: IMcpAppBridge['onmessage'];
|
|
21
|
+
onloggingmessage?: IMcpAppBridge['onloggingmessage'];
|
|
22
|
+
onsizechange?: IMcpAppBridge['onsizechange'];
|
|
23
|
+
onrequestdisplaymode?: IMcpAppBridge['onrequestdisplaymode'];
|
|
24
|
+
onupdatemodelcontext?: IMcpAppBridge['onupdatemodelcontext'];
|
|
25
|
+
onrequestteardown?: IMcpAppBridge['onrequestteardown'];
|
|
26
|
+
constructor(hostInfo: {
|
|
27
|
+
name: string;
|
|
28
|
+
version: string;
|
|
29
|
+
});
|
|
30
|
+
connect(config: McpAppBridgeConnectConfig): Promise<void>;
|
|
31
|
+
sendToolInputPartial(args: Record<string, unknown>): void;
|
|
32
|
+
sendToolInput(args: Record<string, unknown>): void;
|
|
33
|
+
sendToolResult(result: McpAppViewToolResult): void;
|
|
34
|
+
sendToolCancelled(reason?: string): void;
|
|
35
|
+
sendHostContextChange(hostContext: McpAppHostContext): void;
|
|
36
|
+
/**
|
|
37
|
+
* Send `ui/resource-teardown` and dispose immediately; the host doesn't wait for the ack,
|
|
38
|
+
* so the iframe is removed right away. A View that never initialized isn't messaged.
|
|
39
|
+
*/
|
|
40
|
+
sendTeardown(): void;
|
|
41
|
+
}
|