-
Notifications
You must be signed in to change notification settings - Fork 614
feat: add support for mcp ui #1120
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
WalkthroughAdds MCP UI resource support: new message block type, type updates, extraction from tool-call responses, renderer web-component integration, a Vue component to display resources, export formatting for MCP UI blocks, package dependency, and i18n entries across multiple locales. Changes
Sequence Diagram(s)sequenceDiagram
autonumber
participant Tool as Tool Call Response
participant Handler as ToolCallHandler
participant LLM as LLMEventHandler
participant MsgMgr as messageManager
participant Renderer as MessageBlockMcpUi
participant Presenter as mcpPresenter
Tool->>LLM: tool-call end event
LLM->>Handler: processMcpUiResourcesFromToolCall(state, msg, time)
activate Handler
Handler->>Handler: parse response, extract content items
Handler->>Handler: filter uri startsWith "ui://" and MIME whitelist
Handler->>MsgMgr: append mcp_ui_resource blocks to message
deactivate Handler
MsgMgr->>Renderer: render mcp_ui_resource block
activate Renderer
Renderer->>Renderer: mount <ui-resource-renderer> (web component)
Renderer->>Renderer: subscribe to onUIAction events
deactivate Renderer
Renderer->>Presenter: user triggers action -> callTool(...)
activate Presenter
Presenter-->>Renderer: return tool result
deactivate Presenter
Renderer->>Renderer: update UI / show result or error
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~25 minutes
Possibly related PRs
Suggested labels
Suggested reviewers
Poem
Pre-merge checks and finishing touches✅ Passed checks (3 passed)
✨ Finishing touches🧪 Generate unit tests (beta)
📜 Recent review detailsConfiguration used: CodeRabbit UI Review profile: CHILL Plan: Pro 📒 Files selected for processing (2)
🚧 Files skipped from review as they are similar to previous changes (1)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
🔇 Additional comments (1)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
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.
Actionable comments posted: 2
🧹 Nitpick comments (7)
electron.vite.config.ts (1)
82-89: Update comment to match implementation.The Chinese comment on line 85 states "将所有带短横线的标签名都视为自定义元素" (treat all tags with hyphens as custom elements), but the implementation only treats tags starting with
ui-resource-rendereras custom elements.Apply this diff to fix the comment:
vue({ template: { compilerOptions: { - // 将所有带短横线的标签名都视为自定义元素 + // 将 ui-resource-renderer 开头的标签视为自定义元素 isCustomElement: (tag) => tag.startsWith('ui-resource-renderer') } } }),src/renderer/src/i18n/fa-IR/chat.json (1)
84-88: Optional: Normalize Persian wording style for MCP UI labelsCurrent strings are understandable, but
"expand": "گسترش دهید"is an imperative form while most locales use neutral labels. If you prefer consistency with other buttons, something like"گسترش"or"باز کردن"might read more like a generic control label:- "expand": "گسترش دهید", + "expand": "گسترش",Purely a style choice; current text is functional.
src/renderer/src/i18n/fr-FR/chat.json (1)
84-88: Optional: Shorten badge and align capitalization in French MCP UI labelsThe current translations are understandable, but for visual consistency:
- Badge text
"Interface utilisateur"may be long for a small badge, where other locales use"UI"."collapse": "fermer"is lowercase, while"expand": "Développer"is capitalized.You might consider:
- "badge": "Interface utilisateur", - "collapse": "fermer", + "badge": "UI", + "collapse": "Fermer",if this better matches your design and other locales.
src/renderer/src/i18n/ru-RU/chat.json (1)
84-88: Optional: Make Russian MCP UI labels more button-like and conciseCurrent strings are understandable but a bit long/awkward for UI controls:
"badge": "пользовательский интерфейс"is quite long for a badge."collapse": "закрывать"/"expand": "Расширять"are infinitives; imperative or noun forms read more naturally on buttons.If you want more concise, idiomatic labels, consider something like:
- "badge": "пользовательский интерфейс", - "collapse": "закрывать", - "expand": "Расширять", + "badge": "UI", + "collapse": "Свернуть", + "expand": "Развернуть",Not mandatory, but likely a nicer UX.
src/shared/types/core/chat.ts (1)
48-59: Alignmcp_ui_resourcetyping with potential MIME variantsThe new
mcp_ui_resourceblock looks structurally aligned with MCP UI resources (uri + mimeType + text/blob + metadata), and adding'mcp_ui_resource'to thetypeunion is consistent with the existing pattern.One concern: the
mimeTypefield is currently a very narrow union:mimeType: 'text/html' | 'text/uri-list' | 'application/vnd.mcp-ui.remote-dom'If your MCP UI servers ever emit variants with parameters or suffixes (e.g., encoding/framework hints appended to the base type), this will be too strict and cause typing friction when you pass those resources through.
You might want to future‑proof this a bit, for example:
type McpUiMimeType = | 'text/html' | 'text/uri-list' | `application/vnd.mcp-ui.remote-dom${string}` mcp_ui_resource?: { uri: string mimeType: McpUiMimeType text?: string blob?: string _meta?: Record<string, unknown> }or even relax
mimeTypetostringwith a doc-comment if you prefer looser coupling to the upstream spec.Everything else in this type extension looks good.
Also applies to: 84-90
src/main/presenter/threadPresenter/handlers/toolCallHandler.ts (1)
217-220: Redundant MIME type validation.The MIME type validation at lines 217-220 is redundant since the
uiResourceItemsarray has already been filtered to include only items with valid MIME types at lines 193-204.Apply this diff to remove the redundant check:
const resource = item.resource if (!resource?.uri) { return null } - const mimeType = resource.mimeType || '' - if (!ToolCallHandler.MCP_UI_MIME_TYPES.has(mimeType)) { - return null - } - const typedMimeType = mimeType as + const typedMimeType = (resource.mimeType || '') as | 'text/html' | 'text/uri-list' | 'application/vnd.mcp-ui.remote-dom'src/renderer/src/components/message/MessageBlockMcpUi.vue (1)
164-176: Consider using min-height instead of fixed height.The fixed height of
425pxmay not accommodate varying content sizes. Consider usingmin-heightto allow the renderer to grow as needed while maintaining a minimum size.ui-resource-renderer { display: block; width: 100%; - height: 425px; + min-height: 425px; & > div { display: block; width: 100%; - height: 425px; + min-height: 425px; } }
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (20)
electron.vite.config.ts(1 hunks)package.json(1 hunks)src/main/presenter/threadPresenter/exporters/conversationExporter.ts(3 hunks)src/main/presenter/threadPresenter/handlers/llmEventHandler.ts(1 hunks)src/main/presenter/threadPresenter/handlers/toolCallHandler.ts(2 hunks)src/renderer/src/components/message/MessageBlockMcpUi.vue(1 hunks)src/renderer/src/components/message/MessageItemAssistant.vue(2 hunks)src/renderer/src/i18n/en-US/chat.json(1 hunks)src/renderer/src/i18n/fa-IR/chat.json(1 hunks)src/renderer/src/i18n/fr-FR/chat.json(1 hunks)src/renderer/src/i18n/ja-JP/chat.json(1 hunks)src/renderer/src/i18n/ko-KR/chat.json(1 hunks)src/renderer/src/i18n/pt-BR/chat.json(1 hunks)src/renderer/src/i18n/ru-RU/chat.json(1 hunks)src/renderer/src/i18n/zh-CN/chat.json(1 hunks)src/renderer/src/i18n/zh-HK/chat.json(1 hunks)src/renderer/src/i18n/zh-TW/chat.json(1 hunks)src/renderer/src/main.ts(1 hunks)src/shared/chat.d.ts(2 hunks)src/shared/types/core/chat.ts(2 hunks)
🧰 Additional context used
🧠 Learnings (1)
📚 Learning: 2025-10-12T00:58:06.513Z
Learnt from: zerob13
Repo: ThinkInAIXYZ/deepchat PR: 977
File: package.json:137-137
Timestamp: 2025-10-12T00:58:06.513Z
Learning: In this Electron project, renderer process dependencies (used only in src/renderer/) should be placed in devDependencies because they are bundled by electron-vite during the build process. Only main process dependencies (used in src/main/) need to be in the dependencies section.
Applied to files:
electron.vite.config.ts
🧬 Code graph analysis (1)
src/main/presenter/threadPresenter/handlers/toolCallHandler.ts (4)
src/main/presenter/threadPresenter/types.ts (1)
GeneratingMessageState(4-36)src/shared/types/presenters/legacy.presenters.d.ts (4)
LLMAgentEventData(1529-1529)MCPToolResponse(1316-1345)MCPContentItem(1353-1353)MCPResourceContent(1369-1379)src/shared/types/core/agent-events.ts (1)
LLMAgentEventData(4-37)src/shared/types/core/chat.ts (1)
AssistantMessageBlock(48-103)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
- GitHub Check: build-check (x64)
🔇 Additional comments (22)
package.json (1)
110-110: LGTM! Dependency placement follows project conventions.The @mcp-ui/client library is correctly placed in devDependencies since it's used in the renderer process (src/renderer/) and will be bundled by electron-vite during the build.
Based on learnings, renderer process dependencies should be in devDependencies in this Electron project.
src/renderer/src/i18n/pt-BR/chat.json (1)
84-89: LGTM! MCP UI translations added correctly.The new mcpUi namespace is properly structured with all required keys (badge, collapse, expand, title) and follows the existing i18n patterns in this file.
src/renderer/src/i18n/zh-TW/chat.json (1)
84-89: LGTM! Traditional Chinese translations added correctly.The mcpUi translations are properly structured and consistent with other locale files.
src/renderer/src/i18n/ko-KR/chat.json (1)
84-89: LGTM! Korean translations added correctly.The mcpUi namespace follows the established pattern and includes all required keys.
src/renderer/src/components/message/MessageItemAssistant.vue (2)
137-137: MessageBlockMcpUi component exists and is properly exported.The component file is located at
src/renderer/src/components/message/MessageBlockMcpUi.vueand uses Vue 3's<script setup>syntax, which automatically provides a valid default export. The import statement is correct.
62-67: Props correctly match MessageBlockMcpUi component definition.The component accepts
block(AssistantMessageBlock),messageId(optional string), andthreadId(optional string). These match the values passed from MessageItemAssistant.vue:
:block="block"→ block prop:message-id="currentMessage.id"→ messageId prop (Vue auto-converts kebab-case to camelCase):thread-id="currentThreadId"→ threadId propAll prop types align correctly with the component definition.
src/renderer/src/main.ts (1)
14-14: Import path is correct—no changes needed.The @mcp-ui/client package exports ui-resource-renderer.wc.js at
dist/ui-resource-renderer.wc.jsand is imported withimport '@mcp-ui/client/ui-resource-renderer.wc.js', which matches the code in main.ts. The import is correctly placed before i18n initialization.src/main/presenter/threadPresenter/handlers/llmEventHandler.ts (1)
112-115: Verification complete—the method is properly implemented and the call is correct.The
processMcpUiResourcesFromToolCallmethod exists atsrc/main/presenter/threadPresenter/handlers/toolCallHandler.ts:178with full implementation including error handling. The parametermsg(typed asLLMAgentEventData) passed fromhandleLLMAgentResponseat line 114 correctly matches the method'seventparameter type. No issues found.src/renderer/src/i18n/zh-HK/chat.json (1)
84-88: MCP UI translations look good for zh-HKThe
mcpUiblock is syntactically correct and the wording (收起/展開) is clear and idiomatic for a collapsible UI.src/renderer/src/i18n/en-US/chat.json (1)
73-78: en-US MCP UI block is consistent and clearThe new
mcpUisection is well-structured, with clear labels (Expand/Collapse) and consistent casing; no issues from a localization or data-shape perspective.src/renderer/src/i18n/zh-CN/chat.json (1)
73-78: zh-CN MCP UI translations are clear and consistentThe
mcpUikeys (展开/收起) match common UI phrasing and align with other locales; JSON shape looks correct.src/main/presenter/threadPresenter/exporters/conversationExporter.ts (3)
141-150: LGTM! Markdown export implementation is correct.The markdown export for MCP UI resources follows the established pattern and properly handles the URI and MIME type display.
371-381: LGTM! HTML export implementation is correct.The HTML export properly escapes the URI and MIME type using
formatInlineHtmlbefore rendering, which prevents XSS issues.
558-564: LGTM! Text export implementation is correct.The plain text export follows the established pattern and provides a clear, readable format for MCP UI resources.
src/shared/chat.d.ts (1)
94-94: LGTM! Type definition is well-structured.The new
mcp_ui_resourcetype definition is correctly integrated into theAssistantMessageBlockunion type. The MIME type constraint matches the whitelist used in the handler, ensuring type safety across the codebase.Also applies to: 121-127
src/main/presenter/threadPresenter/handlers/toolCallHandler.ts (3)
29-33: LGTM! MIME type whitelist is correctly defined.Using a
Setfor the whitelist provides O(1) lookup performance and the MIME types align with the TypeScript union type definition in the shared types.
193-204: LGTM! Resource filtering logic is robust.The type guard correctly narrows the type to
MCPResourceContentand validates all required conditions: resource type, URI prefix, and MIME type whitelist membership.
253-256: LGTM! Error handling is appropriate.The error handling properly catches exceptions, logs them for debugging, and returns
falseto indicate failure without disrupting the message flow.src/renderer/src/components/message/MessageBlockMcpUi.vue (4)
1-46: LGTM! Template structure is well-organized.The template properly handles all states (error, loading, success) with conditional rendering and provides clear visual feedback to users.
58-69: LGTM! Props and reactive state are correctly defined.The component props are properly typed, and the reactive state variables appropriately track the loading and error states.
87-123: LGTM! UI action handler is well-implemented.The
handleUIActionmethod properly validates inputs, manages loading state, handles errors, and uses a finally block to ensure cleanup.
134-161: Event listener management is correct but verbose.The dual approach of both
addEventListenerand direct property assignment is correctly implemented. This pattern likely supports different consumption methods by theui-resource-rendererweb component.Consider extracting the type casting to a helper type or interface to reduce verbosity:
type UIResourceElement = HTMLElement & { onUIAction?: (action: UIActionResult) => Promise<unknown> }
Summary by CodeRabbit
New Features
Localization
✏️ Tip: You can customize this high-level summary in your review settings.