Skip to content

Conversation

@zerob13
Copy link
Collaborator

@zerob13 zerob13 commented Nov 24, 2025

image

Summary by CodeRabbit

  • New Features

    • Added MCP UI resource support for rendering and interacting with embedded UI elements in conversations.
    • Added export of MCP UI resources to markdown, HTML, and plain text outputs.
    • Integrated automatic extraction and inclusion of MCP UI resources from tool responses.
  • Localization

    • Added MCP UI interface strings across multiple languages (en, fa, fr, ja, ko, pt-BR, ru, zh-CN/HK/TW).

✏️ Tip: You can customize this high-level summary in your review settings.

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Nov 24, 2025

Walkthrough

Adds 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

Cohort / File(s) Change Summary
Type Definitions
src/shared/chat.d.ts, src/shared/types/core/chat.ts
Added mcp_ui_resource variant to AssistantMessageBlock and a mcp_ui_resource? payload with uri, restricted mimeType, optional text, blob, and _meta.
Tool call processing
src/main/presenter/threadPresenter/handlers/toolCallHandler.ts, src/main/presenter/threadPresenter/handlers/llmEventHandler.ts
Added processMcpUiResourcesFromToolCall to extract ui:// resources filtered by a MIME-type whitelist and append mcp_ui_resource blocks; wired call from LLM event handler. Includes error logging. (Note: method appears duplicated in file.)
Exporters
src/main/presenter/threadPresenter/exporters/conversationExporter.ts
Added handling for mcp_ui_resource blocks in markdown, HTML, and plain text exports (URI + MIME info, with safe fallbacks).
Renderer integration & main
src/renderer/src/main.ts, electron.vite.config.ts, package.json
Imported @mcp-ui/client/ui-resource-renderer.wc.js, added @mcp-ui/client devDependency, and updated Vue compilerOptions to treat tags starting with ui-resource-renderer as custom elements.
UI Component & message rendering
src/renderer/src/components/message/MessageBlockMcpUi.vue, src/renderer/src/components/message/MessageItemAssistant.vue
New MessageBlockMcpUi.vue component to render MCP UI resources, handle actions, loading/error states, and call presenter tools; MessageItemAssistant.vue updated to render the new block type.
Internationalization
src/renderer/src/i18n/*/chat.json
src/renderer/src/i18n/en-US/chat.json, .../fa-IR/chat.json, .../fr-FR/chat.json, .../ja-JP/chat.json, .../ko-KR/chat.json, .../pt-BR/chat.json, .../ru-RU/chat.json, .../zh-CN/chat.json, .../zh-HK/chat.json, .../zh-TW/chat.json
Added mcpUi translation object with keys title, badge, expand, and collapse across locales.

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
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

  • Attention areas:
    • MessageBlockMcpUi.vue: event subscription, serialization, error handling, cleanup on unmount.
    • toolCallHandler.ts: duplicate method occurrence and MIME whitelist correctness.
    • Type consistency between chat.d.ts and chat.ts for MIME types.
    • electron.vite.config.ts: custom element predicate correctness.

Possibly related PRs

Suggested labels

codex

Suggested reviewers

  • deepinfect

Poem

🐰 I hopped in quiet with a curious tweak,
ui:// crumbs and a renderer cheek to cheek,
Resources bloom, actions hum — a tiny feat,
Translations hop along, tidy and neat,
A rabbit cheers: MCP UI is sweet!

Pre-merge checks and finishing touches

✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'feat: add support for mcp ui' directly and accurately summarizes the main change—introducing MCP UI support across the codebase, as evidenced by new components, types, handlers, and translations.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
✨ Finishing touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch feat/add-mcp-ui

📜 Recent review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 7e1b429 and 6420f8f.

📒 Files selected for processing (2)
  • src/renderer/src/components/message/MessageBlockMcpUi.vue (1 hunks)
  • src/renderer/src/i18n/ja-JP/chat.json (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • src/renderer/src/components/message/MessageBlockMcpUi.vue
⏰ 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 (1)
src/renderer/src/i18n/ja-JP/chat.json (1)

84-89: Translations are appropriate and well-implemented.

The new mcpUi localization section uses correct Japanese UI terminology:

  • "折りたたむ" (oritate-mu) is the proper verb form for "collapse"
  • "展開" (tenkai) is an idiomatic noun/verb stem commonly used in Japanese UI labels for "expand"
  • The overall structure is consistent with the file's i18n conventions

The translations properly address the feedback from the previous review about using appropriate UI terms.


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.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Contributor

@coderabbitai coderabbitai bot left a 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-renderer as 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 labels

Current 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 labels

The 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 concise

Current 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: Align mcp_ui_resource typing with potential MIME variants

The new mcp_ui_resource block looks structurally aligned with MCP UI resources (uri + mimeType + text/blob + metadata), and adding 'mcp_ui_resource' to the type union is consistent with the existing pattern.

One concern: the mimeType field 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 mimeType to string with 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 uiResourceItems array 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 425px may not accommodate varying content sizes. Consider using min-height to 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

📥 Commits

Reviewing files that changed from the base of the PR and between 9fd63cf and 7e1b429.

📒 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.vue and 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), and threadId (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 prop

All 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.js and is imported with import '@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 processMcpUiResourcesFromToolCall method exists at src/main/presenter/threadPresenter/handlers/toolCallHandler.ts:178 with full implementation including error handling. The parameter msg (typed as LLMAgentEventData) passed from handleLLMAgentResponse at line 114 correctly matches the method's event parameter type. No issues found.

src/renderer/src/i18n/zh-HK/chat.json (1)

84-88: MCP UI translations look good for zh-HK

The mcpUi block 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 clear

The new mcpUi section 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 consistent

The mcpUi keys (展开 / 收起) 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 formatInlineHtml before 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_resource type definition is correctly integrated into the AssistantMessageBlock union 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 Set for 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 MCPResourceContent and 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 false to 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 handleUIAction method 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 addEventListener and direct property assignment is correctly implemented. This pattern likely supports different consumption methods by the ui-resource-renderer web component.

Consider extracting the type casting to a helper type or interface to reduce verbosity:

type UIResourceElement = HTMLElement & {
  onUIAction?: (action: UIActionResult) => Promise<unknown>
}

@zerob13 zerob13 merged commit 50b6f11 into dev Nov 24, 2025
2 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants