Skip to content

fix: ai plugin and mcp issue#1586

Merged
chilingling merged 3 commits intoopentiny:release/v2.8.xfrom
hexqi:fix/mcp-issue
Aug 19, 2025
Merged

fix: ai plugin and mcp issue#1586
chilingling merged 3 commits intoopentiny:release/v2.8.xfrom
hexqi:fix/mcp-issue

Conversation

@hexqi
Copy link
Copy Markdown
Collaborator

@hexqi hexqi commented Aug 18, 2025

English | 简体中文

PR

PR Checklist

Please check if your PR fulfills the following requirements:

  • The commit message follows our Commit Message Guidelines
  • Tests for the changes have been added (for bug fixes / features)
  • Docs have been added / updated (for bug fixes / features)
  • Built its own designer, fully self-validated

PR Type

What kind of change does this PR introduce?

  • Bugfix
  • Feature
  • Code style update (formatting, local variables)
  • Refactoring (no functional changes, no api changes)
  • Build related changes
  • CI related changes
  • Documentation content changes
  • Other... Please describe:

Background and solution

优化代码,修复AI插件与MCP问题:

  • ✓ 全屏时MCP弹窗位置改为侧边
  • ✓ 支持title 中文显示
  • ✓ MCP弹窗改为直角样式与AI插件保持一致
  • ✓ MCP icon样式优化
  • ✓ 支持markdown样式
  • ✓ 代码块高亮
  • ✓ 字体大小不一致,统一为14px
  • ✓ 自动滚动
  • ✓ 第二次对话报错问题
  • ✓ 有tool场景markdown未渲染
  • ✓ markdown表格样式、列表样式
  • ✓ 添加loading效果
  • ✓ 优化部分样式

What is the current behavior?

Issue Number: N/A

What is the new behavior?

Does this PR introduce a breaking change?

  • Yes
  • No

Other information

Summary by CodeRabbit

  • New Features

    • Themeable markdown renderer with syntax highlighting and a small loading indicator.
    • Unified chat container exposing a scrollable container and a content-renderer map for richer render modes.
  • Improvements

    • Container-based auto-scroll during messages and tool interactions.
    • MCP panel positioning adapts to layout and is configurable via a public position prop.
    • Assistant messages support structured render content while keeping final message text concise.
  • Style

    • Improved list/table/code styling, resized MCP button, refined picker visuals; active-count badge removed.

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Aug 18, 2025

Walkthrough

Consolidates chat UI into a single scrollable container with exposed chatContainerRef and mcpDrawerPosition; switches message rendering to incremental renderContent entries and contentRenderers (including LoadingRenderer); updates Markdown rendering to hljs core + markdown-it with theme support; adds MCP position prop; removes mock MCP servers and adjusts tool metadata/types.

Changes

Cohort / File(s) Summary
Chat container & public API
packages/plugins/robot/src/Main.vue
Consolidates welcome and messages under robot-chat-container-content with chatContainerRef; exposes chatContainerRef, mcpDrawerPosition, and contentRenderers from setup; replaces message-renderers with :content-renderers; wires auto-scroll watchers using chatContainerRef; binds :position="mcpDrawerPosition" to mcp-server; assistant role adds customContentField: 'renderContent'.
Markdown renderer (theming & highlighting)
packages/plugins/robot/src/mcp/MarkdownRenderer.vue
Replaces global highlight.js with highlight.js/lib/core, registers languages, adds theme/content/options props, builds markdown-it with a custom highlight using hljs and DOMPurify, and applies non-scoped theme CSS classes for light/dark.
MCP server picker & popup positioning
packages/plugins/robot/src/mcp/McpServer.vue
Removes internal drawerConfig and activeCount badge; introduces public position prop (PopupConfig) with defaults; binds picker :popup-config="props.position" and nests under .robot-mcp-server-picker; adjusts styles and button sizing; internal activeCount default changed to 1.
Types: messages & tool metadata
packages/plugins/robot/src/mcp/types.ts
RobotMessage.content becomes string; adds renderContent?: BubbleContentItem[]; adds optional title?: string to RequestTool.function and McpTool.
MCP server/tool state management
packages/plugins/robot/src/mcp/useMcp.ts
Removes mock servers and toolCount; keeps only engine MCP server; updateServerTools no longer tracks toolCount; tool display name composed from optional title + name.
LLM/message pipeline & rendering
packages/plugins/robot/src/mcp/utils.ts
Adds formatMessages() to produce { role, content } for LLM; accumulates incremental outputs in message.renderContent (with statused entries and loading placeholders) instead of mutating content; handles tool-call loops by appending tool call/result entries and re-fetching LLM; after finalization copies last renderContent to messages.at(-1)!.content; sendMcpRequest sends formatted messages.
Tool identifier tweak
packages/plugins/page/src/mcp/tools/editSpecificPage.ts
Changes exported tool name from 'Edit_page_in_canvas.' to 'edit_page_in_canvas'.
New loading renderer
packages/plugins/robot/src/mcp/LoadingRenderer.vue
Adds a template-only component rendering a small loading image used by content renderers during streaming/loading.

Sequence Diagram(s)

sequenceDiagram
  participant U as User
  participant Main as Main.vue (UI)
  participant Utils as mcp/utils.ts
  participant LLM as LLM
  participant Tool as MCP Tool

  Note over Main: User submits prompt → message appended with renderContent=[loading]
  U->>Main: submit prompt
  Main->>Utils: sendMcpRequest(messages)
  Utils->>LLM: fetchLLM(formatMessages(messages))
  LLM-->>Utils: assistant response (may include tool call or markdown)
  alt assistant requests tool
    Utils->>Tool: execute(toolCall)
    Tool-->>Utils: tool result
    Utils->>Utils: append tool result to message.renderContent (status=success)
    Utils->>LLM: fetchLLM(with tool messages)
    LLM-->>Utils: assistant follow-up
  end
  Utils->>Main: update message.renderContent incrementally (uses LoadingRenderer)
  Main->>Main: auto-scroll via chatContainerRef (nextTick)
  Utils->>Main: finalize messages.at(-1)!.content from last renderContent entry and remove internal renderContent
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Poem

I hop where bubbles stack and flow,
Little loaders blink and glow.
Markdown warms in dark and light,
Tools pop in and hop out right —
I thump, content renders — what a show. 🐇✨

Tip

🔌 Remote MCP (Model Context Protocol) integration is now available!

Pro plan users can now connect to remote MCP servers from the Integrations page. Connect with popular remote MCPs such as Notion and Linear to add more context to your reviews and chats.


📜 Recent review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

💡 Knowledge Base configuration:

  • MCP integration is disabled by default for public repositories
  • Jira integration is disabled by default for public repositories
  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between 6ac1c7e and 3df7871.

📒 Files selected for processing (1)
  • packages/plugins/robot/src/mcp/MarkdownRenderer.vue (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • packages/plugins/robot/src/mcp/MarkdownRenderer.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: push-check
✨ Finishing Touches
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment

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
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.

Support

Need help? Create a ticket on our support page for assistance with any issues or questions.

CodeRabbit Commands (Invoked using PR/Issue comments)

Type @coderabbitai help to get the list of available commands.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Status, Documentation and Community

  • Visit our Status Page to check the current availability of CodeRabbit.
  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

@github-actions github-actions Bot added bug Something isn't working release merge to release/ branch, before release period labels Aug 18, 2025
Copy link
Copy Markdown
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: 5

🔭 Outside diff range comments (2)
packages/plugins/robot/src/mcp/utils.ts (2)

63-65: Sanitize history messages before subsequent LLM calls

In tool-call rounds, you pass raw historyMessages (which can include UI-only fields like renderContent) to fetchLLM. Use formatMessages(...) here too to avoid leaking non-spec fields to the LLM API.

-    const historyMessages = contextMessages?.length ? contextMessages : toRaw(messages.slice(0, -1))
-    const toolMessages: LLMMessage[] = [...historyMessages, res.choices[0].message] as LLMMessage[]
+    const history = contextMessages?.length ? contextMessages : messages.slice(0, -1)
+    const historyForLLM = formatMessages(history as unknown as LLMMessage[])
+    const toolMessages: LLMMessage[] = [...historyForLLM, res.choices[0].message]

107-121: Type mismatch and unsafe assignment to content

  • sendMcpRequest takes LLMMessage[], but later uses renderContent which is only on RobotMessage. This will not type-check and risks runtime errors.
  • Assigning messages.at(-1)!.content from renderContent.at(-1)!.content can assign a non-string (e.g., a tool result object) when the final assistant message has no markdown content.

Update the function to accept RobotMessage[], and safely derive the final string content:

-export const sendMcpRequest = async (messages: LLMMessage[], options: RequestOptions = {}) => {
+export const sendMcpRequest = async (messages: RobotMessage[], options: RequestOptions = {}) => {
   if (messages.length < 1) {
     return
   }
   const tools = await useMcpServer().getLLMTools()
   requestOptions = options
-  const res = await fetchLLM(formatMessages(messages.slice(0, -1)), tools, options)
+  const res = await fetchLLM(formatMessages(messages.slice(0, -1) as unknown as LLMMessage[]), tools, options)
   const hasToolCall = res.choices[0].message.tool_calls?.length > 0
   if (hasToolCall) {
     await handleToolCall(res, tools, messages)
-    messages.at(-1)!.content = messages.at(-1)!.renderContent.at(-1)!.content
+    const lastMsg = messages.at(-1)!
+    const lastItem = lastMsg.renderContent?.at(-1)
+    if (lastItem && lastItem.type === 'markdown' && typeof lastItem.content === 'string') {
+      lastMsg.content = lastItem.content
+    } else if (typeof res.choices[0].message.content === 'string') {
+      lastMsg.content = res.choices[0].message.content
+    } else {
+      lastMsg.content = ''
+    }
   } else {
     messages.at(-1)!.content = res.choices[0].message.content
   }
 }
🧹 Nitpick comments (14)
packages/plugins/robot/src/mcp/types.ts (1)

14-14: Avoid adding non-spec fields to OpenAI tool definitions

RequestTool.function.title is not part of the OpenAI tool/function-call schema. Keeping it in the type can mislead future code to serialize and send it, causing API rejections. Since we don’t actually populate it in convertMCPToOpenAITools, prefer removing it (or clearly documenting it as “UI-only, never serialized”).

Apply this diff to drop the field:

 export interface RequestTool {
   type: 'function'
   function: {
     name: string
     description: string
-    title?: string
     parameters: {
       type: 'object'
       required?: string[]
       properties: Record<
packages/plugins/robot/src/mcp/useMcp.ts (2)

58-58: Coerce isToolsEnabled to a strict boolean

some(...) may never run (undefined chaining), yielding undefined. Coerce to boolean to avoid ternary/boolean logic surprises in consumers.

-const isToolsEnabled = computed(() => getEngineServer()?.tools?.some((tool) => tool.enabled))
+const isToolsEnabled = computed(() => !!getEngineServer()?.tools?.some((tool) => tool.enabled))

81-90: Prevent duplicate server entries when adding

Given inUseMcpServers is initialized with the engine server, pushing again on “add” can create duplicates if the UI ever fires plugin-add for it. Guard against duplicates.

   if (added) {
-    const newServer: PluginInfo = {
+    const newServer: PluginInfo = {
       ...server,
       id: server.id || `mcp-server-${Date.now()}`,
       enabled: true,
       added: true,
       expanded: false,
       tools: server.tools || []
     }
-    inUseMcpServers.value.push(newServer)
+    if (!inUseMcpServers.value.some((s) => s.id === newServer.id)) {
+      inUseMcpServers.value.push(newServer)
+    }
packages/plugins/robot/src/mcp/utils.ts (1)

34-40: Narrow the type of parseArgs to reduce accidental misuse

When JSON.parse fails, you return the original string. That string is later sent to callTool, which declares args: Record<string, unknown>. Consider returning unknown and normalizing to an object to keep the wire format consistent, or update callTool’s signature to accept unknown.

For example:

-const parseArgs = (args: string) => {
+const parseArgs = (args: string): unknown => {
   try {
     return JSON.parse(args)
   } catch (error) {
-    return args
+    // Fallback: preserve the raw string in a stable envelope
+    return { __raw: args }
   }
}

And adapt tool invocation/rendering accordingly.

packages/plugins/robot/src/mcp/MarkdownRenderer.vue (2)

75-84: Avoid querying the whole document when highlighting

Re-highlighting on theme change is fine, but searching document may touch unrelated code blocks. Limit to this component’s root via a ref.

Example (outside selected lines for clarity):

  • Template: <div ref="root" ... />
  • Script:
    • const root = ref<HTMLElement>()
    • root.value?.querySelectorAll('pre code')...
      If you want, I can provide a concrete patch.

Also applies to: 86-93


31-44: Minor typing improvement for theme prop

Use PropType for stricter typing, avoids relying on a function-cast to String.

+import type { PropType } from 'vue'
...
-  theme: {
-    type: String as () => 'light' | 'dark',
+  theme: {
+    type: String as PropType<'light' | 'dark'>,
     default: 'dark'
   },
packages/plugins/robot/src/Main.vue (8)

83-83: Import nextTick (used by scrollContent/watch fix)

Needed for reliable post-DOM update scrolling.

-import { ref, onMounted, watchEffect, type CSSProperties, h, resolveComponent, computed, watch } from 'vue'
+import { ref, onMounted, watchEffect, type CSSProperties, h, resolveComponent, computed, watch, nextTick } from 'vue'

56-58: Avoid double auto-scroll strategies

You both:

  • Manually scroll via chatContainerRef, and
  • Use TrBubbleList’s autoScroll.

These can fight each other or cause redundant work. Prefer one approach.

If you keep the container-based approach, remove autoScroll:

-<tr-bubble-list :items="activeMessages" :roles="roles" autoScroll></tr-bubble-list>
+<tr-bubble-list :items="activeMessages" :roles="roles"></tr-bubble-list>

39-59: Make the container actually scrollable

Since scrollContent scrolls robot-chat-container-content, ensure it’s a scroll container. Otherwise, setting scrollTop has no effect.

Add CSS (outside selected lines) to the scoped style:

.robot-chat-container-content {
  display: flex;
  flex-direction: column;
  min-height: 0;  /* allow child to shrink in flex context */
  height: 100%;
  overflow: auto;
}

Please verify visually that:

  • The content area gets a scrollbar when overflowed,
  • scrollContent moves to bottom as messages stream in.

205-223: Unify loading state across MCP and HTTP flows; always scroll after completion

requestLoading is only toggled for the MCP path, leading to inconsistent UX. Also, scrolling after completion is useful in the HTTP path too.

Suggestion (outside selected lines; illustrative):

// At function start:
requestLoading.value = true

// In HTTP branch:
getMetaApi(META_SERVICE.Http)
  .post(...)
// existing then/catch...
  .finally(async () => {
    inProcesing.value = false
    requestLoading.value = false
    await scrollContent()
  })

This keeps the sender’s loading UI consistent and ensures the viewport stays pinned to the bottom in both flows.


128-129: Type the ref for better TS safety

Minor, but helpful for IDE/TS:

-const chatContainerRef = ref(null)
+const chatContainerRef = ref<HTMLElement | null>(null)

198-201: Normalize message helpers to the new shape

Both getAiRespMessage and getMessage can provide renderContent by default to simplify downstream rendering logic.

 // getAiRespMessage
-const getAiRespMessage = (role = 'assistant', content) => ({
-  role,
-  content
-})
+const getAiRespMessage = (role = 'assistant', content) => ({
+  role,
+  content,
+  renderContent: content
+})

 // getMessage
-const getMessage = (content) => ({
-  role: 'user',
-  content
-})
+const getMessage = (content) => ({
+  role: 'user',
+  content,
+  renderContent: content
+})

If you intend only assistant to use renderContent, skip the user change; otherwise it keeps things uniform and MarkdownRenderer can still handle both roles consistently.

Also applies to: 266-269


436-446: Add CSS var fallback for drawer width; verify variable scope

When not fullscreen you use right: 'var(--tr-container-width)'. If the popup is appended to body, that CSS var may be undefined outside the container scope.

Apply this diff to provide a robust fallback:

-          ...(fullscreen.value ? { left: 0 } : { right: 'var(--tr-container-width)' })
+          ...(fullscreen.value ? { left: 0 } : { right: 'var(--tr-container-width, 400px)' })

Please verify in devtools that the MCP drawer aligns flush with the container edge in both fullscreen and non-fullscreen modes.


134-135: Typo in asset filename: defaultAvator → defaultAvatar
Our repo-wide search confirms the file and all references are consistently spelled “defaultAvator.png,” and there is no “defaultAvatar.png.” To correct this typo and avoid future confusion, please:

• Rename the asset

  • packages/design-core/public/img/defaultAvator.png → defaultAvatar.png
    • Update every import/path
  • packages/toolbars/collaboration/src/Main.vue (watchEffect at line 78; userHead at line 84)
  • packages/plugins/robot/src/Main.vue (avatarUrl.value at line 134)

Once renamed, rebuild or restart your dev server to verify the image loads as expected.

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

💡 Knowledge Base configuration:

  • MCP integration is disabled by default for public repositories
  • Jira integration is disabled by default for public repositories
  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between 27195a0 and 94b6cde.

📒 Files selected for processing (6)
  • packages/plugins/robot/src/Main.vue (12 hunks)
  • packages/plugins/robot/src/mcp/MarkdownRenderer.vue (1 hunks)
  • packages/plugins/robot/src/mcp/McpServer.vue (4 hunks)
  • packages/plugins/robot/src/mcp/types.ts (3 hunks)
  • packages/plugins/robot/src/mcp/useMcp.ts (2 hunks)
  • packages/plugins/robot/src/mcp/utils.ts (6 hunks)
🧰 Additional context used
🧠 Learnings (2)
📚 Learning: 2025-01-15T02:19:06.755Z
Learnt from: yy-wow
PR: opentiny/tiny-engine#940
File: packages/canvas/DesignCanvas/src/DesignCanvas.vue:0-0
Timestamp: 2025-01-15T02:19:06.755Z
Learning: In Vue components using message subscriptions from opentiny/tiny-engine-meta-register, always clean up subscriptions in the onUnmounted hook using useMessage().unsubscribe() to prevent memory leaks.

Applied to files:

  • packages/plugins/robot/src/Main.vue
📚 Learning: 2025-01-14T06:49:00.797Z
Learnt from: gene9831
PR: opentiny/tiny-engine#1011
File: packages/configurator/src/router-select-configurator/RouterSelectConfigurator.vue:63-73
Timestamp: 2025-01-14T06:49:00.797Z
Learning: In the tiny-engine project, the SvgIcon component is globally registered and available throughout Vue components without requiring explicit imports.

Applied to files:

  • packages/plugins/robot/src/Main.vue
🧬 Code Graph Analysis (2)
packages/plugins/robot/src/mcp/useMcp.ts (2)
packages/register/src/common.ts (1)
  • getMetaApi (20-30)
packages/register/src/constants.ts (1)
  • META_SERVICE (1-23)
packages/plugins/robot/src/mcp/utils.ts (1)
packages/plugins/robot/src/mcp/types.ts (1)
  • LLMMessage (30-34)
🔇 Additional comments (7)
packages/plugins/robot/src/mcp/types.ts (2)

38-40: Model shift to structured rendering looks good

Changing RobotMessage.content to string and introducing renderContent?: BubbleContentItem[] aligns with the new rendering pipeline. This pairs correctly with Main.vue’s customContentField: 'renderContent'.


71-71: Adding title to McpTool improves UI/UX for tool display

Optional title enables localized or friendly naming while preserving name as the stable identifier. Good addition.

packages/plugins/robot/src/mcp/useMcp.ts (1)

26-33: Tool display name formatting is sensible

Preferring title(name) when available reads well in Chinese UI while keeping the unique name visible. No issues.

packages/plugins/robot/src/mcp/utils.ts (1)

9-16: Formatting outbound messages before LLM calls is correct

The new formatMessages strips UI-only fields (e.g., renderContent) before sending to the LLM. Good defensive step.

packages/plugins/robot/src/mcp/McpServer.vue (1)

8-21: Popup positioning prop passthrough aligns with Main.vue

Binding :popup-config="props.position" is the right hook for externalizing drawer placement. Once the default fix above is applied, this is good to go.

If you want, I can scan for all usages to ensure they pass a position prop where necessary and won’t rely on the broken default. Let me know and I’ll generate a script.

packages/plugins/robot/src/Main.vue (2)

71-71: LGTM: mcp-server now accepts a position prop

Binding mcpDrawerPosition keeps the popup aligned with the container. Nice.


519-550: LGTM: Markdown/table/list styling upgrades

The table borders, zebra striping, hover effect, and list padding greatly improve readability. Font-size unification to 14px matches the PR goals.

Comment thread packages/plugins/robot/src/Main.vue Outdated
Comment thread packages/plugins/robot/src/Main.vue
Comment thread packages/plugins/robot/src/Main.vue Outdated
Comment thread packages/plugins/robot/src/mcp/MarkdownRenderer.vue Outdated
Comment thread packages/plugins/robot/src/mcp/McpServer.vue
Copy link
Copy Markdown
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: 1

🔭 Outside diff range comments (4)
packages/plugins/robot/src/mcp/utils.ts (1)

63-80: Bug: toolMessages constructed from raw messages; include only role/content and preserve tool_calls

Currently historyMessages (RobotMessage[]) are spread directly into toolMessages, which leaks non-LLM fields (e.g., renderContent) into the backend. Also, res.choices[0].message is pushed as-is with optional role. Shape should be normalized: use formatMessages for history and construct an assistant message preserving tool_calls.

Apply this diff:

-    const historyMessages = contextMessages?.length ? contextMessages : toRaw(messages.slice(0, -1))
-    const toolMessages: LLMMessage[] = [...historyMessages, res.choices[0].message] as LLMMessage[]
+    const historyMessages = contextMessages?.length ? contextMessages : toRaw(messages.slice(0, -1))
+    const baseMessages = formatMessages(historyMessages as unknown as LLMMessage[])
+    const assistantMsg: LLMMessage = {
+      role: (res.choices[0].message.role as string) || 'assistant',
+      content: res.choices[0].message.content || '',
+      tool_calls: res.choices[0].message.tool_calls
+    }
+    const toolMessages: LLMMessage[] = [...baseMessages, assistantMsg]
@@
-      const toolCallResult = await useMcpServer().callTool(name, parsedArgs)
+      const toolCallResult = await useMcpServer().callTool(name, parsedArgs)
       toolMessages.push({
         type: 'text',
-        content: toolCallResult.content,
+        content: typeof toolCallResult?.content === 'string' ? toolCallResult.content : JSON.stringify(toolCallResult ?? {}),
         role: 'tool',
         tool_call_id: tool.id
       })
@@
-    const newResp = await fetchLLM(toolMessages, tools)
+    const newResp = await fetchLLM(toolMessages, tools)

Also applies to: 92-95

packages/plugins/robot/src/mcp/useMcp.ts (1)

124-129: Ensure listTools always returns an McpListToolsResponse to match its signature
The current implementation can resolve to undefined due to optional chaining, violating the Promise<McpListToolsResponse> return type. Wrap the call in an await and provide a { tools: [] } fallback.

• packages/plugins/robot/src/mcp/useMcp.ts – update the listTools function

-const listTools = async (): Promise<McpListToolsResponse> =>
-  getMetaApi(META_SERVICE.McpService)?.getMcpClient()?.listTools()
+const listTools = async (): Promise<McpListToolsResponse> => {
+  const resp = await getMetaApi(META_SERVICE.McpService)
+    ?.getMcpClient()
+    ?.listTools()
+  return resp ?? { tools: [] }
+}
packages/plugins/robot/src/Main.vue (2)

205-224: Bug: computed used as truthy object; must use .value; also mirror renderContent on errors

  • if (useMcpServer().isToolsEnabled) always enters the MCP branch; should be .isToolsEnabled.value.
  • In catch/finally paths, update renderContent so assistant bubble renders error text.

Apply this diff:

-  if (useMcpServer().isToolsEnabled) {
+  if (useMcpServer().isToolsEnabled.value) {
     try {
       requestLoading.value = true
       await scrollContent() // eslint-disable-line
       await sendMcpRequest(messages.value, {
         model: selectedModel.value.value,
         headers: {
           Authorization: `Bearer ${tokenValue.value || import.meta.env.VITE_API_TOKEN}`
         }
       })
     } catch (error) {
-      messages.value[messages.value.length - 1].content = '连接失败'
+      const failed = '连接失败'
+      const last = messages.value[messages.value.length - 1]
+      last.content = failed
+      last.renderContent = [{ type: 'markdown', content: failed }]
     } finally {
       inProcesing.value = false
       requestLoading.value = false
       await scrollContent() // eslint-disable-line
     }
     return
   }

233-246: Mirror renderContent in HTTP branch success/error paths

Keeps assistant rendering consistent with customContentField='renderContent'.

-          messages.value[messages.value.length - 1].content = replyWithoutCode
+          const last = messages.value[messages.value.length - 1]
+          last.content = replyWithoutCode
+          last.renderContent = [{ type: 'markdown', content: replyWithoutCode }]
@@
-        .catch(() => {
-          messages.value[messages.value.length - 1].content = '连接失败'
+        .catch(() => {
+          const failed = '连接失败'
+          const last = messages.value[messages.value.length - 1]
+          last.content = failed
+          last.renderContent = [{ type: 'markdown', content: failed }]
♻️ Duplicate comments (5)
packages/plugins/robot/src/Main.vue (5)

83-83: Import nextTick for robust scrolling (paired with scroll fix below)

Add nextTick to ensure DOM updates before scrolling.

Apply this diff:

-import { ref, onMounted, watchEffect, type CSSProperties, h, resolveComponent, computed, watch } from 'vue'
+import { ref, onMounted, watchEffect, type CSSProperties, h, resolveComponent, computed, watch, nextTick } from 'vue'

249-253: Ensure DOM update before scrolling; improve scroll robustness

Await nextTick and use a local var.

-const scrollContent = async () => {
-  if (chatContainerRef.value) {
-    chatContainerRef.value.scrollTop = chatContainerRef.value.scrollHeight
-  }
-}
+const scrollContent = async () => {
+  await nextTick()
+  const el = chatContainerRef.value as HTMLElement | null
+  if (el) {
+    el.scrollTop = el.scrollHeight
+  }
+}

306-306: Assistant placeholder must set renderContent; remove stray name prop

Without renderContent, the assistant bubble can appear blank.

-messages.value.push({ role: 'assistant', content: '好的,正在执行相关操作,请稍等片刻...', name: 'AI' })
+messages.value.push({
+  role: 'assistant',
+  content: '好的,正在执行相关操作,请稍等片刻...',
+  renderContent: [{ type: 'markdown', content: '好的,正在执行相关操作,请稍等片刻...' }]
+})

432-435: watch sources are non-reactive; callback won’t re-run

Pass functions (reactive getters) instead of evaluated values.

-watch([activeMessages.value.length, activeMessages.value.at(-1)?.renderContent?.length], async () => {
-  await scrollContent()
-})
+watch(
+  [() => activeMessages.value.length, () => activeMessages.value.at(-1)?.renderContent?.length ?? 0],
+  async () => {
+    await scrollContent()
+  }
+)

198-201: Default assistant message should include renderContent (markdown item)

Prevents empty assistant bubbles when customContentField='renderContent'.

-const getAiRespMessage = (role = 'assistant', content) => ({
-  role,
-  content
-})
+const getAiRespMessage = (role = 'assistant', content: string) => ({
+  role,
+  content,
+  renderContent: [{ type: 'markdown', content }]
+})
🧹 Nitpick comments (5)
packages/plugins/robot/src/mcp/types.ts (2)

14-14: Good addition: optional title on RequestTool.function

This enables localized/display-friendly tool names. Ensure it’s propagated when converting MCP tools to OpenAI tools (see convertMCPToOpenAITools in useMcp.ts).


71-72: Good: McpTool gains optional title

Matches UI’s “title(name)” display. Also consider carrying this into RequestTool.function.title during conversion so LLM UIs can surface friendlier labels.

packages/plugins/robot/src/mcp/utils.ts (1)

113-118: Correctly formats initial LLM call; minor safety on final content assignment

Initial call uses formatMessages — good. For the final assignment below, guard against non-markdown last entries.

Apply this diff to pick the last markdown item’s content or fallback to the plain response:

-    messages.at(-1)!.content = messages.at(-1)!.renderContent.at(-1)!.content
+    const rc = messages.at(-1)!.renderContent || []
+    const lastMarkdown = [...rc].reverse().find((it: any) => it?.type === 'markdown')
+    messages.at(-1)!.content = lastMarkdown?.content ?? res.choices?.[0]?.message?.content ?? ''
packages/plugins/robot/src/mcp/useMcp.ts (1)

37-52: Propagate title into OpenAI tool function metadata

You’ve added title to types but it’s not forwarded. This helps downstream UIs and logs.

Apply this diff:

   return mcpTools.map((tool: McpTool) => ({
     type: 'function',
     function: {
       name: tool.name,
-      description: tool.description || '',
+      description: tool.description || '',
+      title: tool.title,
       parameters: {
         type: 'object',
         properties: Object.fromEntries(
           Object.entries(tool.inputSchema?.properties || {}).map(([key, prop]: [string, any]) => [key, { ...prop }])
         ),
         required: tool.inputSchema?.required || []
       }
     }
   })) as RequestTool[]
packages/plugins/robot/src/Main.vue (1)

128-129: Type chatContainerRef for safer DOM access

Avoid implicit any and enable editor intellisense.

-const chatContainerRef = ref(null)
+const chatContainerRef = ref<HTMLElement | null>(null)
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

💡 Knowledge Base configuration:

  • MCP integration is disabled by default for public repositories
  • Jira integration is disabled by default for public repositories
  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between 94b6cde and d7c3a1f.

📒 Files selected for processing (6)
  • packages/plugins/robot/src/Main.vue (12 hunks)
  • packages/plugins/robot/src/mcp/MarkdownRenderer.vue (1 hunks)
  • packages/plugins/robot/src/mcp/McpServer.vue (4 hunks)
  • packages/plugins/robot/src/mcp/types.ts (3 hunks)
  • packages/plugins/robot/src/mcp/useMcp.ts (2 hunks)
  • packages/plugins/robot/src/mcp/utils.ts (6 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
  • packages/plugins/robot/src/mcp/McpServer.vue
  • packages/plugins/robot/src/mcp/MarkdownRenderer.vue
🧰 Additional context used
🧠 Learnings (2)
📚 Learning: 2025-01-15T02:19:06.755Z
Learnt from: yy-wow
PR: opentiny/tiny-engine#940
File: packages/canvas/DesignCanvas/src/DesignCanvas.vue:0-0
Timestamp: 2025-01-15T02:19:06.755Z
Learning: In Vue components using message subscriptions from opentiny/tiny-engine-meta-register, always clean up subscriptions in the onUnmounted hook using useMessage().unsubscribe() to prevent memory leaks.

Applied to files:

  • packages/plugins/robot/src/Main.vue
📚 Learning: 2025-01-14T06:49:00.797Z
Learnt from: gene9831
PR: opentiny/tiny-engine#1011
File: packages/configurator/src/router-select-configurator/RouterSelectConfigurator.vue:63-73
Timestamp: 2025-01-14T06:49:00.797Z
Learning: In the tiny-engine project, the SvgIcon component is globally registered and available throughout Vue components without requiring explicit imports.

Applied to files:

  • packages/plugins/robot/src/Main.vue
🧬 Code Graph Analysis (2)
packages/plugins/robot/src/mcp/useMcp.ts (2)
packages/register/src/common.ts (1)
  • getMetaApi (20-30)
packages/register/src/constants.ts (1)
  • META_SERVICE (1-23)
packages/plugins/robot/src/mcp/utils.ts (1)
packages/plugins/robot/src/mcp/types.ts (1)
  • LLMMessage (30-34)
⏰ 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: push-check
🔇 Additional comments (8)
packages/plugins/robot/src/mcp/utils.ts (3)

9-16: formatMessages: simple, correct shaping for LLM input

Keeps only role/content which most backends expect.


52-60: Render pipeline initialization looks correct

Initializing renderContent and appending initial markdown content is consistent with the new Bubble renderer.


86-91: Status and result plumbing looks good

Marks the last tool block as success and replaces content with params/result bundle — aligns with renderers expecting structured tool output.

packages/plugins/robot/src/mcp/useMcp.ts (1)

26-34: UI tool naming via optional title is solid

Displaying “title(name)” improves clarity while preserving identity.

packages/plugins/robot/src/Main.vue (4)

39-59: Unified scrollable container and conditional rendering look good

Single container with welcome and message states improves UX and makes auto-scroll easier to reason about.


422-429: roles config aligns with new render pipeline

Using MarkdownRenderer and customContentField='renderContent' is consistent with the types and utils changes.


436-445: mcpDrawerPosition computed is sensible

Anchoring left in fullscreen and right to container otherwise matches the PR objective “全屏时 MCP 弹窗位置改为侧边”.


519-550: Markdown/table/list styling update looks good

Readable defaults (14px, paddings, table borders/zebra) align with “支持 markdown 样式 / 代码块高亮 / 字体大小统一为 14px”.

Comment thread packages/plugins/robot/src/mcp/types.ts
Comment thread packages/plugins/robot/src/Main.vue
Comment thread packages/plugins/robot/src/Main.vue Outdated
Comment thread packages/plugins/robot/src/Main.vue Outdated
Comment thread packages/plugins/robot/src/mcp/useMcp.ts Outdated
Copy link
Copy Markdown
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: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
packages/plugins/robot/src/mcp/useMcp.ts (1)

90-93: Do not force-enable all tools on server add; respect server-reported statuses

Calling updateEngineServer(newServer, added) after updateEngineTools() will enable every tool regardless of its reported status. This contradicts the fetched metadata.

     if (server.id === ENGINE_MCP_SERVER.id) {
       await updateEngineTools()
-      updateEngineServer(newServer, added)
     }

If you need a “toggle all” action, trigger updateEngineServer only from that UI action, not on add.

packages/plugins/robot/src/Main.vue (1)

225-244: Bug: computed ref used as boolean — MCP branch always taken

useMcpServer().isToolsEnabled is a ref; without .value the condition is always truthy. This forces the MCP path even when no tools are enabled.

-  if (useMcpServer().isToolsEnabled) {
+  if (useMcpServer().isToolsEnabled.value) {

Optional: initialize once to avoid repeatedly creating the composable:

// near other consts in setup()
const mcp = useMcpServer()
// then:
if (mcp.isToolsEnabled.value) { /* ... */ }
♻️ Duplicate comments (5)
packages/plugins/robot/src/mcp/MarkdownRenderer.vue (1)

10-11: Remove global highlight.js theme import to avoid overriding theme-scoped styles

This import applies the light theme globally and conflicts with the theme-scoped CSS you already import in the Less block.

-import 'highlight.js/styles/github.css'
packages/plugins/robot/src/Main.vue (4)

166-173: Nice: scroll now awaits DOM update

Switching to nextTick + null-checked element eliminates race conditions and flakiness.


247-254: Mirror renderContent when updating assistant replies and errors

Prevents empty assistant bubbles when only content is set.

-          messages.value[messages.value.length - 1].content = replyWithoutCode
+          messages.value[messages.value.length - 1].content = replyWithoutCode
+          messages.value[messages.value.length - 1].renderContent = replyWithoutCode
           setContextSession()
@@
-        .catch(() => {
-          messages.value[messages.value.length - 1].content = '连接失败'
+        .catch(() => {
+          messages.value[messages.value.length - 1].content = '连接失败'
+          messages.value[messages.value.length - 1].renderContent = '连接失败'

Also add the same mirroring in the MCP catch at Line 237:

-        } catch (error) {
-          messages.value[messages.value.length - 1].content = '连接失败'
+        } catch (error) {
+          messages.value[messages.value.length - 1].content = '连接失败'
+          messages.value[messages.value.length - 1].renderContent = '连接失败'

Also applies to: 261-266


320-321: Populate placeholder assistant’s renderContent and drop stray name

Aligns with roles.assistant.customContentField and avoids unused prop.

-        messages.value.push({ role: 'assistant', content: '好的,正在执行相关操作,请稍等片刻...', name: 'AI' })
+        messages.value.push({
+          role: 'assistant',
+          content: '好的,正在执行相关操作,请稍等片刻...',
+          renderContent: '好的,正在执行相关操作,请稍等片刻...'
+        })

218-221: Default assistant message to renderContent to match customContentField

Assistant bubbles use customContentField='renderContent'. Ensure it’s always present.

-const getAiRespMessage = (role = 'assistant', content) => ({
-  role,
-  content
-})
+const getAiRespMessage = (role = 'assistant', content) => ({
+  role,
+  content,
+  renderContent: content
+})
🧹 Nitpick comments (9)
packages/plugins/page/src/mcp/tools/editSpecificPage.ts (2)

5-5: Validate non-empty page id.

Guard against empty ids to fail fast at validation.

Apply:

-  id: z.string().describe('The id of the page')
+  id: z.string().min(1, 'page id is required').describe('The id of the page')

18-33: Wrap switchPage in try/catch and return Markdown for better UX

The pipeline already recognizes type: 'markdown' (see other plugins), so you can safely return a formatted code block instead of plain text.

– File: packages/plugins/page/src/mcp/tools/editSpecificPage.ts (around lines 18–33)
Apply the following diff:

     await switchPage(id)

     const res = {
       status: 'success',
       message: `Page now can be edited.`,
       data: {}
     }

     return {
       content: [
         {
           type: 'text',
           text: JSON.stringify(res)
         }
       ]
     }

becomes

+    try {
+      await switchPage(id)
+      const res = {
+        status: 'success',
+        message: `Page "${id}" is now editable.`,
+        data: {}
+      }
+      return {
+        content: [
+          {
+            type: 'markdown',
+            text: `✅ 成功:已在画布中打开并可编辑页面:${id}\n\n\`\`\`json\n${JSON.stringify(res, null, 2)}\n\`\`\``
+          }
+        ]
+      }
+    } catch (e) {
+      const errMsg = e instanceof Error ? e.message : String(e)
+      const res = {
+        status: 'error',
+        message: `Failed to switch page: ${id}`,
+        error: errMsg
+      }
+      return {
+        content: [
+          {
+            type: 'markdown',
+            text: `❌ 切换页面失败:${id}\n\n\`\`\`json\n${JSON.stringify(res, null, 2)}\n\`\`\``
+          }
+        ]
+      }
+    }
packages/plugins/robot/src/mcp/MarkdownRenderer.vue (2)

36-38: Use PropType for union prop and (optionally) add a runtime validator

Improves typing and prevents accidental non-light/dark values at runtime.

-  theme: {
-    type: String as () => 'light' | 'dark',
-    default: 'light'
-  },
+  theme: {
+    type: String as import('vue').PropType<'light' | 'dark'>,
+    default: 'light',
+    validator: (v: string) => v === 'light' || v === 'dark'
+  },

Note: If preferred, import PropType at the top:

-import { computed } from 'vue'
+import { computed } from 'vue'
+import type { PropType } from 'vue'

66-67: Option precedence: consider protecting highlight/html defaults

Currently ...props.options can override highlight/html. If you don't intend that, spread first so your defaults win.

-const markdownIt = new MarkdownIt({
-  html: true,
-  breaks: true,
-  typographer: true,
-  highlight: (str: string, lang: string) => {
+const markdownIt = new MarkdownIt({
+  ...props.options,
+  html: true,
+  breaks: true,
+  typographer: true,
+  highlight: (str: string, lang: string) => {
     // ...
-  },
-  ...props.options
+  }
 })
packages/plugins/robot/src/mcp/LoadingRenderer.vue (1)

1-3: Use imported asset binding for robustness; avoid brittle relative URL

Importing the asset ensures correct bundler handling across environments. Also replace inline styles with a class if desired.

+<script setup lang="ts">
+import loadingImg from '../../assets/loading.webp'
+</script>
 <template>
-  <img src="../../assets/loading.webp" alt="loading" style="width: 24px; height: 24px" />
+  <img :src="loadingImg" alt="loading" style="width: 24px; height: 24px" />
 </template>
packages/plugins/robot/src/mcp/useMcp.ts (3)

26-33: Avoid merging title into name; keep semantic fields separate and format in UI

Mixing title (human label) into name (identifier) can leak UI wording into identifiers and complicate downstream logic. Preserve both and add a displayName for UI if needed.

-  const engineTools = tools.map((tool) => ({
-    id: tool.name,
-    name: tool.title ? `${tool.title} ${tool.name}` : tool.name,
-    description: tool.description,
-    enabled: tool.status === 'enabled'
-  }))
+  const engineTools = tools.map((tool) => ({
+    id: tool.name,
+    name: tool.name,
+    title: tool.title, // keep semantic label
+    displayName: tool.title ? `${tool.title} ${tool.name}` : tool.name, // UI only
+    description: tool.description,
+    enabled: tool.status === 'enabled'
+  }))

Adjust UI renderers to use displayName where appropriate.


124-129: Fix return type of listTools; handle undefined client gracefully

The current signature promises a Promise but may return undefined if the client is absent.

-const listTools = async (): Promise<McpListToolsResponse> =>
-  getMetaApi(META_SERVICE.McpService)?.getMcpClient()?.listTools()
+const listTools = async (): Promise<McpListToolsResponse | undefined> => {
+  const client = getMetaApi(META_SERVICE.McpService)?.getMcpClient?.()
+  return client?.listTools()
+}

Optionally also refine callTool’s return type instead of || {}.


58-59: Expose boolean accessor or ensure .value usage
isToolsEnabled is a computed ref—callers must use .value to read the boolean. We found one direct misuse in your code:

• packages/plugins/robot/src/Main.vue:226

- if (useMcpServer().isToolsEnabled) {
+ if (useMcpServer().isToolsEnabled.value) {

To avoid accidental omissions, consider one of the following optional refactors:

  • Export a plain function isToolsEnabled() that returns boolean instead of the ref.
  • Rename the ref to isToolsEnabledRef to make its reactive nature explicit.
packages/plugins/robot/src/Main.vue (1)

446-449: Make watcher async and await scroll for deterministic behavior

Ensures scroll occurs after reactive updates settle.

-watch([() => activeMessages.value.length, () => activeMessages.value.at(-1)?.renderContent?.length ?? 0], () => {
-  scrollContent()
-})
+watch(
+  [() => activeMessages.value.length, () => activeMessages.value.at(-1)?.renderContent?.length ?? 0],
+  async () => {
+    await scrollContent()
+  }
+)
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

💡 Knowledge Base configuration:

  • MCP integration is disabled by default for public repositories
  • Jira integration is disabled by default for public repositories
  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between d7c3a1f and 6ac1c7e.

📒 Files selected for processing (7)
  • packages/plugins/page/src/mcp/tools/editSpecificPage.ts (1 hunks)
  • packages/plugins/robot/src/Main.vue (13 hunks)
  • packages/plugins/robot/src/mcp/LoadingRenderer.vue (1 hunks)
  • packages/plugins/robot/src/mcp/MarkdownRenderer.vue (1 hunks)
  • packages/plugins/robot/src/mcp/McpServer.vue (4 hunks)
  • packages/plugins/robot/src/mcp/useMcp.ts (2 hunks)
  • packages/plugins/robot/src/mcp/utils.ts (5 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
  • packages/plugins/robot/src/mcp/utils.ts
  • packages/plugins/robot/src/mcp/McpServer.vue
🧰 Additional context used
🧠 Learnings (2)
📚 Learning: 2025-01-15T02:19:06.755Z
Learnt from: yy-wow
PR: opentiny/tiny-engine#940
File: packages/canvas/DesignCanvas/src/DesignCanvas.vue:0-0
Timestamp: 2025-01-15T02:19:06.755Z
Learning: In Vue components using message subscriptions from opentiny/tiny-engine-meta-register, always clean up subscriptions in the onUnmounted hook using useMessage().unsubscribe() to prevent memory leaks.

Applied to files:

  • packages/plugins/robot/src/Main.vue
📚 Learning: 2025-01-14T06:49:00.797Z
Learnt from: gene9831
PR: opentiny/tiny-engine#1011
File: packages/configurator/src/router-select-configurator/RouterSelectConfigurator.vue:63-73
Timestamp: 2025-01-14T06:49:00.797Z
Learning: In the tiny-engine project, the SvgIcon component is globally registered and available throughout Vue components without requiring explicit imports.

Applied to files:

  • packages/plugins/robot/src/Main.vue
🧬 Code Graph Analysis (1)
packages/plugins/robot/src/mcp/useMcp.ts (2)
packages/register/src/common.ts (1)
  • getMetaApi (20-30)
packages/register/src/constants.ts (1)
  • META_SERVICE (1-23)
⏰ 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: push-check
🔇 Additional comments (3)
packages/plugins/page/src/mcp/tools/editSpecificPage.ts (2)

13-14: No changes needed for editSpecificPage.ts – using .shape is consistent across all MCP tools

I’ve verified that every MCP tool in packages/plugins passes inputSchema: inputSchema.shape and then uses z.infer<typeof inputSchema> in its callback (e.g. in addPage, delPage, getPageList, all i18n and materials tools). This matches the established pattern, so unless the MCP tool registry has changed to expect full Zod schemas instead of shape objects, no action is required here.

• If the registry was updated to accept full Zod schemas (z.ZodType), convert all tools to pass inputSchema directly (and similarly for outputSchema).
• Otherwise, keep using .shape as-is.


9-9: No stale references found; tool name is unique and correctly normalized

  • Searched for “Edit_page_in_canvas” (with or without trailing dot): no matches
  • Searched for “edit_page_in_canvas”: only found in packages/plugins/page/src/mcp/tools/editSpecificPage.ts (its own definition)
  • No other tool exports use the same name

All references have been updated; no breakage expected.

packages/plugins/robot/src/Main.vue (1)

436-442: Roles configuration looks solid

Using MarkdownRenderer with customContentField for assistant is consistent with the new render pipeline.

Comment thread packages/plugins/robot/src/mcp/MarkdownRenderer.vue
@chilingling chilingling merged commit 12b6384 into opentiny:release/v2.8.x Aug 19, 2025
2 checks passed
@coderabbitai coderabbitai Bot mentioned this pull request Aug 26, 2025
14 tasks
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working release merge to release/ branch, before release period

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants