fix: 移除退役内置 MCP 默认项
This commit is contained in:
parent
bf7a4821d1
commit
79ff9b14b8
@ -38,13 +38,6 @@ _UNSET = object()
|
||||
|
||||
# Default MCP Server configurations (Imported to DB on first run)
|
||||
_DEFAULT_MCP_SERVERS = {
|
||||
"sequentialthinking": {
|
||||
"url": "https://remote.mcpservers.org/sequentialthinking/mcp",
|
||||
"transport": "streamable_http",
|
||||
"description": "顺序思考工具,帮助 AI 将复杂问题分解为多个步骤",
|
||||
"icon": "🧠",
|
||||
"tags": ["内置", "AI"],
|
||||
},
|
||||
"mcp-server-chart": {
|
||||
"command": "npx",
|
||||
"args": ["-y", "@antv/mcp-server-chart"],
|
||||
@ -55,6 +48,8 @@ _DEFAULT_MCP_SERVERS = {
|
||||
},
|
||||
}
|
||||
|
||||
_RETIRED_BUILTIN_MCP_SERVER_SLUGS = ("sequentialthinking",)
|
||||
|
||||
_SYNCED_MCP_FIELDS = (
|
||||
"description",
|
||||
"transport",
|
||||
@ -81,6 +76,17 @@ async def ensure_builtin_mcp_servers_in_db() -> None:
|
||||
try:
|
||||
async with pg_manager.get_async_session_context() as session:
|
||||
any_changed = False
|
||||
for slug in _RETIRED_BUILTIN_MCP_SERVER_SLUGS:
|
||||
result = await session.execute(
|
||||
select(MCPServer).filter(MCPServer.slug == slug, MCPServer.created_by == "system")
|
||||
)
|
||||
retired = result.scalar_one_or_none()
|
||||
if retired:
|
||||
await session.delete(retired)
|
||||
clear_mcp_server_tools_cache(slug)
|
||||
any_changed = True
|
||||
logger.info(f"Removed retired built-in MCP server '{slug}' from database")
|
||||
|
||||
for slug, config in _DEFAULT_MCP_SERVERS.items():
|
||||
result = await session.execute(select(MCPServer).filter(MCPServer.slug == slug))
|
||||
existing = result.scalar_one_or_none()
|
||||
|
||||
@ -49,7 +49,7 @@ def test_update_mcp_server_status(monkeypatch):
|
||||
self.enabled = enabled
|
||||
|
||||
def to_dict(self):
|
||||
return {"name": "sequentialthinking", "enabled": self.enabled}
|
||||
return {"name": "demo-mcp", "enabled": self.enabled}
|
||||
|
||||
async def fake_set_server_enabled(db, name, enabled, updated_by=None):
|
||||
captured["name"] = name
|
||||
@ -60,13 +60,13 @@ def test_update_mcp_server_status(monkeypatch):
|
||||
monkeypatch.setattr("server.routers.mcp_router.set_server_enabled", fake_set_server_enabled)
|
||||
|
||||
client = TestClient(_build_app())
|
||||
resp = client.put("/api/system/mcp-servers/sequentialthinking/status", json={"enabled": False})
|
||||
resp = client.put("/api/system/mcp-servers/demo-mcp/status", json={"enabled": False})
|
||||
assert resp.status_code == 200, resp.text
|
||||
payload = resp.json()
|
||||
assert payload["success"] is True
|
||||
assert payload["enabled"] is False
|
||||
assert payload["data"]["enabled"] is False
|
||||
assert captured == {"name": "sequentialthinking", "enabled": False, "updated_by": "admin"}
|
||||
assert captured == {"name": "demo-mcp", "enabled": False, "updated_by": "admin"}
|
||||
|
||||
|
||||
def test_update_mcp_server_status_not_found(monkeypatch):
|
||||
|
||||
@ -2,7 +2,37 @@ from __future__ import annotations
|
||||
|
||||
from types import SimpleNamespace
|
||||
|
||||
import pytest_asyncio
|
||||
from sqlalchemy import select
|
||||
from sqlalchemy.ext.asyncio import async_sessionmaker, create_async_engine
|
||||
|
||||
from yuxi.agents.mcp import service as mcp_service
|
||||
from yuxi.storage.postgres import manager as postgres_manager
|
||||
from yuxi.storage.postgres.models_business import MCPServer
|
||||
|
||||
|
||||
class _AsyncSessionContext:
|
||||
def __init__(self, db):
|
||||
self.db = db
|
||||
|
||||
async def __aenter__(self):
|
||||
return self.db
|
||||
|
||||
async def __aexit__(self, *_args):
|
||||
return False
|
||||
|
||||
|
||||
@pytest_asyncio.fixture
|
||||
async def mcp_session():
|
||||
engine = create_async_engine("sqlite+aiosqlite:///:memory:")
|
||||
async with engine.begin() as conn:
|
||||
await conn.run_sync(MCPServer.__table__.create)
|
||||
|
||||
session_factory = async_sessionmaker(engine, expire_on_commit=False)
|
||||
async with session_factory() as session:
|
||||
yield session
|
||||
|
||||
await engine.dispose()
|
||||
|
||||
|
||||
class _FakeClient:
|
||||
@ -13,6 +43,61 @@ class _FakeClient:
|
||||
return self._tools
|
||||
|
||||
|
||||
async def test_ensure_builtin_mcp_servers_removes_retired_system_server(monkeypatch, mcp_session):
|
||||
retired_server = MCPServer(
|
||||
slug="sequentialthinking",
|
||||
name="sequentialthinking",
|
||||
description="old builtin",
|
||||
transport="streamable_http",
|
||||
url="https://remote.mcpservers.org/sequentialthinking/mcp",
|
||||
enabled=1,
|
||||
created_by="system",
|
||||
updated_by="system",
|
||||
)
|
||||
mcp_session.add(retired_server)
|
||||
await mcp_session.commit()
|
||||
|
||||
monkeypatch.setattr(
|
||||
postgres_manager.pg_manager,
|
||||
"get_async_session_context",
|
||||
lambda: _AsyncSessionContext(mcp_session),
|
||||
)
|
||||
|
||||
await mcp_service.ensure_builtin_mcp_servers_in_db()
|
||||
|
||||
retired = await mcp_session.scalar(select(MCPServer).where(MCPServer.slug == "sequentialthinking"))
|
||||
chart = await mcp_session.scalar(select(MCPServer).where(MCPServer.slug == "mcp-server-chart"))
|
||||
assert retired is None
|
||||
assert chart is not None
|
||||
|
||||
|
||||
async def test_ensure_builtin_mcp_servers_preserves_user_server_with_retired_slug(monkeypatch, mcp_session):
|
||||
user_server = MCPServer(
|
||||
slug="sequentialthinking",
|
||||
name="用户自定义 MCP",
|
||||
description="user managed",
|
||||
transport="streamable_http",
|
||||
url="https://example.com/mcp",
|
||||
enabled=1,
|
||||
created_by="admin",
|
||||
updated_by="admin",
|
||||
)
|
||||
mcp_session.add(user_server)
|
||||
await mcp_session.commit()
|
||||
|
||||
monkeypatch.setattr(
|
||||
postgres_manager.pg_manager,
|
||||
"get_async_session_context",
|
||||
lambda: _AsyncSessionContext(mcp_session),
|
||||
)
|
||||
|
||||
await mcp_service.ensure_builtin_mcp_servers_in_db()
|
||||
|
||||
server = await mcp_session.scalar(select(MCPServer).where(MCPServer.slug == "sequentialthinking"))
|
||||
assert server is not None
|
||||
assert server.created_by == "admin"
|
||||
|
||||
|
||||
async def test_get_enabled_mcp_tools_loads_latest_config_from_db(monkeypatch):
|
||||
captured: list[dict] = []
|
||||
|
||||
@ -135,4 +220,3 @@ async def test_get_mcp_tools_sets_handle_tool_error(monkeypatch):
|
||||
assert tools[0].handle_tool_error is True
|
||||
|
||||
mcp_service.clear_mcp_cache()
|
||||
|
||||
|
||||
@ -18,9 +18,9 @@ MCP(Model Context Protocol)是扩展智能体能力的重要方式。系统
|
||||
|
||||
```json
|
||||
{
|
||||
"name": "sequentialthinking",
|
||||
"name": "custom-remote-mcp",
|
||||
"transport": "streamable_http",
|
||||
"url": "https://remote.mcpservers.org/sequentialthinking/mcp"
|
||||
"url": "https://example.com/mcp"
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
@ -38,6 +38,7 @@
|
||||
### 0.7.0 开发记录
|
||||
|
||||
<!-- 0.7.0 的内容请放在这里 -->
|
||||
- 调整内置 MCP 默认项:移除 `sequentialthinking` 的系统内置同步,启动同步时清理历史系统内置记录,保留用户手动创建的同名 MCP。
|
||||
- 图片生成能力迁移为 Skill:Qwen-Image 从内置 Python 生成工具迁移到内置 Skill `image-gen`,模型调用与图片下载在 Agent 沙盒中完成,生成结果保存到 outputs 并通过 `present_artifacts` 展示,为多图片生成模型接入复用同一产物展示链路。
|
||||
- 降低知识库路由与工具模块复杂度:示例问题生成迁移到知识库 utils,文件上传统一 100 MB 限制,URL 预处理入库路径与旧 `content_type=url` 行为收敛,并修复 uid、导出 MIME 与异常透传等路由问题。
|
||||
- 重构智能体配置语义:用户可见的 `AgentConfig` 收敛为数据库持久化的一级 `Agent`,内置 Python Agent 改为智能体后端;新增 `/api/agent` 管理与运行接口,聊天、运行任务、恢复审批和文件预览均从线程绑定的 Agent 解析运行时上下文,前端只提交 `agent_id`,并在模型配置页新增“智能体”管理页签。
|
||||
|
||||
Loading…
Reference in New Issue
Block a user