chore: add title property for mcp tools#1584
Conversation
WalkthroughAdds a localized title field to multiple MCP tool descriptors across canvas, layout, i18n, materials, and page plugins. One tool (delNode) gains additional annotations metadata. Robot tool mapping now propagates title to engine tools. Minor error-handling message refinement in getPageDetail; no other behavioral changes. Changes
Sequence Diagram(s)sequenceDiagram
participant UI as UI
participant MCP as MCP Tool List
participant Robot as robot/useMcp
participant Engine as Engine Tools
UI->>MCP: getToolList()
MCP-->>UI: tools[{ name, description, title, ... }]
UI->>Robot: updateEngineTools(tools)
Robot->>Engine: map to engineTools with title
Engine-->>UI: tools available with title metadata
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~25 minutes Poem
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. ✨ Finishing Touches
🧪 Generate unit tests
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. 🪧 TipsChatThere are 3 ways to chat with CodeRabbit:
SupportNeed help? Create a ticket on our support page for assistance with any issues or questions. CodeRabbit Commands (Invoked using PR/Issue comments)Type Other keywords and placeholders
CodeRabbit Configuration File (
|
There was a problem hiding this comment.
Actionable comments posted: 0
🔭 Outside diff range comments (1)
packages/plugins/i18n/src/composable/tools/updateI18n.ts (1)
41-54: Validation logic never rejects missing translationsThe intent (comment on Line 41) is to require at least one of zh_CN or en_US, but the current schema allows both to be absent and safeParse will still succeed. This path will never hit the error branch.
Replace the block with a direct check against the destructured values.
Apply this diff:
- // 验证至少有一个翻译字段 - const translationValidation = z - .object({ - zh_CN: z.string().optional(), - en_US: z.string().optional() - }) - .safeParse(args) - - if (!translationValidation.success) { - // 直接返回验证错误,已经符合新的结构化格式 - return createErrorResponse( - 'Invalid translation fields', - 'At least one translation (zh_CN or en_US) must be provided' - ) - } + // 验证至少有一个翻译字段 + if (zh_CN === undefined && en_US === undefined) { + return createErrorResponse( + 'Invalid translation fields', + 'At least one translation (zh_CN or en_US) must be provided' + ) + }
🧹 Nitpick comments (22)
packages/plugins/page/src/mcp/tools/getPageList.ts (1)
15-16: Avoid shadowing the exported constant name with a local variable.Inside the callback,
const { getPageList } = usePage()shadows the exportedgetPageListobject. Alias the destructured method to improve readability and prevent confusion.- const { getPageList } = usePage() - const [firstGroup, secondGroup] = await getPageList() + const { getPageList: fetchPageList } = usePage() + const [firstGroup, secondGroup] = await fetchPageList()packages/canvas/DesignCanvas/src/mcp/tools/changeNodeProps.ts (1)
16-23: Deduplicate title: it appears both top-level and in annotations.You now have
titleat the top level and inannotations.title. Keeping two sources risks divergence. Suggest centralizing the literal.- title: '修改节点属性', + title: TITLE, @@ - annotations: { - title: '修改节点属性', // 人性化标题 + annotations: { + title: TITLE, // 人性化标题Add a shared constant near the top of the file:
// near imports const TITLE = '修改节点属性'packages/plugins/page/src/mcp/tools/editSpecificPage.ts (1)
9-9: Normalize tool name (optional): snake_case and no trailing dot.Current
name: 'Edit_page_in_canvas.'is inconsistent (capital E, trailing period). If this identifier is externally referenced, changing it is breaking—otherwise consider normalizing.- name: 'Edit_page_in_canvas.', + name: 'edit_page_in_canvas',packages/plugins/i18n/src/composable/tools/updateI18n.ts (1)
31-37: Clarify which title is meant for UI display (duplication with annotations.title)You now have:
- top-level title: '更新 I18n 词条' (CN)
- annotations.title: 'Update I18n Entry' (EN)
If both are rendered by different consumers, that’s fine. Otherwise, consider converging on a single source or documenting which one the UI should use to avoid inconsistent labels.
packages/plugins/page/src/mcp/tools/changePageBasicInfo.ts (2)
19-19: Good addition of title metadataAdds a localized UI label consistent with the rest of the PR. Note this object also has label (EN) and title (CN); ensure downstream consumers consistently prefer one to avoid UI inconsistency.
30-41: Add isError flag to error response for parity with other toolsOther tools (e.g., getPageDetail) mark error payloads with isError: true on the content item. Consider aligning for consistent handling by consumers.
Apply this diff:
- return { - content: [ - { - type: 'text', - text: JSON.stringify({ - status: 'error', - message: error - }) - } - ] - } + return { + content: [ + { + isError: true, + type: 'text', + text: JSON.stringify({ + status: 'error', + message: error + }) + } + ] + }packages/layout/src/mcp/tools/getAllPlugins.ts (1)
12-13: Avoid shadowing the exported constant name inside callbackDestructuring getAllPlugins into a local with the same name as the exported constant can be confusing. Rename the local binding.
Apply this diff:
- const { getAllPlugins } = useLayout() - const plugins = await getAllPlugins() + const { getAllPlugins: fetchAllPlugins } = useLayout() + const plugins = await fetchAllPlugins()packages/layout/src/mcp/tools/switchPlugin.ts (3)
4-7: Schema currently requires pluginId even when operation === 'close'If the intent is to allow close without a pluginId, make pluginId optional in the schema and validate the dependency in the callback. This avoids forcing callers to pass a dummy string.
Apply this diff:
-const inputSchema = z.object({ - pluginId: z.string().describe('The id of the plugin to operate.'), - operation: z.enum(['open', 'close']).describe('The operation to perform on the plugin.') -}) +const inputSchema = z.object({ + pluginId: z.string().optional().describe('The id of the plugin to operate. Required when operation is "open".'), + operation: z.enum(['open', 'close']).describe('The operation to perform on the plugin.') +})Follow-up callback guard suggested in the next comment.
19-23: Guard against open without pluginId and avoid treating it as closeCurrently, an 'open' operation with a missing/empty pluginId falls into the else branch and closes the panel. Provide an explicit error instead.
Apply this diff:
- if (operation === 'open' && pluginId) { - await activePlugin(pluginId) - } else { - await closePlugin() - } + if (operation === 'open') { + if (!pluginId) { + return { + content: [ + { + isError: true, + type: 'text', + text: JSON.stringify({ + status: 'error', + message: 'pluginId is required when operation is "open"' + }) + } + ] + } + } + await activePlugin(pluginId) + } else { + await closePlugin() + }
12-14: Wording tweak for description (clarity)The sentence repeats and doesn’t mention opening/closing. Minor copy edit for clarity.
Apply this diff:
- description: - 'Switch to the current TinyEngine low-code application. Use this when you need to switch to the current TinyEngine low-code application.', + description: + 'Open or close a plugin panel in the current TinyEngine low-code application.',packages/plugins/page/src/mcp/tools/getPageDetail.ts (1)
11-11: Title metadata addition looks goodAdds a localized title consistent with other page tools. With both label (EN) and title (CN) present, ensure the UI consistently prefers the intended property.
packages/canvas/DesignCanvas/src/mcp/tools/queryNodeById.ts (1)
10-10: Avoid duplicating title in both top-level and annotationsYou now have title at the top-level and also annotations.title. This can drift over time and cause inconsistent UI labeling.
Consider keeping the top-level title as the single source of truth and removing the duplicate in annotations:
annotations: { - title: '根据ID查询节点', // 人性化标题 readOnlyHint: true, // 只读操作,不会修改任何状态 openWorldHint: false // 不与外部世界交互,只在 TinyEngine 内部操作 },Also applies to: 15-19
packages/plugins/i18n/src/composable/tools/delI18n.ts (1)
21-21: Unify display text language between title and annotations.titleTop-level title is Chinese, while annotations.title is English. Mixing languages in the same tool definition can lead to inconsistent UI.
If annotations.title is still consumed downstream, align it with the new top-level title:
- title: 'Delete I18n Entry', + // 与顶层 title 保持一致,避免显示不一致 + title: '删除 I18n 词条',Alternatively, if only the top-level title is used for display now, remove annotations.title to avoid duplication.
Also applies to: 27-27
packages/plugins/page/src/mcp/tools/delPage.ts (1)
11-11: Title added — check for label/title divergenceGood to see title added. Note the existing label: 'Delete Page' is English while title is Chinese. If both are surfaced somewhere, this creates inconsistent UI.
- If label is deprecated, consider removing it (or marking clearly as legacy) to avoid confusion.
- If label is still used, align it with title to maintain a consistent language, e.g.:
- label: 'Delete Page', + label: '删除页面',Please confirm whether label is still referenced in the engine/robot UI. If not, we can open a follow-up PR to standardize on title only.
packages/plugins/robot/src/mcp/useMcp.ts (1)
65-73: Make title optional and provide a safe fallback to avoid undefined UI labelsSome tools may not yet define title; mapping without a fallback can surface undefined in the UI. Also widen typing to reflect reality and reduce brittleness.
Apply:
- const tools: Array<{ name: string; description: string; status: string; title: string }> = + const tools: Array<{ name: string; description?: string; status: string; title?: string }> = (await getMetaApi(META_SERVICE.McpService)?.getToolList?.()) || [] const engineTools = tools.map((tool) => ({ id: tool.name, name: tool.name, - title: tool.title, + // Prefer explicit title; fall back to name to ensure a non-empty label + title: tool.title || tool.name, description: tool.description, enabled: tool.status === 'enabled' }))As a follow-up, ensure PluginTool (from @opentiny/tiny-robot) tolerates a title field or that extra fields are harmless at call sites.
packages/plugins/i18n/src/composable/tools/getI18n.ts (1)
26-33: Align annotation.title language
The top-leveltitleis in Chinese whileannotations.titleremains in English, which may lead to inconsistent UI labels. Please updateannotations.titleto match the top-level title.• File:
packages/plugins/i18n/src/composable/tools/getI18n.ts
Lines: ~26–33Suggested diff:
name: 'get_i18n', title: '获取 I18n 词条', description: 'Retrieve i18n entries from the current TinyEngine low-code application. Can get a specific entry by key or all entries if no key is provided.', inputSchema: inputSchema.shape, outputSchema: outputSchema.shape, // 使用 Zod 版本的统一输出结构 annotations: { - title: 'Get I18n Entries', + title: '获取 I18n 词条', readOnlyHint: true, openWorldHint: false },packages/plugins/i18n/src/composable/tools/addI18n.ts (1)
25-33: Title addition is good; align annotations.title to prevent mixed-language labelsTop-level title is Chinese; annotations.title is English. Align to avoid inconsistent UI.
Apply this diff:
export const addI18n = { name: 'add_i18n', title: '新增 I18n 词条', description: 'Add a new i18n entry to the current TinyEngine low-code application. Use this when you need to add new internationalization translations to your application.', inputSchema: inputSchema.shape, outputSchema: outputSchema.shape, // 使用 Zod 版本的统一输出结构 annotations: { - title: 'Add I18n Entry', + title: '新增 I18n 词条', readOnlyHint: false, destructiveHint: false, idempotentHint: true, openWorldHint: false },I can sweep all i18n tools to standardize titles if you’d like a quick follow-up PR.
packages/canvas/DesignCanvas/src/mcp/tools/delNode.ts (5)
15-21: Annotations are helpful; de-duplicate the title to a single source of truthGood use of annotations. To avoid drift between the top-level title and annotations.title, define the label once and reference it in both places.
Apply this diff within this file:
- title: '删除节点', + title: TITLE,- title: '删除节点', // 人性化标题 + title: TITLE, // 人性化标题Add this near the imports (outside the selected ranges):
const TITLE = '删除节点' as const
5-5: Harden input validation for idPrevent empty strings to avoid ambiguous lookups.
- id: z.string().describe('The id of the node to delete.') + id: z.string().min(1, 'ID cannot be empty').describe('The id of the node to delete.')
53-58: Return JSON content instead of stringified text for consistencyYou return structured JSON on error but a stringified JSON on success. Unify to
type: 'json'to simplify client handling.- content: [ - { - type: 'text', - text: JSON.stringify(res) - } - ] + content: [ + { + type: 'json', + value: res + } + ]
40-44: Surface runtime failures from operateNodeIf
operateNodecan throw (e.g., internal errors), wrap it to return a structured error instead of failing the tool execution.- useCanvas().operateNode({ - type: 'delete', - id - }) + try { + useCanvas().operateNode({ + type: 'delete', + id + }) + } catch (err) { + return { + content: [ + { + type: 'json', + value: { + status: 'error', + message: (err as Error).message || 'Failed to delete node.' + } + } + ] + } + }
33-34: Language consistency: error/success messages vs. Chinese titlesTitles are Chinese, messages are English. Consider localizing messages (or using i18n) for a consistent UX.
Example (Chinese):
- message: 'Node not found, please check the id is correct.' + message: '未找到节点,请检查 ID 是否正确。'- message: `Node deleted successfully`, + message: '节点删除成功',Also applies to: 47-47
📜 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.
📒 Files selected for processing (21)
packages/canvas/DesignCanvas/src/mcp/tools/changeNodeProps.ts(1 hunks)packages/canvas/DesignCanvas/src/mcp/tools/delNode.ts(1 hunks)packages/canvas/DesignCanvas/src/mcp/tools/getCurrentSelectedNode.ts(1 hunks)packages/canvas/DesignCanvas/src/mcp/tools/getPageSchema.ts(1 hunks)packages/canvas/DesignCanvas/src/mcp/tools/queryNodeById.ts(1 hunks)packages/canvas/DesignCanvas/src/mcp/tools/selectSpecificNode.ts(1 hunks)packages/layout/src/mcp/tools/getAllPlugins.ts(1 hunks)packages/layout/src/mcp/tools/switchPlugin.ts(1 hunks)packages/plugins/i18n/src/composable/tools/addI18n.ts(1 hunks)packages/plugins/i18n/src/composable/tools/delI18n.ts(1 hunks)packages/plugins/i18n/src/composable/tools/getI18n.ts(1 hunks)packages/plugins/i18n/src/composable/tools/updateI18n.ts(1 hunks)packages/plugins/materials/src/mcp/tools/getComponentDetail.ts(1 hunks)packages/plugins/materials/src/mcp/tools/getComponentList.ts(1 hunks)packages/plugins/page/src/mcp/tools/addPage.ts(1 hunks)packages/plugins/page/src/mcp/tools/changePageBasicInfo.ts(1 hunks)packages/plugins/page/src/mcp/tools/delPage.ts(1 hunks)packages/plugins/page/src/mcp/tools/editSpecificPage.ts(1 hunks)packages/plugins/page/src/mcp/tools/getPageDetail.ts(2 hunks)packages/plugins/page/src/mcp/tools/getPageList.ts(1 hunks)packages/plugins/robot/src/mcp/useMcp.ts(1 hunks)
🧰 Additional context used
🧬 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 (15)
packages/plugins/page/src/mcp/tools/getPageList.ts (2)
9-9: Title metadata addition looks good and aligns with PR intent.No behavioral change; the field is consistent with other tools in this PR.
6-12: Title field propagation verified – no further changes neededThe
titleproperty is defined on all MCP tools (includinggetPageList), explicitly included in thetoolsarray type, and mapped throughupdateEngineToolsinpackages/plugins/robot/src/mcp/useMcp.ts(lines 65–73). Common type definitions (ToolItem,UpdateToolConfig) supporttitle?, so TypeScript will pass and the UI will surface it correctly.packages/plugins/page/src/mcp/tools/addPage.ts (1)
18-18: LGTM: Added title metadata.Consistent with the PR’s pattern; no runtime impact.
packages/plugins/materials/src/mcp/tools/getComponentDetail.ts (1)
10-12: LGTM: Title field added.Matches the cross-repo change; no behavior change.
packages/plugins/page/src/mcp/tools/editSpecificPage.ts (1)
10-10: LGTM: Title field added.Aligned with the PR’s objective; no logic change.
packages/plugins/i18n/src/composable/tools/updateI18n.ts (1)
26-26: Title metadata addition looks goodThe new title field aligns with the PR objective of surfacing UI-friendly titles on MCP tools.
packages/layout/src/mcp/tools/getAllPlugins.ts (1)
8-8: Title metadata addition looks goodMatches the PR-wide pattern of exposing user-friendly names for tools.
packages/layout/src/mcp/tools/switchPlugin.ts (1)
11-11: Title metadata addition looks goodConsistent with the rest of the MCP tools.
packages/plugins/page/src/mcp/tools/getPageDetail.ts (1)
40-40: Error message robustness improvedUsing instanceof Error avoids emitting undefined messages when the thrown value is not an Error instance. Good defensive tweak.
packages/plugins/materials/src/mcp/tools/getComponentList.ts (1)
8-8: LGTM — title metadata addition is consistent with PR intentThe added title aligns with how engine tools now surface display names.
packages/canvas/DesignCanvas/src/mcp/tools/getPageSchema.ts (1)
8-16: LGTM: Title addition is consistent with annotations and engine propagationThe mapping in packages/plugins/robot/src/mcp/useMcp.ts confirms
title: tool.title(line 70), so the top‐level title propagates correctly.packages/canvas/DesignCanvas/src/mcp/tools/getCurrentSelectedNode.ts (1)
8-16: LGTM: Title added and consistent with annotationsNo behavioral change; metadata is clear and aligned.
packages/canvas/DesignCanvas/src/mcp/tools/selectSpecificNode.ts (1)
10-20: LGTM: Title added; consistent with annotations and other toolsMatches the pattern across the PR; no concerns.
packages/canvas/DesignCanvas/src/mcp/tools/delNode.ts (2)
10-10: Title metadata added — aligns with PR goalAdding the top-level title meets the PR objective and will help downstream UIs label this tool clearly.
13-14: Verify expected type forinputSchemaacross MCP toolsOur search shows every MCP tool (including
delNode.ts) is passing the plain shape map via:
- packages/canvas/DesignCanvas/src/mcp/tools/delNode.ts:13: inputSchema: inputSchema.shape
Please confirm that the MCP tooling runtime expects a Zod
.shapeobject rather than the full Zod schema. If it actually requires the full schema, update:- inputSchema: inputSchema.shape, + inputSchema: inputSchema,accordingly.
English | 简体中文
PR
PR Checklist
Please check if your PR fulfills the following requirements:
PR Type
What kind of change does this PR introduce?
Background and solution
What is the current behavior?
Issue Number: N/A
What is the new behavior?
Does this PR introduce a breaking change?
Other information
Summary by CodeRabbit
New Features
Bug Fixes