-
-
Notifications
You must be signed in to change notification settings - Fork 13.7k
✨ feat: Add MCP UI integration support #9326
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
base: main
Are you sure you want to change the base?
Conversation
- Add MCP UI resource extraction and rendering system - Support HTML, URL, and iframe content types with sandboxed rendering - Hide tool inspector when UI resources are present - Add type safety with TypeScript definitions - Support multiple UI resource formats and encoding patterns
@JoeCastrom is attempting to deploy a commit to the LobeHub OSS Team on Vercel. A member of the Team first needs to authorize it. |
Thank you for raising your pull request and contributing to our Community |
TestGru AssignmentSummary
Files
Tip You can |
Reviewer's GuideThis PR implements full MCP UI integration by extracting and encoding UI resources from MCP responses, extending service logic, updating rendering components, adjusting state persistence, and introducing new types, utilities, and sandboxed UI rendering components while preserving backward compatibility. Sequence diagram for MCP UI resource extraction and renderingsequenceDiagram
participant MCPServer
participant MCPService
participant extractUIResources()
participant Database
participant McpUIRender
participant User
MCPServer->>MCPService: Send response (may contain UI resources)
MCPService->>extractUIResources(): Extract text and UI resources
extractUIResources()-->>MCPService: Return {textContent, uiResources}
MCPService->>Database: Store encoded UI resources (Base64)
User->>McpUIRender: View message with UI resources
McpUIRender->>User: Render HTML/iframe/URL in sandboxed UI
Entity relationship diagram for MCP UI resource data model changeserDiagram
MESSAGE ||--o| MESSAGE_PLUGIN : contains
MESSAGE_PLUGIN {
text tool_call_id
text type
text api_name
text identifier
json args
json result
json error
}
MESSAGE_PLUGIN ||--|{ MCP_UI_RESOURCE : has
MCP_UI_RESOURCE {
string content
string type
json metadata
}
Class diagram for new and updated types for MCP UI integrationclassDiagram
class McpUIResource {
+string content
+string type ("html"|"url"|"iframe")
+metadata: Record<string, any>
}
class LobeTool {
+type: LobeToolRenderType
}
class LobeToolRenderType {
<<union>>
builtin
mcp-ui
...
}
class BuiltinToolManifest {
+api
+identifier
+meta
+systemRole
+type
}
McpUIResource <|-- BuiltinToolManifest
LobeTool --> LobeToolRenderType
File-Level Changes
Tips and commandsInteracting with Sourcery
Customizing Your ExperienceAccess your dashboard to:
Getting Help
|
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.
Hey there - I've reviewed your changes and they look great!
Prompt for AI Agents
Please address the comments from this code review:
## Individual Comments
### Comment 1
<location> `src/tools/mcp-ui/utils/extractUIResources.ts:17` </location>
<code_context>
+ // Parse text content for ui:// prefixed resources
</code_context>
<issue_to_address>
**suggestion (bug_risk):** Regex may not match all valid ui:// resource URIs due to use of \S+.
The current pattern may fail for URIs with spaces or special characters. Please update the regex to handle a wider range of valid URI formats.
```suggestion
const uiResourceRegex = /ui:\/\/([^\s'"]+)/g;
```
</issue_to_address>
### Comment 2
<location> `src/tools/mcp-ui/utils/extractUIResources.ts:23-32` </location>
<code_context>
+**Changes:** Added 'mcp-ui' case to plugin rendering switch
+```typescript
+case 'mcp-ui': {
+ try {
+ const { extractUIResources } = require('@/tools/mcp-ui/utils/extractUIResources');
+ const { textContent, uiResources } = extractUIResources(content);
</code_context>
<issue_to_address>
**suggestion:** atob usage may throw for non-base64 strings, but fallback only checks for http URLs.
The current fallback may not handle non-base64, non-http URIs. Please consider expanding the fallback logic or add logging for unsupported formats.
</issue_to_address>
### Comment 3
<location> `src/tools/mcp-ui/Render/index.tsx:60` </location>
<code_context>
+
+### Iframe Sandbox Configuration
+```typescript
+sandbox="allow-scripts allow-same-origin allow-forms"
+```
+- **allow-scripts:** Enable JavaScript for interactive content
</code_context>
<issue_to_address>
**🚨 issue (security):** Allowing 'allow-same-origin' in sandbox may introduce security risks.
Review if 'allow-same-origin' is essential; removing it can reduce exposure to malicious iframe content.
</issue_to_address>
### Comment 4
<location> `src/tools/mcp-ui/Render/index.tsx:39-48` </location>
<code_context>
+ onLoad={(e) => {
</code_context>
<issue_to_address>
**nitpick:** Auto-resizing iframe based on content may fail for cross-origin data.
Document the cross-origin limitation and consider implementing a more reliable fallback for iframe height when content cannot be accessed.
</issue_to_address>
### Comment 5
<location> `src/services/mcp.ts:78-79` </location>
<code_context>
+
+ // If there are UI resources, encode them into the response string
+ if (uiResources.length > 0) {
+ // Encode UI resources as base64 JSON and append to content
+ const encodedResources = btoa(JSON.stringify({ uiResources }));
+
+ // Exclude verbose UI resource text from LLM context
</code_context>
<issue_to_address>
**issue (bug_risk):** btoa may throw for non-ASCII content in UI resources.
btoa will fail if UI resources include non-ASCII characters. Use a base64 encoding method that supports UTF-8, such as Buffer or a suitable library, to prevent runtime errors.
</issue_to_address>
### Comment 6
<location> `src/tools/mcp-ui/utils/extractUIResources.ts:67` </location>
<code_context>
const resource = item.resource;
</code_context>
<issue_to_address>
**suggestion (code-quality):** Prefer object destructuring when accessing and using properties. ([`use-object-destructuring`](https://docs.sourcery.ai/Reference/Rules-and-In-Line-Suggestions/TypeScript/Default-Rules/use-object-destructuring))
```suggestion
const {resource} = item;
```
<br/><details><summary>Explanation</summary>Object destructuring can often remove an unnecessary temporary reference, as well as making your code more succinct.
From the [Airbnb Javascript Style Guide](https://airbnb.io/javascript/#destructuring--object)
</details>
</issue_to_address>
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.
|
||
// Parse text content for ui:// prefixed resources | ||
if (textContent) { | ||
const uiResourceRegex = /ui:\/\/(\S+)/g; |
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.
suggestion (bug_risk): Regex may not match all valid ui:// resource URIs due to use of \S+.
The current pattern may fail for URIs with spaces or special characters. Please update the regex to handle a wider range of valid URI formats.
const uiResourceRegex = /ui:\/\/(\S+)/g; | |
const uiResourceRegex = /ui:\/\/([^\s'"]+)/g; |
try { | ||
// Decode if it's base64 encoded | ||
const decodedContent = atob(resourceUri); | ||
const parsedResource = JSON.parse(decodedContent); | ||
|
||
if (parsedResource.type && parsedResource.content) { | ||
uiResources.push({ | ||
content: parsedResource.content, | ||
metadata: parsedResource.metadata, | ||
type: parsedResource.type, |
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.
suggestion: atob usage may throw for non-base64 strings, but fallback only checks for http URLs.
The current fallback may not handle non-base64, non-http URIs. Please consider expanding the fallback logic or add logging for unsupported formats.
// Encode UI resources as base64 JSON and append to content | ||
const encodedResources = btoa(JSON.stringify({ uiResources })); |
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.
issue (bug_risk): btoa may throw for non-ASCII content in UI resources.
btoa will fail if UI resources include non-ASCII characters. Use a base64 encoding method that supports UTF-8, such as Buffer or a suitable library, to prevent runtime errors.
textContent += (textContent ? '\n' : '') + item.text; | ||
} else if (item.type === 'resource' && item.resource) { | ||
// Extract UI resource from MCP resource object | ||
const resource = item.resource; |
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.
suggestion (code-quality): Prefer object destructuring when accessing and using properties. (use-object-destructuring
)
const resource = item.resource; | |
const {resource} = item; |
Explanation
Object destructuring can often remove an unnecessary temporary reference, as well as making your code more succinct.From the Airbnb Javascript Style Guide
✨ MCP UI Integration for LobeChat
MCP UI extends the Model Context Protocol (MCP) by allowing server tool calls to return not just plain text, but also UI elements such as raw HTML or embeddable iFrame URLs. It further provides a communication channel between the client and embedded UIs via message passing.
Several popular clients—such as Postman and Goose by Block—already support this capability.
This PR brings equivalent MCP UI integration to LobeChat, enabling secure, interactive, and accessible experiences.
💻 变更类型 | Change Type
🔀 变更说明 | Description of Change
This PR adds comprehensive MCP (Model Context Protocol) UI integration support to LobeChat, enabling rich interactive content rendering from MCP servers.
💡 Key Features
🏗️ Architecture
Data Flow
Security Features
allow-scripts allow-same-origin allow-forms
📁 Files Changed
New Files (2)
src/tools/mcp-ui/Render/index.tsx
- Main UI renderer componentsrc/tools/mcp-ui/utils/extractUIResources.ts
- Resource extraction utilitiesModified Files (8)
src/services/mcp.ts
- UI resource processing & integrationsrc/features/PluginsUI/Render/index.tsx
- Added 'mcp-ui' render casesrc/features/Conversation/Messages/Assistant/Tool/Render/CustomRender.tsx
- MCP tool detectionpackages/types/src/tool/index.ts
- Extended LobeToolRenderTypepackages/types/src/message/tools.ts
- Added McpUIResource interfacepackages/database/src/schemas/message.ts
- Updated schema for mcp-ui typesrc/store/chat/slices/message/action.ts
- Database compatibility layersrc/store/chat/slices/plugin/action.ts
- Type transformation🧪 Testing
🔧 Implementation Highlights
📝 补充信息 | Additional Information
📸 Demo
Example MCP Server that has UI Resources as HTML
Name: mcp-aharvard
Transport Type: Streamable HTTP
URL: https://mcp-aharvard.netlify.app/mcp
Before MCP UI:

After MCP UI Integration:

✅ Summary
This PR introduces secure MCP UI rendering to LobeChat, enabling rich interactive content such as widgets, dashboards, and HTML-based components from MCP servers—while maintaining strict security boundaries and backward compatibility.
Summary by Sourcery
Add comprehensive MCP UI integration to LobeChat by extracting UI resources from server responses, securely rendering them as HTML/URL/iframe elements, and updating the tool pipeline, types, and storage without breaking existing functionality
New Features:
Enhancements:
Tests: