From dc06bc943adc9767d2f67b1f531c03f2dd502a2b Mon Sep 17 00:00:00 2001 From: letr <123731298+letr007@users.noreply.github.com> Date: Sun, 1 Feb 2026 11:01:49 +0800 Subject: [PATCH] fix(mcp): cannot rename MCP Server (#4766) * fix(mcp): support renaming when editing MCP servers When editing the MCP server configuration, you can now change the server name. The frontend will save the original name in edit mode, and the backend will recognize the rename operation through the oldName field. * fix(mcp): fixed an issue where renaming the MCP server did not check for name conflicts When renaming an MCP server, add a check to see if the target name already exists. If the name exists and it is a rename operation, return an error message to avoid overwriting the configuration. --- astrbot/dashboard/routes/tools.py | 55 ++++++++++++++----- .../extension/McpServersSection.vue | 6 ++ 2 files changed, 47 insertions(+), 14 deletions(-) diff --git a/astrbot/dashboard/routes/tools.py b/astrbot/dashboard/routes/tools.py index d7b082000..333700410 100644 --- a/astrbot/dashboard/routes/tools.py +++ b/astrbot/dashboard/routes/tools.py @@ -130,19 +130,25 @@ class ToolsRoute(Route): server_data = await request.json name = server_data.get("name", "") + old_name = server_data.get("oldName") or name if not name: return Response().error("服务器名称不能为空").__dict__ config = self.tool_mgr.load_mcp_config() - if name not in config["mcpServers"]: - return Response().error(f"服务器 {name} 不存在").__dict__ + if old_name not in config["mcpServers"]: + return Response().error(f"服务器 {old_name} 不存在").__dict__ + + is_rename = name != old_name + + if name in config["mcpServers"] and is_rename: + return Response().error(f"服务器 {name} 已存在").__dict__ # 获取活动状态 active = server_data.get( "active", - config["mcpServers"][name].get("active", True), + config["mcpServers"][old_name].get("active", True), ) # 创建新的配置对象 @@ -153,7 +159,13 @@ class ToolsRoute(Route): # 复制所有配置字段 for key, value in server_data.items(): - if key not in ["name", "active", "tools", "errlogs"]: # 排除特殊字段 + if key not in [ + "name", + "active", + "tools", + "errlogs", + "oldName", + ]: # 排除特殊字段 if key == "mcpServers": key_0 = list(server_data["mcpServers"].keys())[ 0 @@ -165,29 +177,42 @@ class ToolsRoute(Route): # 如果只更新活动状态,保留原始配置 if only_update_active: - for key, value in config["mcpServers"][name].items(): + for key, value in config["mcpServers"][old_name].items(): if key != "active": # 除了active之外的所有字段都保留 server_config[key] = value - config["mcpServers"][name] = server_config + # config["mcpServers"][name] = server_config + if is_rename: + config["mcpServers"].pop(old_name) + config["mcpServers"][name] = server_config + else: + config["mcpServers"][name] = server_config if self.tool_mgr.save_mcp_config(config): # 处理MCP客户端状态变化 if active: - if name in self.tool_mgr.mcp_client_dict or not only_update_active: + if ( + old_name in self.tool_mgr.mcp_client_dict + or not only_update_active + or is_rename + ): try: - await self.tool_mgr.disable_mcp_server(name, timeout=10) + await self.tool_mgr.disable_mcp_server(old_name, timeout=10) except TimeoutError as e: return ( Response() - .error(f"启用前停用 MCP 服务器时 {name} 超时: {e!s}") + .error( + f"启用前停用 MCP 服务器时 {old_name} 超时: {e!s}" + ) .__dict__ ) except Exception as e: logger.error(traceback.format_exc()) return ( Response() - .error(f"启用前停用 MCP 服务器时 {name} 失败: {e!s}") + .error( + f"启用前停用 MCP 服务器时 {old_name} 失败: {e!s}" + ) .__dict__ ) try: @@ -208,18 +233,20 @@ class ToolsRoute(Route): .__dict__ ) # 如果要停用服务器 - elif name in self.tool_mgr.mcp_client_dict: + elif old_name in self.tool_mgr.mcp_client_dict: try: - await self.tool_mgr.disable_mcp_server(name, timeout=10) + await self.tool_mgr.disable_mcp_server(old_name, timeout=10) except TimeoutError: return ( - Response().error(f"停用 MCP 服务器 {name} 超时。").__dict__ + Response() + .error(f"停用 MCP 服务器 {old_name} 超时。") + .__dict__ ) except Exception as e: logger.error(traceback.format_exc()) return ( Response() - .error(f"停用 MCP 服务器 {name} 失败: {e!s}") + .error(f"停用 MCP 服务器 {old_name} 失败: {e!s}") .__dict__ ) diff --git a/dashboard/src/components/extension/McpServersSection.vue b/dashboard/src/components/extension/McpServersSection.vue index fe20497f8..53f0cc67f 100644 --- a/dashboard/src/components/extension/McpServersSection.vue +++ b/dashboard/src/components/extension/McpServersSection.vue @@ -251,6 +251,7 @@ export default { active: true, tools: [] }, + originalServerName: '', save_message_snack: false, save_message: '', save_message_success: 'success' @@ -359,6 +360,9 @@ export default { active: this.currentServer.active, ...configObj }; + if (this.isEditMode && this.originalServerName) { + serverData.oldName = this.originalServerName; + } const endpoint = this.isEditMode ? '/api/tools/mcp/update' : '/api/tools/mcp/add'; axios.post(endpoint, serverData) .then(response => { @@ -402,6 +406,7 @@ export default { active: server.active, tools: server.tools || [] }; + this.originalServerName = server.name; this.serverConfigJson = JSON.stringify(configCopy, null, 2); this.isEditMode = true; this.showMcpServerDialog = true; @@ -461,6 +466,7 @@ export default { this.serverConfigJson = ''; this.jsonError = null; this.isEditMode = false; + this.originalServerName = ''; }, showSuccess(message) { this.save_message = message;