-
Notifications
You must be signed in to change notification settings - Fork 295
feat: add UIResourceRenderer Web Component #58
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
Deploying mcp-ui with
|
| Latest commit: |
e40e28b
|
| Status: | ✅ Deploy successful! |
| Preview URL: | https://225fb3fe.mcp-ui.pages.dev |
| Branch Preview URL: | https://feat-webcomponent.mcp-ui.pages.dev |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pull Request Overview
This PR adds Web Component support to the MCP-UI client library, enabling usage in non-React frameworks like Vue, Svelte, or vanilla JavaScript. The Web Component wraps the existing React-based UIResourceRenderer component and provides the same functionality through a standard HTML custom element.
Key changes:
- Added
UIResourceRendererWCWeb Component wrapper with event dispatching - Enhanced build system to generate the Web Component bundle
- Fixed iframe styling to use
height: 100%instead ofminHeight: 200px
Reviewed Changes
Copilot reviewed 26 out of 27 changed files in this pull request and generated 3 comments.
Show a summary per file
| File | Description |
|---|---|
sdks/typescript/client/src/components/UIResourceRendererWC.tsx |
New Web Component wrapper for UIResourceRenderer with JSON prop normalization |
sdks/typescript/client/src/components/UIResourceRendererWC.test.tsx |
Test suite for the Web Component functionality |
sdks/typescript/client/vite.config.wc.ts |
Vite configuration for building the Web Component bundle |
sdks/typescript/client/package.json |
Updated build scripts and added Web Component dependency |
sdks/typescript/client/src/components/HTMLResourceRenderer.tsx |
Changed iframe styling from minHeight to height: 100% |
examples/wc-demo/ |
Demo application showcasing Web Component usage |
| Documentation files | Updated to include Web Component usage examples and guides |
Files not reviewed (1)
- pnpm-lock.yaml: Language not supported
| try { | ||
| return JSON.parse(prop); | ||
| } catch (e) { | ||
| console.error('Failed to parse JSON prop:', { prop, error: e }); |
Copilot
AI
Aug 3, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Logging the entire prop value could expose sensitive data. Consider logging only the error message or a sanitized version of the prop.
| console.error('Failed to parse JSON prop:', { prop, error: e }); | |
| // Avoid logging the full prop to prevent leaking sensitive data | |
| const propInfo = typeof prop === 'string' | |
| ? { type: typeof prop, length: prop.length, preview: prop.slice(0, 50) } | |
| : { type: typeof prop }; | |
| console.error('Failed to parse JSON prop:', { propInfo, error: e instanceof Error ? e.message : e }); |
| export const UIResourceRendererWCWrapper: FC<UIResourceRendererWCProps> = (props) => { | ||
| const { | ||
| resource: rawResource, | ||
| supportedContentTypes: rawSupportedContentTypes, | ||
| htmlProps: rawHtmlProps, | ||
| remoteDomProps: rawRemoteDomProps, | ||
| } = props; | ||
|
|
||
| const resource = normalizeJsonProp(rawResource); | ||
| const supportedContentTypes = normalizeJsonProp(rawSupportedContentTypes); | ||
| const htmlProps = normalizeJsonProp(rawHtmlProps); | ||
| const remoteDomProps = normalizeJsonProp(rawRemoteDomProps); | ||
|
|
||
| const ref = useRef<HTMLDivElement>(null); | ||
|
|
||
| const onUIActionCallback = useCallback(async (event: UIActionResult): Promise<void> => { | ||
| if (ref.current) { | ||
| const customEvent = new CustomEvent('onUIAction', { | ||
| detail: event, | ||
| composed: true, | ||
| bubbles: true, | ||
| }); | ||
| ref.current.dispatchEvent(customEvent); | ||
| } | ||
| }, []); | ||
|
|
||
| if (!resource) { | ||
| return <p style={{ color: 'red' }}>Resource not provided.</p>; | ||
| } | ||
|
|
||
| return ( | ||
| <div ref={ref}> | ||
| <UIResourceRenderer | ||
| resource={resource as Resource} | ||
| supportedContentTypes={supportedContentTypes as unknown as UIResourceRendererProps['supportedContentTypes']} | ||
| htmlProps={htmlProps} | ||
| remoteDomProps={remoteDomProps} | ||
| onUIAction={onUIActionCallback} | ||
| /> | ||
| </div> | ||
| ); | ||
| }; | ||
|
|
||
|
|
Copilot
AI
Aug 3, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The type assertion as Resource bypasses type checking. Consider adding runtime validation to ensure the resource object has the required properties before casting.
| export const UIResourceRendererWCWrapper: FC<UIResourceRendererWCProps> = (props) => { | |
| const { | |
| resource: rawResource, | |
| supportedContentTypes: rawSupportedContentTypes, | |
| htmlProps: rawHtmlProps, | |
| remoteDomProps: rawRemoteDomProps, | |
| } = props; | |
| const resource = normalizeJsonProp(rawResource); | |
| const supportedContentTypes = normalizeJsonProp(rawSupportedContentTypes); | |
| const htmlProps = normalizeJsonProp(rawHtmlProps); | |
| const remoteDomProps = normalizeJsonProp(rawRemoteDomProps); | |
| const ref = useRef<HTMLDivElement>(null); | |
| const onUIActionCallback = useCallback(async (event: UIActionResult): Promise<void> => { | |
| if (ref.current) { | |
| const customEvent = new CustomEvent('onUIAction', { | |
| detail: event, | |
| composed: true, | |
| bubbles: true, | |
| }); | |
| ref.current.dispatchEvent(customEvent); | |
| } | |
| }, []); | |
| if (!resource) { | |
| return <p style={{ color: 'red' }}>Resource not provided.</p>; | |
| } | |
| return ( | |
| <div ref={ref}> | |
| <UIResourceRenderer | |
| resource={resource as Resource} | |
| supportedContentTypes={supportedContentTypes as unknown as UIResourceRendererProps['supportedContentTypes']} | |
| htmlProps={htmlProps} | |
| remoteDomProps={remoteDomProps} | |
| onUIAction={onUIActionCallback} | |
| /> | |
| </div> | |
| ); | |
| }; |
# 1.0.0-alpha.1 (2025-10-10) ### Bug Fixes * adapter version ([259c842](259c842)) * add a bridge to pass messages in and out of the proxy ([#38](#38)) ([30ccac0](30ccac0)) * bump client version ([75c9236](75c9236)) * **client:** specify iframe ([fd0b70a](fd0b70a)) * **client:** styling ([6ff9b68](6ff9b68)) * dependencies ([887f61f](887f61f)) * export RemoteDomResource ([2b86f2d](2b86f2d)) * export ResourceRenderer and HtmlResource ([2b841a5](2b841a5)) * exports ([3a93a16](3a93a16)) * iframe handle ([#15](#15)) ([66bd4fd](66bd4fd)) * lint ([4487820](4487820)) * lint ([d0a91f9](d0a91f9)) * minor typo ([a0bee9c](a0bee9c)) * move react dependencies to be peer dependencies ([#91](#91)) ([f672f3e](f672f3e)), closes [#90](#90) * package config ([8dc1e53](8dc1e53)) * packaging ([9e6babd](9e6babd)) * pass ref explicitly using iframeProps ([#33](#33)) ([d01b5d1](d01b5d1)) * publish ([0943e7a](0943e7a)) * ref passing to UIResourceRenderer ([#32](#32)) ([d28c23f](d28c23f)) * release ([420efc0](420efc0)) * remove shared dependency ([e66e8f4](e66e8f4)) * rename components and methods to fit new scope ([#22](#22)) ([6bab1fe](6bab1fe)) * rename delivery -> encoding and flavor -> framework ([#36](#36)) ([9a509ed](9a509ed)) * Ruby comment ([b22dc2e](b22dc2e)) * support react-router ([21ffb95](21ffb95)) * text and blob support in RemoteDOM resources ([ec68eb9](ec68eb9)) * trigger release ([aaca831](aaca831)) * typescript ci publish ([e7c0ebf](e7c0ebf)) * typescript types to be compatible with MCP SDK ([#10](#10)) ([74365d7](74365d7)) * update deps ([4091ef4](4091ef4)) * update isUIResource to use EmbeddedResource type ([#122](#122)) ([5a65a0b](5a65a0b)), closes [#117](#117) * use targetOrigin in the proxy message relay ([#40](#40)) ([b3fb54e](b3fb54e)) * validate URL ([b7c994d](b7c994d)) * wc dist overwrite ([#63](#63)) ([9e46c56](9e46c56)) ### Documentation * bump ([#4](#4)) ([ad4d163](ad4d163)) ### Features * add adapters infra (appssdk) ([#125](#125)) ([2e016cd](2e016cd)) * add convenience function isUIResource to client SDK ([#86](#86)) ([607c6ad](607c6ad)) * add embeddedResourceProps for annotations ([#99](#99)) ([b96ec44](b96ec44)) * add proxy option to externalUrl ([#37](#37)) ([7b95cd0](7b95cd0)) * add remote-dom content type ([#18](#18)) ([5dacf37](5dacf37)) * add Ruby server SDK ([#31](#31)) ([5ffcde4](5ffcde4)) * add sandbox permissions instead of an override ([#83](#83)) ([b1068e9](b1068e9)) * add ui-request-render-data message type ([#111](#111)) ([26135ce](26135ce)) * add UIResourceRenderer Web Component ([#58](#58)) ([ec8f299](ec8f299)) * auto resize with the autoResizeIframe prop ([#56](#56)) ([76c867a](76c867a)) * change onGenericMcpAction to optional onUiAction ([1913b59](1913b59)) * **client:** allow setting supportedContentTypes for HtmlResource ([#17](#17)) ([e009ef1](e009ef1)) * consolidate ui:// and ui-app:// ([#8](#8)) ([2e08035](2e08035)) * pass iframe props down ([#14](#14)) ([112539d](112539d)) * refactor UTFtoB64 (bump server version) ([#95](#95)) ([2d5e16b](2d5e16b)) * send render data to the iframe ([#51](#51)) ([d38cfc7](d38cfc7)) * separate html and remote-dom props ([#24](#24)) ([a7f0529](a7f0529)) * support generic messages response ([#35](#35)) ([10b407b](10b407b)) * support passing resource metadata ([#87](#87)) ([f1c1c9b](f1c1c9b)) * support ui action result types ([#6](#6)) ([899d152](899d152)) * switch to ResourceRenderer ([#21](#21)) ([6fe3166](6fe3166)) ### BREAKING CHANGES * The existing naming is ambiguous. Renaming delivery to encoding and flavor to framework should clarify the intent. * exported names have changed * removed deprecated client API * (previous one didn't take due to semantic-release misalignment)
# 1.0.0-alpha.1 (2025-10-10) ### Bug Fixes * adapter version ([259c842](259c842)) * add a bridge to pass messages in and out of the proxy ([#38](#38)) ([30ccac0](30ccac0)) * bump client version ([75c9236](75c9236)) * **client:** specify iframe ([fd0b70a](fd0b70a)) * **client:** styling ([6ff9b68](6ff9b68)) * dependencies ([887f61f](887f61f)) * export RemoteDomResource ([2b86f2d](2b86f2d)) * export ResourceRenderer and HtmlResource ([2b841a5](2b841a5)) * exports ([3a93a16](3a93a16)) * iframe handle ([#15](#15)) ([66bd4fd](66bd4fd)) * lint ([4487820](4487820)) * lint ([d0a91f9](d0a91f9)) * minor typo ([a0bee9c](a0bee9c)) * move react dependencies to be peer dependencies ([#91](#91)) ([f672f3e](f672f3e)), closes [#90](#90) * package config ([8dc1e53](8dc1e53)) * packaging ([9e6babd](9e6babd)) * pass ref explicitly using iframeProps ([#33](#33)) ([d01b5d1](d01b5d1)) * publish ([0943e7a](0943e7a)) * ref passing to UIResourceRenderer ([#32](#32)) ([d28c23f](d28c23f)) * release ([420efc0](420efc0)) * remove shared dependency ([e66e8f4](e66e8f4)) * rename components and methods to fit new scope ([#22](#22)) ([6bab1fe](6bab1fe)) * rename delivery -> encoding and flavor -> framework ([#36](#36)) ([9a509ed](9a509ed)) * Ruby comment ([b22dc2e](b22dc2e)) * server versioning ([2324371](2324371)) * support react-router ([21ffb95](21ffb95)) * text and blob support in RemoteDOM resources ([ec68eb9](ec68eb9)) * trigger release ([aaca831](aaca831)) * typescript ci publish ([e7c0ebf](e7c0ebf)) * typescript types to be compatible with MCP SDK ([#10](#10)) ([74365d7](74365d7)) * update deps ([4091ef4](4091ef4)) * update isUIResource to use EmbeddedResource type ([#122](#122)) ([5a65a0b](5a65a0b)), closes [#117](#117) * use targetOrigin in the proxy message relay ([#40](#40)) ([b3fb54e](b3fb54e)) * validate URL ([b7c994d](b7c994d)) * wc dist overwrite ([#63](#63)) ([9e46c56](9e46c56)) ### Documentation * bump ([#4](#4)) ([ad4d163](ad4d163)) ### Features * add adapters infra (appssdk) ([#125](#125)) ([2e016cd](2e016cd)) * add convenience function isUIResource to client SDK ([#86](#86)) ([607c6ad](607c6ad)) * add embeddedResourceProps for annotations ([#99](#99)) ([b96ec44](b96ec44)) * add proxy option to externalUrl ([#37](#37)) ([7b95cd0](7b95cd0)) * add remote-dom content type ([#18](#18)) ([5dacf37](5dacf37)) * add Ruby server SDK ([#31](#31)) ([5ffcde4](5ffcde4)) * add sandbox permissions instead of an override ([#83](#83)) ([b1068e9](b1068e9)) * add ui-request-render-data message type ([#111](#111)) ([26135ce](26135ce)) * add UIResourceRenderer Web Component ([#58](#58)) ([ec8f299](ec8f299)) * auto resize with the autoResizeIframe prop ([#56](#56)) ([76c867a](76c867a)) * change onGenericMcpAction to optional onUiAction ([1913b59](1913b59)) * **client:** allow setting supportedContentTypes for HtmlResource ([#17](#17)) ([e009ef1](e009ef1)) * consolidate ui:// and ui-app:// ([#8](#8)) ([2e08035](2e08035)) * pass iframe props down ([#14](#14)) ([112539d](112539d)) * refactor UTFtoB64 (bump server version) ([#95](#95)) ([2d5e16b](2d5e16b)) * send render data to the iframe ([#51](#51)) ([d38cfc7](d38cfc7)) * separate html and remote-dom props ([#24](#24)) ([a7f0529](a7f0529)) * support generic messages response ([#35](#35)) ([10b407b](10b407b)) * support passing resource metadata ([#87](#87)) ([f1c1c9b](f1c1c9b)) * support ui action result types ([#6](#6)) ([899d152](899d152)) * switch to ResourceRenderer ([#21](#21)) ([6fe3166](6fe3166)) ### BREAKING CHANGES * The existing naming is ambiguous. Renaming delivery to encoding and flavor to framework should clarify the intent. * exported names have changed * removed deprecated client API * (previous one didn't take due to semantic-release misalignment)
# 1.0.0 (2025-11-04) ### Bug Fixes * add a bridge to pass messages in and out of the proxy ([#38](#38)) ([30ccac0](30ccac0)) * bump client version ([75c9236](75c9236)) * **client:** specify iframe ([fd0b70a](fd0b70a)) * **client:** styling ([6ff9b68](6ff9b68)) * dependencies ([887f61f](887f61f)) * Enable bidirectional message relay in rawhtml proxy mode ([#138](#138)) ([f0bdefb](f0bdefb)) * ensure Apps SDK adapter is bundled properly and initialized wth config ([#137](#137)) ([4f7c25c](4f7c25c)) * export RemoteDomResource ([2b86f2d](2b86f2d)) * export ResourceRenderer and HtmlResource ([2b841a5](2b841a5)) * exports ([3a93a16](3a93a16)) * fix file extension reference in package.json ([927989c](927989c)) * iframe handle ([#15](#15)) ([66bd4fd](66bd4fd)) * lint ([4487820](4487820)) * lint ([d0a91f9](d0a91f9)) * minor typo ([a0bee9c](a0bee9c)) * move react dependencies to be peer dependencies ([#91](#91)) ([f672f3e](f672f3e)), closes [#90](#90) * package config ([8dc1e53](8dc1e53)) * packaging ([9e6babd](9e6babd)) * pass ref explicitly using iframeProps ([#33](#33)) ([d01b5d1](d01b5d1)) * publish ([0943e7a](0943e7a)) * ref passing to UIResourceRenderer ([#32](#32)) ([d28c23f](d28c23f)) * remove shared dependency ([e66e8f4](e66e8f4)) * rename components and methods to fit new scope ([#22](#22)) ([6bab1fe](6bab1fe)) * rename delivery -> encoding and flavor -> framework ([#36](#36)) ([9a509ed](9a509ed)) * Ruby comment ([b22dc2e](b22dc2e)) * support react-router ([21ffb95](21ffb95)) * text and blob support in RemoteDOM resources ([ec68eb9](ec68eb9)) * trigger release ([aaca831](aaca831)) * typescript ci publish ([e7c0ebf](e7c0ebf)) * typescript types to be compatible with MCP SDK ([#10](#10)) ([74365d7](74365d7)) * update deps ([4091ef4](4091ef4)) * update isUIResource to use EmbeddedResource type ([#122](#122)) ([5a65a0b](5a65a0b)), closes [#117](#117) * use targetOrigin in the proxy message relay ([#40](#40)) ([b3fb54e](b3fb54e)) * validate URL ([b7c994d](b7c994d)) * wc dist overwrite ([#63](#63)) ([9e46c56](9e46c56)) ### Documentation * bump ([#4](#4)) ([ad4d163](ad4d163)) ### Features * add convenience function isUIResource to client SDK ([#86](#86)) ([607c6ad](607c6ad)) * add embeddedResourceProps for annotations ([#99](#99)) ([b96ec44](b96ec44)) * add proxy option to externalUrl ([#37](#37)) ([7b95cd0](7b95cd0)) * add remote-dom content type ([#18](#18)) ([5dacf37](5dacf37)) * add Ruby server SDK ([#31](#31)) ([5ffcde4](5ffcde4)) * add sandbox permissions instead of an override ([#83](#83)) ([b1068e9](b1068e9)) * add ui-request-render-data message type ([#111](#111)) ([26135ce](26135ce)) * add UIResourceRenderer Web Component ([#58](#58)) ([ec8f299](ec8f299)) * auto resize with the autoResizeIframe prop ([#56](#56)) ([76c867a](76c867a)) * change onGenericMcpAction to optional onUiAction ([1913b59](1913b59)) * **client:** allow setting supportedContentTypes for HtmlResource ([#17](#17)) ([e009ef1](e009ef1)) * consolidate ui:// and ui-app:// ([#8](#8)) ([2e08035](2e08035)) * pass iframe props down ([#14](#14)) ([112539d](112539d)) * refactor UTFtoB64 (bump server version) ([#95](#95)) ([2d5e16b](2d5e16b)) * send render data to the iframe ([#51](#51)) ([d38cfc7](d38cfc7)) * separate html and remote-dom props ([#24](#24)) ([a7f0529](a7f0529)) * support adapters ([#127](#127)) ([d4bd152](d4bd152)) * support generic messages response ([#35](#35)) ([10b407b](10b407b)) * support metadata in Python SDK ([#134](#134)) ([9bc3c64](9bc3c64)) * support passing resource metadata ([#87](#87)) ([f1c1c9b](f1c1c9b)) * support proxy for rawHtml ([#132](#132)) ([1bbeb09](1bbeb09)) * support ui action result types ([#6](#6)) ([899d152](899d152)) * switch to ResourceRenderer ([#21](#21)) ([6fe3166](6fe3166)) ### BREAKING CHANGES * The existing naming is ambiguous. Renaming delivery to encoding and flavor to framework should clarify the intent. * exported names have changed * removed deprecated client API * (previous one didn't take due to semantic-release misalignment)
Add mcp-ui/client support for non-React projects