Skip to content

fix: add namespace prefix to MCP tools to avoid conflicts with plugin tools#6007

Open
ChuwuYo wants to merge 8 commits intoAstrBotDevs:masterfrom
ChuwuYo:master
Open

fix: add namespace prefix to MCP tools to avoid conflicts with plugin tools#6007
ChuwuYo wants to merge 8 commits intoAstrBotDevs:masterfrom
ChuwuYo:master

Conversation

@ChuwuYo
Copy link
Copy Markdown
Contributor

@ChuwuYo ChuwuYo commented Mar 10, 2026

Closes #5689

Description / 描述

This PR fixes the conflict where MCP tools and plugin tools with the same name could not coexist, which previously caused one tool to overwrite the other during registration. The fix introduces namespaced internal names for MCP tools while preserving backward compatibility for existing persona configurations. It also includes follow-up UI, review-feedback, and maintainability fixes so friendly tool names, search behavior, and MCP-specific rendering stay consistent across the dashboard.

此 PR 修复了 MCP 工具和插件工具同名时无法共存的问题。此前工具注册时会发生覆盖,导致两者不能同时启用。修复方案为 MCP 工具引入命名空间内部名称,同时保持对现有 persona 配置的向后兼容;并补充了若干前端、审查反馈和可维护性修复,确保仪表盘中的友好名称展示、搜索行为以及 MCP 相关渲染保持一致。

Relation to #5925 / 与 #5925 的关系

#5925 已合并,通过在 get_func() 中优先返回已激活的工具来缓解同名工具互相"连坐"的问题。它是冲突发生时的选择策略,保证至少一个工具能用。本 PR 解决的是更上一层的问题:通过为 MCP 工具加命名空间前缀 mcp_<server>__<tool>,让同名的 MCP 工具和插件工具可以同时注册、同时被 LLM 调用,而不是二选一。两者不冲突——合并本 PR 后 #5925 的激活优先逻辑继续保留,作为同名场景下的通用 tie-breaker。

#5925 is already merged and mitigates the "both disabled" problem by having get_func() prefer the active tool when names collide — a resolution policy ensuring at least one tool remains usable. This PR addresses the layer above: by namespacing MCP tool internal names as mcp_<server>__<tool>, same-named MCP tools and plugin tools can both register and both be invoked by the LLM, instead of one overriding the other. The two changes are complementary — #5925's active-preference logic stays intact after this PR merges, continuing to serve as a generic tie-breaker for same-name scenarios.

Root Cause / 根本原因

The ToolSet.add_tool() method uses the tool name as the unique identifier. When an MCP tool and a plugin tool share the same name, the later registration replaces the earlier one instead of allowing both to coexist.

ToolSet.add_tool() 方法使用工具名称作为唯一标识。当 MCP 工具与插件工具同名时,后注册的工具会覆盖先注册的工具,而不是允许两者共存。

