fix: enable only synced ModelScope MCP servers (#9084)

* fix: enable only synced ModelScope MCP servers

* fix: avoid mutating default MCP config
This commit is contained in:
VectorPeak
2026-07-03 01:05:25 +08:00
committed by GitHub
parent 9f50c900b0
commit 89b80a6ca7
2 changed files with 91 additions and 10 deletions

View File

@@ -1057,11 +1057,14 @@ class FunctionToolManager:
"mcp_server_list",
[],
)
local_mcp_config = self.load_mcp_config()
local_mcp_config = copy.deepcopy(self.load_mcp_config())
synced_count = 0
mcp_servers = local_mcp_config.setdefault("mcpServers", {})
synced_servers: list[tuple[str, dict]] = []
for server in mcp_server_list:
server_name = server["name"]
server_name = server.get("name")
if not server_name:
continue
operational_urls = server.get("operational_urls", [])
if not operational_urls:
continue
@@ -1070,28 +1073,28 @@ class FunctionToolManager:
if not server_url:
continue
# 添加到配置中(同名会覆盖)
local_mcp_config["mcpServers"][server_name] = {
server_config = {
"url": server_url,
"transport": "sse",
"active": True,
"provider": "modelscope",
}
synced_count += 1
mcp_servers[server_name] = server_config
synced_servers.append((server_name, server_config))
if synced_count > 0:
if synced_servers:
self.save_mcp_config(local_mcp_config)
tasks = []
for server in mcp_server_list:
name = server["name"]
for name, config in synced_servers:
tasks.append(
self.enable_mcp_server(
name=name,
config=local_mcp_config["mcpServers"][name],
config=config,
),
)
await asyncio.gather(*tasks)
logger.info(
f"从 ModelScope 同步了 {synced_count} 个 MCP 服务器",
f"从 ModelScope 同步了 {len(synced_servers)} 个 MCP 服务器",
)
else:
logger.warning("没有找到可用的 ModelScope MCP 服务器")

View File

@@ -3,6 +3,7 @@ import json
import pytest
from astrbot.core import sp
from astrbot.core.provider import func_tool_manager as ftm
from astrbot.core.provider.func_tool_manager import FunctionToolManager
from astrbot.core.tools.computer_tools.shell import ExecuteShellTool
from astrbot.core.tools.message_tools import SendMessageToUserTool
@@ -345,3 +346,80 @@ def test_firecrawl_tools_are_registered_as_builtin_tools():
assert extract_tool.name == "firecrawl_extract_web_page"
assert manager.is_builtin_tool("web_search_firecrawl") is True
assert manager.is_builtin_tool("firecrawl_extract_web_page") is True
@pytest.mark.asyncio
async def test_modelscope_sync_enables_only_synced_servers(monkeypatch):
class FakeResponse:
status = 200
async def __aenter__(self):
return self
async def __aexit__(self, exc_type, exc, tb):
return False
async def json(self):
return {
"data": {
"mcp_server_list": [
{
"name": "valid",
"operational_urls": [{"url": "https://example.com/mcp"}],
},
{"name": "missing-url", "operational_urls": []},
{"name": "empty-url", "operational_urls": [{}]},
{"operational_urls": [{"url": "https://example.com/no-name"}]},
]
}
}
class FakeSession:
async def __aenter__(self):
return self
async def __aexit__(self, exc_type, exc, tb):
return False
def get(self, *_args, **_kwargs):
return FakeResponse()
saved_configs = []
enabled_servers = []
default_config = {"mcpServers": {}}
manager = FunctionToolManager()
async def fake_enable_mcp_server(name, config):
enabled_servers.append((name, config))
monkeypatch.setattr(ftm.aiohttp, "ClientSession", lambda: FakeSession())
monkeypatch.setattr(manager, "load_mcp_config", lambda: default_config)
monkeypatch.setattr(manager, "save_mcp_config", saved_configs.append)
monkeypatch.setattr(manager, "enable_mcp_server", fake_enable_mcp_server)
await manager.sync_modelscope_mcp_servers("token")
assert default_config == {"mcpServers": {}}
assert saved_configs == [
{
"mcpServers": {
"valid": {
"url": "https://example.com/mcp",
"transport": "sse",
"active": True,
"provider": "modelscope",
}
}
}
]
assert enabled_servers == [
(
"valid",
{
"url": "https://example.com/mcp",
"transport": "sse",
"active": True,
"provider": "modelscope",
},
)
]