Solution / 解决方案

  1. Namespaced Internal Names: MCP tools now use the internal format mcp_<encoded_server_name>__<tool_name> to avoid collisions.
  2. Original Name Preservation: The original MCP tool name is stored in MCPTool.original_tool_name and is still used when calling the MCP server.
  3. Backward Compatibility: get_func() falls back to a shared _find_mcp_tool_by_original_name helper that prefers active MCP tools, so legacy persona configurations continue to resolve. The fallback sits after the upstream active-first exact match (introduced in fix: prevent accidental removal of MCP external tools due to name collisions with disabled built-in tools #5925), so it only triggers when the namespaced name isn't found — no behavior change for current configurations.
  4. Friendly Display Names: The tool list API returns both internal name and user-facing display_name; the MCP "available tools" popup reads a parallel original_tool_names field so it shows raw tool names while persona matching keeps using namespaced identifiers.
  5. Frontend Consistency: Vue components use shared display-name and tool-search helpers, and search-query normalization is centralized in the helper to avoid redundant work on the caller side.
  6. MCP Detection Fix: Tool icons now use origin === 'mcp' instead of parsing the tool name format.
  7. Production Safety: The post-init invariant in _start_mcp_server raises an explicit RuntimeError instead of relying on assert, so it still holds under python -O.

Modifications / 改动点

Backend / 后端:

  • astrbot/core/agent/mcp_client.py

    • Add namespaced MCP tool names using mcp_<encoded_server_name>__<tool_name>
    • URL-encode server names to avoid normalization collisions
    • Store original tool name in original_tool_name
    • Move quote import to module scope
  • astrbot/core/provider/func_tool_manager.py

    • Extract _find_mcp_tool_by_original_name helper shared by get_func(); active matches take priority, deterministic tie-break with warning when multiple MCP servers expose the same original tool name
    • Keep is_builtin_tool as a pure boolean predicate (remove prior unreachable MCP fallback)
    • Replace assert mcp_client is not None in _start_mcp_server with an explicit RuntimeError so the invariant survives python -O
  • astrbot/dashboard/routes/tools.py

    • Add display_name to the tool list API
    • Return MCP origin metadata for UI rendering
    • Initialize display_name in the builtin-tool branch to stay compatible with the builtin tool registry introduced upstream
    • get_mcp_servers pre-indexes MCPTool instances by server once and uses dict lookup on the runtime view, avoiding an O(#servers × #tools) rescan
    • Expose both tools (namespaced, for personaForm.tools set matching) and original_tool_names (for UI display) on each MCP server

Frontend / 前端:

  • dashboard/src/components/extension/componentPanel/types.ts

    • Add display_name to the ToolItem interface
  • dashboard/src/utils/toolDisplayName.ts

    • Centralize tool display-name resolution and search matching logic; matchesToolSearch handles all query normalization internally
  • dashboard/src/components/shared/PersonaForm.vue

    • Use display_name for tool selection and selected tool rendering
    • Reuse shared display-name and search-matching helpers
    • Remove outdated search dependency on old MCP-specific fields
  • dashboard/src/components/shared/PersonaQuickPreview.vue

    • Use display_name for tool preview display
  • dashboard/src/views/persona/PersonaManager.vue

    • Use display_name for persona detail display
    • Reuse shared display-name helper
    • Type availableTools as ToolItem[] instead of any[]
  • dashboard/src/components/extension/componentPanel/components/ToolTable.vue

    • Use origin === 'mcp' for icon rendering instead of name parsing
    • Use display_name for user-facing display
    • Keep the builtin-tool config-tag tooltip introduced upstream
  • dashboard/src/components/extension/componentPanel/index.vue

    • Use shared tool-search matching so friendly names are searchable in the component panel
    • Drop redundant pre-normalization; delegate trim/lowercase to matchesToolSearch
  • dashboard/src/components/extension/McpServersSection.vue

    • Render the "available tools" popup from original_tool_names with a fallback to tools for forward compatibility

Key Improvements / 关键改进

  • MCP tools and plugin tools with the same original name can now coexist

  • Existing persona configurations continue to work without modification

  • MCP server names are encoded safely to avoid namespace collisions

  • UI consistently shows friendly MCP tool names instead of internal namespaced identifiers

  • The MCP "available tools" popup continues to show raw MCP tool names while namespaced names power set-based matching in persona forms

  • Tool search behavior matches what users actually see in the UI

  • MCP tool rendering no longer depends on fragile name-format heuristics

  • Shared frontend helpers reduce duplicated logic and future drift

  • _start_mcp_server fails loudly under python -O instead of silently skipping an assert

  • This is NOT a breaking change. / 这不是一个破坏性变更。

Validation / 验证情况

  • Rebased onto current master; conflicts with the upstream builtin tool registry (refactor: improve astrbot builtin tool management #7418) and related refactors were resolved, preserving upstream's active-first get_func behavior and the builtin-tool UI affordances
  • Backward-compatibility path in get_func() reviewed against legacy persona tool references; falls through only after the active-first exact match, so it's a no-op for existing configs that already resolve by current name
  • MCP tool display flow checked end-to-end through /api/tools/list, /api/tools/mcp/servers, and related Vue consumers
  • uv sync --group dev, ruff format --check and ruff check pass on the modified Python files; vue-tsc --noEmit passes for the touched frontend files
  • Local dashboard rebuilt with pnpm run build and copied into data/dist/; startup smoke test against http://localhost:6185 succeeds with no initialization errors and both the tool management view and the MCP servers popup render the expected names
  • Review follow-up items addressed in subsequent commits:
    • quote import moved to module scope
    • Duplicated display-name logic centralized in a shared helper
    • MCP icon detection switched from name parsing to origin
    • Stale search references updated to use shared matching logic
    • MCP fallback lookup extracted into a dedicated helper and made active-first
    • is_builtin_tool restored to a pure boolean predicate
    • /api/tools/mcp/servers returns a parallel original_tool_names field so the popup keeps showing raw tool names
    • get_mcp_servers no longer rescans func_list per server
    • Search-query normalization consolidated inside matchesToolSearch
    • assert mcp_client is not None replaced with an explicit RuntimeError

Screenshots / 截图

image image

Checklist / 检查清单

  • 😊 如果 PR 中有新加入的功能,已经通过 Issue / 邮件等方式和作者讨论过。/ If there are new features added in the PR, I have discussed it with the authors through issues/emails, etc.
  • 👀 我的更改经过了良好的检查,并已在上方提供了验证说明和运行截图。/ My changes have been reviewed carefully, and validation notes and screenshots have been provided above.
  • 🤓 我确保没有引入新依赖库,或者引入了新依赖库的同时将其添加到了 requirements.txtpyproject.toml 文件相应位置。/ I have ensured that no new dependencies are introduced, OR if new dependencies are introduced, they have been added to the appropriate locations in requirements.txt and pyproject.toml.
  • 😮 我的更改没有引入恶意代码。/ My changes do not introduce malicious code.

Summary by Sourcery

确保名称相同的 MCP 工具和插件工具可以共存,同时保持向后兼容性,并改进 UI 中工具名称的显示。

新功能:

  • 为 MCP 工具引入带命名空间的内部标识符,以避免与插件工具发生冲突。
  • 在 API 和前端组件中暴露更易于理解的工具 display_name 供用户查看。

缺陷修复:

  • 允许旧版 persona 继续工作,这些 persona 仍通过原始名称引用 MCP 工具;通过在函数工具管理器中添加兼容性查找机制实现。

增强改进:

  • 更新后端工具列表和与 persona 相关的 Vue 组件,使其使用 display_name 来显示工具名称,而不是内部名称。
  • 通过在启动成功后断言 MCP 客户端可用,使 MCP 服务器运行时初始化过程更加健壮。
Original summary in English

Summary by Sourcery

Ensure MCP and plugin tools with identical names can coexist while preserving backward compatibility and improving tool name display in the UI.

New Features:

  • Introduce namespaced internal identifiers for MCP tools to avoid collisions with plugin tools.
  • Expose a user-friendly display_name for tools in the API and frontend components.

Bug Fixes:

  • Allow legacy personas that reference MCP tools by their original names to continue functioning via a compatibility lookup in the function tool manager.

Enhancements:

  • Update backend tool listing and persona-related Vue components to use display_name for showing tool names instead of internal names.
  • Make MCP server runtime initialization more robust by asserting the MCP client is available after successful startup.

Summary by Sourcery

Allow MCP and plugin tools with identical names to coexist by namespacing MCP tool identifiers while keeping existing personas and UI behavior compatible.

Bug Fixes:

  • Add backward-compatible lookup of MCP tools by original name so existing personas continue to resolve tools after namespacing changes.
  • Ensure MCP server runtime initialization stores a non-null MCP client after successful startup.

Enhancements:

  • Namespace MCP tool internal names and preserve original names on the MCPTool object for display and server calls.
  • Expose a user-facing display_name and origin metadata for tools via the tool list API and use them across persona and component panel views.
  • Unify tool display-name resolution and search matching through shared frontend helpers so tool search and rendering stay consistent.
  • Render MCP tools with a dedicated icon based on origin metadata instead of parsing tool names.

Summary by Sourcery

Allow MCP tools and plugin tools with identical names to coexist by namespacing MCP tool identifiers while keeping existing personas and UI behavior compatible.

New Features:

  • Introduce namespaced internal names for MCP tools to avoid collisions with plugin tools and other tools.

Bug Fixes:

  • Resolve MCP and plugin tool registration conflicts so tools with the same original name no longer overwrite each other.
  • Ensure MCP server runtime initialization always stores a non-null MCP client after successful startup.

Enhancements:

  • Add a fallback lookup path to resolve MCP tools by their original names for legacy persona configurations.
  • Expose user-facing display names and origin metadata for tools in the tool list API and propagate them through MCP server data.
  • Standardize tool display names and search behavior across persona-related and component panel Vue components using shared helpers.
  • Render MCP tools with a dedicated icon based on origin metadata and show original tool names in MCP server details for readability.
  • Store original MCP tool names alongside namespaced identifiers to keep server calls and UI labels intuitive.

@auto-assign auto-assign Bot requested review from Soulter and anka-afk March 10, 2026 18:38
@dosubot dosubot Bot added size:M This PR changes 30-99 lines, ignoring generated files. area:core The bug / feature is about astrbot's core, backend area:webui The bug / feature is about webui(dashboard) of astrbot. labels Mar 10, 2026
@dosubot
Copy link
Copy Markdown

dosubot Bot commented Mar 10, 2026

Related Documentation

1 document(s) may need updating based on files changed in this PR:

AstrBotTeam's Space

pr4697的改动
View Suggested Changes
@@ -446,11 +446,63 @@
   - `on_task_canceled`:任务被取消时触发
   - `on_task_result_ignored`:任务结果被忽略时触发(如状态已变化)
 
+#### MCP 工具命名空间前缀(PR #6007)
+
+[PR #6007](https://github.com/AstrBotDevs/AstrBot/pull/6007) 修复了 MCP 工具与插件工具同名时的冲突问题,通过为 MCP 工具添加命名空间前缀,确保两者可以同时启用。
+
+**命名空间前缀机制:**
+
+- **内部名称格式**:MCP 工具现在使用格式 `mcp_<server_name>__<tool_name>`(双下划线)作为内部唯一标识符
+- **服务器名称规范化**:服务器名称中的空格和特殊字符会被替换为下划线,仅保留字母、数字和下划线
+- **原始名称保留**:MCP 工具通过 `original_tool_name` 属性存储原始工具名称,用于调用 MCP 服务器和显示
+- **用户友好显示**:Dashboard UI 通过 `display_name` 字段显示格式化的工具名称(格式:`<ServerName>_<ToolName>`),用户看到的是友好名称而非内部的命名空间前缀名称
+
+**向后兼容性:**
+
+- **Persona 配置兼容**:旧的 Persona 配置引用 MCP 工具时可以继续使用原始工具名称(无前缀),系统会自动回退查找
+- **查找逻辑**:`ToolSet.get_func()` 方法包含回退机制:
+  1. 优先按完整名称(带前缀)精确匹配
+  2. 如果未找到,对 MCP 工具按 `original_tool_name` 回退查找
+  3. 如果多个 MCP 服务器提供相同原始名称的工具,会记录警告并返回第一个匹配项
+- **无需立即更新**:现有配置无需立即修改,但建议新配置使用带前缀的完整名称以避免歧义
+
+**Dashboard UI 更新:**
+
+以下 UI 组件已更新为显示 `display_name` 字段,确保用户看到友好的工具名称:
+
+- **ToolTable 组件**:函数工具管理表格中显示 `display_name`
+- **PersonaForm 组件**:人格编辑表单的工具选择列表和已选工具标签使用 `display_name`
+- **PersonaQuickPreview 组件**:人格预览中显示 `display_name`
+- **PersonaManager 组件**:人格详情页面使用 `display_name` 显示工具名称
+
+**API 更新:**
+
+- **工具列表 API**(`/api/tools/list`):返回结果中包含 `display_name` 字段,用于前端显示
+- **MCP 服务器详情 API**:MCP 服务器信息中的工具列表显示去除前缀的原始工具名称
+
+**技术实现:**
+
+- **MCPTool 类改动**(`astrbot/core/agent/mcp_client.py`):
+  - 工具名称添加命名空间前缀:`f"mcp_{normalized_server_name}__{mcp_tool.name}"`
+  - 新增 `original_tool_name` 属性存储原始名称
+  - 调用 MCP 服务器时使用 `original_tool_name` 而非内部名称
+- **ToolSet 回退逻辑**(`astrbot/core/provider/func_tool_manager.py`):
+  - `get_func()` 方法支持按原始名称查找 MCP 工具
+  - 处理多个 MCP 服务器提供同名工具的情况,记录警告并返回第一个匹配项
+
+**用户收益:**
+
+- MCP 工具和插件工具同名时不再冲突,可以同时启用
+- 多个 MCP 服务器可以提供同名工具,系统会自动区分
+- 旧的 Persona 配置无需修改即可继续工作
+- UI 显示友好的工具名称,提升用户体验
+
 #### 注意事项
 - 工具需在对应 SubAgent/Persona 配置中声明
 - 动态注册工具时需确保配置同步更新
 - 后台任务需正确设置 `background_task: true` 参数
 - 后台任务执行通过 `HandoffExecutor.execute_queued_task()` 方法完成,该方法从数据库中恢复任务上下文并执行
+- **MCP 工具命名**:新配置建议使用带前缀的完整名称(`mcp_<server>__<tool>`)以避免歧义,但旧配置使用原始名称仍可正常工作
 
 #### Neo 技能生命周期工具(PR #5028)
 

[Accept] [Decline]

Note: You must be authenticated to accept/decline updates.

How did I do? Any feedback?  Join Discord

Copy link
Copy Markdown
Contributor

@sourcery-ai sourcery-ai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey - 我发现了两个问题,并留下了一些整体性反馈:

  • get_mcp_servers 中,从 tool.name 中剥离 mcp_<server_name>__ 前缀的逻辑假设与 MCPTool 相同的规范化方式(去掉空格/短横线等),但目前这里是直接使用 name;建议应用与 MCPTool 中相同的规范化逻辑,这样对于名称中包含空格或特殊字符的服务器,前缀检测才能稳定工作。
  • 同样在 get_mcp_servers 中,你在 mcp_client.tools 上拆分 tool.name 来移除命名空间前缀,但这些 tools 看起来是原始的 MCP tools(没有新的命名空间包装),因此 startswith/split('__', 1) 这段逻辑实际上是个空操作,可以简化,或者改为对你在其他地方实际使用的带命名空间的表示进行处理。
给 AI Agents 的提示词
Please address the comments from this code review:

## Overall Comments
- In `get_mcp_servers`, the logic that strips the `mcp_<server_name>__` prefix from `tool.name` assumes the same normalization as `MCPTool` (spaces/dashes removed etc.), but it currently uses `name` directly; consider applying the same normalization used in `MCPTool` so the prefix detection works reliably for servers whose names contain spaces or special characters.
- Also in `get_mcp_servers`, you are splitting `tool.name` to drop a namespace prefix on `mcp_client.tools`, but those `tools` appear to be the raw MCP tools (without the new namespaced wrapper), so the `startswith`/`split('__', 1)` block is effectively a no-op and can be simplified or updated to operate on the namespaced representation you actually use elsewhere.

## Individual Comments

### Comment 1
<location path="astrbot/core/agent/mcp_client.py" line_range="382-390" />
<code_context>
     def __init__(
         self, mcp_tool: mcp.Tool, mcp_client: MCPClient, mcp_server_name: str, **kwargs
     ) -> None:
+        # Add namespace prefix to avoid conflicts with plugin tools
+        # Normalize server name: remove spaces and special characters
+        normalized_server_name = mcp_server_name.replace(" ", "_").replace("-", "_")
+        # Remove any other special characters, keep only alphanumeric and underscore
+        normalized_server_name = "".join(
+            c for c in normalized_server_name if c.isalnum() or c == "_"
+        )
+        # Format: mcp_<normalized_server_name>__<tool_name>
+        namespaced_name = f"mcp_{normalized_server_name}__{mcp_tool.name}"
+
         super().__init__(
</code_context>
<issue_to_address>
**issue (bug_risk):** Normalized MCP tool prefix can diverge from raw server name used in routing logic, causing mismatches.

This code normalizes `mcp_server_name`, but `dashboard/routes/tools.py:get_mcp_servers` de-namespaces tools using `tool.name.startswith(f"mcp_{name}__")`, where `name` is the raw server identifier from the route. If the route name contains spaces, hyphens, or other characters that are sanitized here, the prefix check will fail and tools won’t be matched. Consider centralizing this normalization (e.g., a shared helper) and applying it consistently both when building the tool name and when constructing `f"mcp_{name}__"` in the route, or store and reuse a single normalized server key.
</issue_to_address>

### Comment 2
<location path="astrbot/dashboard/routes/tools.py" line_range="107-110" />
<code_context>
                     if name_key == name:
                         mcp_client = runtime.client
-                        server_info["tools"] = [tool.name for tool in mcp_client.tools]
+                        # Display original tool names without namespace prefix
+                        server_info["tools"] = [
+                            tool.name.split("__", 1)[1]
+                            if tool.name.startswith(f"mcp_{name}__")
+                            else tool.name
+                            for tool in mcp_client.tools
</code_context>
<issue_to_address>
**issue (bug_risk):** Prefix stripping for MCP tool names assumes raw server name matches the normalized prefix used in MCPTool.

Because `MCPTool` normalizes `mcp_server_name` (e.g., replacing spaces/hyphens and stripping special characters) before building the `mcp_{server}__` prefix, `startswith(f"mcp_{name}__")` may fail for server names that change under that normalization. In those cases, the prefix won’t be stripped and the UI will show the raw, prefixed name. Please normalize `name` the same way before constructing the prefix (or use a shared helper) so prefix creation and decoding stay consistent.
</issue_to_address>

Sourcery 对开源项目是免费的——如果你觉得我们的评审有帮助,请考虑分享 ✨
帮我变得更有用!请在每条评论上点 👍 或 👎,我会根据你的反馈改进后续的评审。
Original comment in English

Hey - I've found 2 issues, and left some high level feedback:

  • In get_mcp_servers, the logic that strips the mcp_<server_name>__ prefix from tool.name assumes the same normalization as MCPTool (spaces/dashes removed etc.), but it currently uses name directly; consider applying the same normalization used in MCPTool so the prefix detection works reliably for servers whose names contain spaces or special characters.
  • Also in get_mcp_servers, you are splitting tool.name to drop a namespace prefix on mcp_client.tools, but those tools appear to be the raw MCP tools (without the new namespaced wrapper), so the startswith/split('__', 1) block is effectively a no-op and can be simplified or updated to operate on the namespaced representation you actually use elsewhere.
Prompt for AI Agents
Please address the comments from this code review:

## Overall Comments
- In `get_mcp_servers`, the logic that strips the `mcp_<server_name>__` prefix from `tool.name` assumes the same normalization as `MCPTool` (spaces/dashes removed etc.), but it currently uses `name` directly; consider applying the same normalization used in `MCPTool` so the prefix detection works reliably for servers whose names contain spaces or special characters.
- Also in `get_mcp_servers`, you are splitting `tool.name` to drop a namespace prefix on `mcp_client.tools`, but those `tools` appear to be the raw MCP tools (without the new namespaced wrapper), so the `startswith`/`split('__', 1)` block is effectively a no-op and can be simplified or updated to operate on the namespaced representation you actually use elsewhere.

## Individual Comments

### Comment 1
<location path="astrbot/core/agent/mcp_client.py" line_range="382-390" />
<code_context>
     def __init__(
         self, mcp_tool: mcp.Tool, mcp_client: MCPClient, mcp_server_name: str, **kwargs
     ) -> None:
+        # Add namespace prefix to avoid conflicts with plugin tools
+        # Normalize server name: remove spaces and special characters
+        normalized_server_name = mcp_server_name.replace(" ", "_").replace("-", "_")
+        # Remove any other special characters, keep only alphanumeric and underscore
+        normalized_server_name = "".join(
+            c for c in normalized_server_name if c.isalnum() or c == "_"
+        )
+        # Format: mcp_<normalized_server_name>__<tool_name>
+        namespaced_name = f"mcp_{normalized_server_name}__{mcp_tool.name}"
+
         super().__init__(
</code_context>
<issue_to_address>
**issue (bug_risk):** Normalized MCP tool prefix can diverge from raw server name used in routing logic, causing mismatches.

This code normalizes `mcp_server_name`, but `dashboard/routes/tools.py:get_mcp_servers` de-namespaces tools using `tool.name.startswith(f"mcp_{name}__")`, where `name` is the raw server identifier from the route. If the route name contains spaces, hyphens, or other characters that are sanitized here, the prefix check will fail and tools won’t be matched. Consider centralizing this normalization (e.g., a shared helper) and applying it consistently both when building the tool name and when constructing `f"mcp_{name}__"` in the route, or store and reuse a single normalized server key.
</issue_to_address>

### Comment 2
<location path="astrbot/dashboard/routes/tools.py" line_range="107-110" />
<code_context>
                     if name_key == name:
                         mcp_client = runtime.client
-                        server_info["tools"] = [tool.name for tool in mcp_client.tools]
+                        # Display original tool names without namespace prefix
+                        server_info["tools"] = [
+                            tool.name.split("__", 1)[1]
+                            if tool.name.startswith(f"mcp_{name}__")
+                            else tool.name
+                            for tool in mcp_client.tools
</code_context>
<issue_to_address>
**issue (bug_risk):** Prefix stripping for MCP tool names assumes raw server name matches the normalized prefix used in MCPTool.

Because `MCPTool` normalizes `mcp_server_name` (e.g., replacing spaces/hyphens and stripping special characters) before building the `mcp_{server}__` prefix, `startswith(f"mcp_{name}__")` may fail for server names that change under that normalization. In those cases, the prefix won’t be stripped and the UI will show the raw, prefixed name. Please normalize `name` the same way before constructing the prefix (or use a shared helper) so prefix creation and decoding stay consistent.
</issue_to_address>

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

Comment thread astrbot/core/agent/mcp_client.py
Comment thread astrbot/dashboard/routes/tools.py Outdated
@gemini-code-assist
Copy link
Copy Markdown
Contributor

Summary of Changes

Hello, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This pull request resolves a critical issue where MCP tools and plugin tools with identical names would conflict, preventing their simultaneous use. The solution involves implementing a robust namespacing strategy for MCP tools internally, while meticulously ensuring full backward compatibility for existing configurations. Concurrently, the user interface has been refined to display the original, user-friendly names of these tools, significantly enhancing usability without exposing the internal namespaced identifiers.

Highlights

  • Resolve Tool Name Conflicts: MCP tools now use a namespace prefix (e.g., mcp_<server_name>__<tool_name>) internally to prevent naming conflicts with plugin tools, allowing both to coexist.
  • Backward Compatibility: Existing persona configurations that reference tools by their original names will continue to function correctly due to a new fallback mechanism in the get_func() method.
  • Enhanced User Interface: The frontend has been updated to display user-friendly original tool names (display_name) instead of the internal namespaced identifiers, improving clarity for users.
  • Multiple MCP Servers Support: The system now supports multiple MCP servers providing tools with the same original name, with a warning logged if ambiguity arises during fallback lookups.
Changelog
  • astrbot/core/agent/mcp_client.py
    • Added logic to prefix MCP tool names with mcp_<normalized_server_name>__ to create unique internal identifiers.
    • Stored the original tool name in a new original_tool_name attribute for use in MCP server calls and display.
  • astrbot/core/provider/func_tool_manager.py
    • Modified get_func to first attempt an exact match, then fall back to searching for MCP tools by their original_tool_name for backward compatibility.
    • Added logging for cases where multiple MCP tools share the same original name.
  • astrbot/dashboard/routes/tools.py
    • Updated the get_mcp_servers API to display original tool names without the namespace prefix.
    • Added a display_name field to the get_tool_list API response, providing a user-friendly name for the frontend.
  • dashboard/src/components/extension/componentPanel/components/ToolTable.vue
    • Updated to display item.display_name instead of item.name for tool names.
  • dashboard/src/components/extension/componentPanel/types.ts
    • Added an optional display_name property to the ToolItem interface.
  • dashboard/src/components/shared/PersonaForm.vue
    • Modified tool selection lists and selected tool chips to use display_name for presentation.
    • Introduced a getToolDisplayName helper method to resolve display names.
  • dashboard/src/components/shared/PersonaQuickPreview.vue
    • Updated to use display_name for tool preview display.
    • Modified loadToolsMeta to store display_name in the tool metadata map.
  • dashboard/src/views/persona/PersonaManager.vue
    • Updated persona detail view to use getToolDisplayName for displaying tool names.
    • Added axios import and availableTools data property.
    • Implemented loadTools method to fetch tool list and populate availableTools.
    • Added getToolDisplayName helper method to resolve display names.
    • Integrated loadTools into the initialize method.
Activity
  • The author, ChuwuYo, created this pull request to resolve tool name conflicts.
  • The PR description provides a detailed breakdown of the root cause, solution, and modifications, covering both backend and frontend changes.
  • Test results are included, confirming unit test passes, backward compatibility, namespace collision prevention, and correct UI display.
  • Screenshots are provided to visually demonstrate the changes.
  • A checklist indicates that the changes are not breaking, well-tested, do not introduce new dependencies, and contain no malicious code.
Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for GitHub and other Google products, sign up here.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

Copy link
Copy Markdown
Contributor

@gemini-code-assist gemini-code-assist Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request effectively addresses the tool name conflict between MCP and plugin tools by introducing a namespacing scheme. The changes are comprehensive, touching both the backend and frontend to ensure backward compatibility and a good user experience.

My review has identified a few areas for improvement:

  • There are a couple of bugs in the backend related to server name normalization that could lead to incorrect behavior or collisions.
  • The frontend code has some duplication in data fetching and utility functions, which could be refactored for better maintainability and performance by using a centralized state management store.
  • A minor logic improvement for deterministic behavior when handling multiple tool matches.

Overall, this is a solid implementation. Addressing the points in my review will make it more robust and maintainable.

Comment thread astrbot/core/agent/mcp_client.py Outdated
Comment thread astrbot/dashboard/routes/tools.py Outdated
Comment thread astrbot/core/provider/func_tool_manager.py Outdated
Comment thread dashboard/src/views/persona/PersonaManager.vue
@ChuwuYo ChuwuYo changed the title fix: resolve MCP tool name conflicts with plugin tools fix: add namespace prefix to MCP tools to avoid conflicts with plugin tools Mar 10, 2026
@ChuwuYo
Copy link
Copy Markdown
Contributor Author

ChuwuYo commented Mar 11, 2026

@sourcery-ai review

Copy link
Copy Markdown
Contributor

@sourcery-ai sourcery-ai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey - 我发现了 1 个问题,并给出了一些整体反馈:

  • MCPTool.__init__ 中,建议将 urllib.parse.quote 的导入移动到模块作用域,这样在每次实例化工具时就不会重复导入。
  • 新的 getToolDisplayName 逻辑目前在 PersonaForm.vuePersonaManager.vue 中都有重复;建议将其集中到一个共享的 helper/composable 中,以便保持显示名称解析行为的一致性并更易于维护。
Prompt for AI Agents
Please address the comments from this code review:

## Overall Comments
- In `MCPTool.__init__`, consider moving the `urllib.parse.quote` import to module scope so the import isn’t repeated on every tool instantiation.
- The new `getToolDisplayName` logic is now duplicated across `PersonaForm.vue` and `PersonaManager.vue`; consider centralizing this in a shared helper/composable so the display-name resolution behavior stays consistent and easier to maintain.

## Individual Comments

### Comment 1
<location path="dashboard/src/components/extension/componentPanel/components/ToolTable.vue" line_range="44" />
<code_context>
             {{ item.name.includes(':') ? 'mdi-server-network' : 'mdi-function-variant' }}
           </v-icon>
           <div>
</code_context>
<issue_to_address>
**issue (bug_risk):** The icon heuristic based on `name.includes(':')` may no longer correctly distinguish MCP tools after namespacing.

With MCP tools now named `mcp_<server>__<tool>`, this colon-based check will misclassify many items. Since the API exposes `origin`, `origin_name`, and `display_name`, icon selection should key off `origin === 'mcp'` vs `origin === 'plugin'` (or similar) instead of parsing `name`.
</issue_to_address>

Sourcery 对开源项目是免费的——如果你觉得我们的评审有帮助,欢迎分享 ✨
帮我变得更有用!请在每条评论上点 👍 或 👎,我会根据这些反馈改进后续的评审。
Original comment in English

Hey - I've found 1 issue, and left some high level feedback:

  • In MCPTool.__init__, consider moving the urllib.parse.quote import to module scope so the import isn’t repeated on every tool instantiation.
  • The new getToolDisplayName logic is now duplicated across PersonaForm.vue and PersonaManager.vue; consider centralizing this in a shared helper/composable so the display-name resolution behavior stays consistent and easier to maintain.
Prompt for AI Agents
Please address the comments from this code review:

## Overall Comments
- In `MCPTool.__init__`, consider moving the `urllib.parse.quote` import to module scope so the import isn’t repeated on every tool instantiation.
- The new `getToolDisplayName` logic is now duplicated across `PersonaForm.vue` and `PersonaManager.vue`; consider centralizing this in a shared helper/composable so the display-name resolution behavior stays consistent and easier to maintain.

## Individual Comments

### Comment 1
<location path="dashboard/src/components/extension/componentPanel/components/ToolTable.vue" line_range="44" />
<code_context>
             {{ item.name.includes(':') ? 'mdi-server-network' : 'mdi-function-variant' }}
           </v-icon>
           <div>
</code_context>
<issue_to_address>
**issue (bug_risk):** The icon heuristic based on `name.includes(':')` may no longer correctly distinguish MCP tools after namespacing.

With MCP tools now named `mcp_<server>__<tool>`, this colon-based check will misclassify many items. Since the API exposes `origin`, `origin_name`, and `display_name`, icon selection should key off `origin === 'mcp'` vs `origin === 'plugin'` (or similar) instead of parsing `name`.
</issue_to_address>

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

Comment thread dashboard/src/components/extension/componentPanel/components/ToolTable.vue Outdated
@dosubot dosubot Bot added size:L This PR changes 100-499 lines, ignoring generated files. and removed size:M This PR changes 30-99 lines, ignoring generated files. labels Mar 11, 2026
@ChuwuYo
Copy link
Copy Markdown
Contributor Author

ChuwuYo commented Mar 11, 2026

🦌 Finish

ChuwuYo added 4 commits April 18, 2026 14:46
…in tools

- Add namespace prefix format: mcp_<server_name>__<tool_name> to MCPTool
- Store original tool name in MCPTool.original_tool_name for display and calling
- Add backward compatibility in get_func() to support old persona tool references
- Update tool list API to include display_name field showing original name
- Update MCP server info to display original tool names without prefix
- Fixes issue where MCP tools and plugin tools with same name would conflict
- Update frontend components to use display_name for user-friendly display
- Add display_name field to ToolItem TypeScript interface
- Update tool list display in:
  * PersonaForm.vue - tool selection list and selected tools
  * PersonaQuickPreview.vue - tool preview display
  * PersonaManager.vue - persona detail view
  * ToolTable.vue - function tools management table
- Add getToolDisplayName() helper methods in PersonaForm and PersonaManager
- Use URL encoding for MCP server name normalization to avoid collisions
- Add deterministic sorting for ambiguous MCP tool name resolution
- Add type assertion to fix Pylance type error
- Simplify MCP server tools display (use raw tool names directly)
- move quote import in MCPTool to module scope
- use origin instead of name parsing for MCP tool icons
- centralize tool display-name resolution in shared helper
- centralize tool search matching for persona and component views
- remove stale MCP-specific frontend search reference
@ChuwuYo
Copy link
Copy Markdown
Contributor Author

ChuwuYo commented Apr 18, 2026

@sourcery-ai review

Copy link
Copy Markdown
Contributor

@sourcery-ai sourcery-ai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey - I've found 2 issues, and left some high level feedback:

  • In FuncToolManager.is_builtin_tool, the new MCP fallback block appears after an early return and is therefore unreachable; consider moving this logic into a separate helper or above the return so it can actually run.
  • The MCP fallback lookup logic for original_tool_name is now duplicated (and slightly diverging) between get_func and the unreachable block under is_builtin_tool; consider extracting a shared MCP lookup helper to keep behavior consistent and easier to maintain.
Prompt for AI Agents
Please address the comments from this code review:

## Overall Comments
- In `FuncToolManager.is_builtin_tool`, the new MCP fallback block appears after an early `return` and is therefore unreachable; consider moving this logic into a separate helper or above the return so it can actually run.
- The MCP fallback lookup logic for `original_tool_name` is now duplicated (and slightly diverging) between `get_func` and the unreachable block under `is_builtin_tool`; consider extracting a shared MCP lookup helper to keep behavior consistent and easier to maintain.

## Individual Comments

### Comment 1
<location path="dashboard/src/utils/toolDisplayName.ts" line_range="4" />
<code_context>
+import type { ToolItem } from '@/components/extension/componentPanel/types';
+
+type ToolDisplayNameSource = Pick<ToolItem, 'name' | 'display_name'>;
+type ToolSearchSource = Pick<ToolItem, 'name' | 'display_name' | 'description' | 'origin' | 'origin_name'>;
+
+export function resolveToolDisplayName(
</code_context>
<issue_to_address>
**issue (bug_risk):** ToolSearchSource references `origin` fields that are not defined on ToolItem.

`ToolItem` in `types.ts` only defines `name`, `display_name?`, `description`, `active`, and `readonly?`, so `Pick<ToolItem, 'origin' | 'origin_name'>` will not type-check. Please either add `origin`/`origin_name` to `ToolItem` or define `ToolSearchSource` independently (or via a broader API type) so it accurately reflects the objects returned from `/api/tools/list`. Also update any callers that pass `ToolItem` into `matchesToolSearch` to use the corrected type.
</issue_to_address>

### Comment 2
<location path="astrbot/core/provider/func_tool_manager.py" line_range="334" />
<code_context>
             if f.name == name:
                 return f
+
+        # Fallback: try to find MCP tool by original name for backward compatibility
+        # This handles cases where personas reference tools by their original names
+        if isinstance(name, str):
</code_context>
<issue_to_address>
**issue (complexity):** Consider extracting the MCP lookup into a shared helper and keeping `is_builtin_tool` purely boolean to avoid duplication and mixed responsibilities.

You can keep the MCP fallback behavior while reducing duplication and fixing the `is_builtin_tool` confusion by:

1. Extracting the MCP lookup into a private helper.
2. Keeping `is_builtin_tool` purely boolean.
3. Calling the helper where the actual `MCPTool` instance is needed.

### 1. Extract a shared MCP lookup helper

```python
def _find_mcp_tool_by_original_name(self, name: str) -> MCPTool | None:
    """Best-effort lookup of an MCP tool by its original name."""
    matches = [
        f for f in self.func_list
        if isinstance(f, MCPTool) and f.original_tool_name == name
    ]

    if not matches:
        return None

    if len(matches) > 1:
        # Sort for deterministic selection
        matches.sort(key=lambda f: f.name)
        logger.warning(
            "Multiple MCP tools found with original name '%s': %s. Using %s",
            name,
            [f.name for f in matches],
            matches[0].name,
        )

    return matches[0]
```

### 2. Simplify `get_func` to call the helper

Replace the MCP block in `get_func` with a call to the helper:

```python
def get_func(self, name) -> FuncTool | None:
    # 优先返回已激活的工具(后加载的覆盖前面的,与 ToolSet.add_tool 保持一致)
    # 使用 getattr(..., True) 与 ToolSet.add_tool 保持一致:没有 active 属性的工具视为已激活
    for f in reversed(self.func_list):
        if f.name == name and getattr(f, "active", True):
            return f

    # 退化则拿最后一个同名工具
    for f in reversed(self.func_list):
        if f.name == name:
            return f

    # Fallback: try to find MCP tool by original name for backward compatibility
    if isinstance(name, str):
        mcp_tool = self._find_mcp_tool_by_original_name(name)
        if mcp_tool is not None:
            return mcp_tool

        try:
            builtin_tool = self.get_builtin_tool(name)
        except KeyError:
            return None

        if getattr(builtin_tool, "active", True):
            return builtin_tool
        return builtin_tool

    return None
```

### 3. Keep `is_builtin_tool` as a boolean

Remove the MCP block from `is_builtin_tool` entirely and leave it as a pure predicate:

```python
def is_builtin_tool(self, name: str) -> bool:
    ensure_builtin_tools_loaded()
    return get_builtin_tool_class(name) is not None
```

If you truly need “does this name correspond to some tool, including MCP by original name”, introduce a clearly named method instead of overloading `is_builtin_tool`:

```python
def resolve_tool_by_name(self, name: str) -> FuncTool | None:
    """Resolve a tool by name, including MCP original names."""
    tool = self.get_func(name)
    if tool is not None:
        return tool
    if isinstance(name, str):
        return self._find_mcp_tool_by_original_name(name)
    return None
```

This keeps functionality intact, removes duplicated MCP matching logic, reduces branching, and restores `is_builtin_tool` to a single, predictable responsibility and return type.
</issue_to_address>

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

Comment thread dashboard/src/utils/toolDisplayName.ts
Comment thread astrbot/core/provider/func_tool_manager.py Outdated
@ChuwuYo
Copy link
Copy Markdown
Contributor Author

ChuwuYo commented Apr 18, 2026

@sourcery-ai review

Copy link
Copy Markdown
Contributor

@sourcery-ai sourcery-ai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey - I've found 1 issue, and left some high level feedback:

  • resolveToolDisplayName does a linear search over availableTools on every chip render (e.g., in PersonaForm and PersonaManager); consider precomputing a name→display_name map in component state/computed to avoid repeated O(n) scans during rendering.
  • The new assert mcp_client is not None in MCP server initialization will be stripped in optimized Python runs; if you intend this as a runtime safety check rather than a development-only assertion, replace it with an explicit conditional that raises a clear error or logs a critical failure.
Prompt for AI Agents
Please address the comments from this code review:

## Overall Comments
- resolveToolDisplayName does a linear search over availableTools on every chip render (e.g., in PersonaForm and PersonaManager); consider precomputing a name→display_name map in component state/computed to avoid repeated O(n) scans during rendering.
- The new `assert mcp_client is not None` in MCP server initialization will be stripped in optimized Python runs; if you intend this as a runtime safety check rather than a development-only assertion, replace it with an explicit conditional that raises a clear error or logs a critical failure.

## Individual Comments

### Comment 1
<location path="astrbot/core/provider/func_tool_manager.py" line_range="362-363" />
<code_context>
         if isinstance(name, str):
+            # Fallback: look up MCP tool by original (pre-namespaced) name
+            # for backward compatibility with legacy persona configurations.
+            mcp_tool = self._find_mcp_tool_by_original_name(name)
+            if mcp_tool is not None:
+                return mcp_tool
+
</code_context>
<issue_to_address>
**issue (bug_risk):** MCP fallback lookup ignores the `active` flag and may violate the existing "active tools first" semantics.

The first loop over `reversed(self.func_list)` correctly respects `active` and load order, but `_find_mcp_tool_by_original_name` does not filter on `active`, so an inactive MCP tool can still be returned if its original name matches. To keep the "active tools first" behavior consistent, either filter MCP matches by `getattr(f, "active", True)` or apply the same active-precedence rule when choosing among MCP matches.
</issue_to_address>

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

Comment thread astrbot/core/provider/func_tool_manager.py
Copy link
Copy Markdown

@SourceryAI SourceryAI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey - I've found 1 issue, and left some high level feedback:

  • In toolDisplayName.ts, ToolSearchSource uses Pick<ToolItem, 'origin' | 'origin_name'> but ToolItem doesn't declare those fields, which will either fail type-checking or rely on implicit any—consider extending ToolItem with origin and origin_name or changing the helper type to a looser interface that matches the actual API payload.
  • PersonaManager now fetches /api/tools/list directly and maintains its own availableTools, which duplicates the tool-loading logic already used elsewhere; consider centralizing tool metadata in a store or shared composable so persona-related views all reuse the same source of truth.
Prompt for AI Agents
Please address the comments from this code review:

## Overall Comments
- In `toolDisplayName.ts`, `ToolSearchSource` uses `Pick<ToolItem, 'origin' | 'origin_name'>` but `ToolItem` doesn't declare those fields, which will either fail type-checking or rely on implicit `any`—consider extending `ToolItem` with `origin` and `origin_name` or changing the helper type to a looser interface that matches the actual API payload.
- PersonaManager now fetches `/api/tools/list` directly and maintains its own `availableTools`, which duplicates the tool-loading logic already used elsewhere; consider centralizing tool metadata in a store or shared composable so persona-related views all reuse the same source of truth.

## Individual Comments

### Comment 1
<location path="dashboard/src/views/persona/PersonaManager.vue" line_range="354-355" />
<code_context>
-            skeletonTimer: null as ReturnType<typeof setTimeout> | null
+            skeletonTimer: null as ReturnType<typeof setTimeout> | null,
+
+            // 工具列表用于显示名称
+            availableTools: [] as any[]
         };
     },
</code_context>
<issue_to_address>
**suggestion:** Use a shared typed interface for availableTools instead of any[] to keep it aligned with backend tool metadata.

Since /api/tools/list appears to return the same structure as the ToolItem used in the extension panel (name, display_name, description, origin, origin_name, etc.), consider typing availableTools as ToolItem[] (or a narrowed variant) instead of any[]. This will give getToolDisplayName proper type checking and help catch mismatches if the backend tool schema changes.

Suggested implementation:

```
import { resolveToolDisplayName } from '@/utils/toolDisplayName';

import type { Folder, FolderTreeNode } from '@/components/folder/types';
import type { ToolItem } from '@/types/tool';

```

```
            // 骨架屏延迟显示控制
            showSkeleton: false,
            skeletonTimer: null as ReturnType<typeof setTimeout> | null,

            // 工具列表用于显示名称
            availableTools: [] as ToolItem[]

```

1. Ensure that `ToolItem` is actually defined and exported in `@/types/tool` (or adjust the import path to wherever the shared tool metadata interface lives, e.g. a module already used by the extension panel).
2. Confirm that the `ToolItem` type matches the shape returned by `/api/tools/list` (fields like `name`, `display_name`, `description`, `origin`, `origin_name`, etc.). If the backend response is a superset, you can define a narrower interface that extends `ToolItem` and use that instead.
3. If `getToolDisplayName` (or `resolveToolDisplayName`) assumes specific fields on `availableTools`, make sure those fields are present on `ToolItem` so you get full type checking rather than falling back to `any`.
</issue_to_address>

Hi @ChuwuYo! 👋

Thanks for trying out Sourcery by commenting with @sourcery-ai review! 🚀

Install the sourcery-ai bot to get automatic code reviews on every pull request ✨

Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

Comment thread dashboard/src/views/persona/PersonaManager.vue Outdated
ChuwuYo added 2 commits April 18, 2026 15:09
- Expose original_tool_names alongside namespaced tools in
  /api/tools/mcp/servers so the MCP "available tools" popup can render
  human-friendly names while persona tool matching continues to use
  namespaced identifiers.
- Render the popup list from item.original_tool_names with a fallback to
  item.tools for forward compatibility.
- Replace `assert mcp_client is not None` in _start_mcp_server with an
  explicit RuntimeError so the post-init invariant still holds when
  running under `python -O`, which strips asserts.
- Tighten the _find_mcp_tool_by_original_name docstring to match the
  surrounding single-line style.
@ChuwuYo
Copy link
Copy Markdown
Contributor Author

ChuwuYo commented Apr 18, 2026

@sourcery-ai review

Copy link
Copy Markdown
Contributor

@sourcery-ai sourcery-ai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey - I've left some high level feedback:

  • PersonaManager.vue now performs its own /api/tools/list fetch solely for display names; consider reusing the existing tool list source (e.g., via a shared store/composable) to avoid duplicate requests and keep tool metadata in sync across views.
  • In get_mcp_servers, building tools and original_tool_names by scanning tool_mgr.func_list for each server introduces tight coupling to the internal storage and a potential O(#servers * #tools) cost; it may be cleaner and cheaper to derive this from a pre-indexed structure or the MCP runtime/client directly.
  • matchesToolSearch lowercases and trims the query internally while some callers (like componentPanel/index.vue) pre-normalize the input with normalizeTextInput, leading to redundant work and slightly inconsistent normalization rules; consider standardizing normalization in one place (either always before or always inside the helper).
Prompt for AI Agents
Please address the comments from this code review:

## Overall Comments
- PersonaManager.vue now performs its own `/api/tools/list` fetch solely for display names; consider reusing the existing tool list source (e.g., via a shared store/composable) to avoid duplicate requests and keep tool metadata in sync across views.
- In `get_mcp_servers`, building `tools` and `original_tool_names` by scanning `tool_mgr.func_list` for each server introduces tight coupling to the internal storage and a potential `O(#servers * #tools)` cost; it may be cleaner and cheaper to derive this from a pre-indexed structure or the MCP runtime/client directly.
- `matchesToolSearch` lowercases and trims the query internally while some callers (like `componentPanel/index.vue`) pre-normalize the input with `normalizeTextInput`, leading to redundant work and slightly inconsistent normalization rules; consider standardizing normalization in one place (either always before or always inside the helper).

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

@ChuwuYo
Copy link
Copy Markdown
Contributor Author

ChuwuYo commented Apr 18, 2026

@seoeaa 原 issue 有人催促合并

@Soulter Soulter force-pushed the master branch 2 times, most recently from faf411f to 0068960 Compare April 19, 2026 09:50
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area:core The bug / feature is about astrbot's core, backend area:webui The bug / feature is about webui(dashboard) of astrbot. size:L This PR changes 100-499 lines, ignoring generated files.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Bug]同名的MCP工具会冲突

2 participants