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 Server configurations (Imported to DB on first run)
|
||||||
_DEFAULT_MCP_SERVERS = {
|
_DEFAULT_MCP_SERVERS = {
|
||||||
"sequentialthinking": {
|
|
||||||
"url": "https://remote.mcpservers.org/sequentialthinking/mcp",
|
|
||||||
"transport": "streamable_http",
|
|
||||||
"description": "顺序思考工具,帮助 AI 将复杂问题分解为多个步骤",
|
|
||||||
"icon": "🧠",
|
|
||||||
"tags": ["内置", "AI"],
|
|
||||||
},
|
|
||||||
"mcp-server-chart": {
|
"mcp-server-chart": {
|
||||||
"command": "npx",
|
"command": "npx",
|
||||||
"args": ["-y", "@antv/mcp-server-chart"],
|
"args": ["-y", "@antv/mcp-server-chart"],
|
||||||
@ -55,6 +48,8 @@ _DEFAULT_MCP_SERVERS = {
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_RETIRED_BUILTIN_MCP_SERVER_SLUGS = ("sequentialthinking",)
|
||||||
|
|
||||||
_SYNCED_MCP_FIELDS = (
|
_SYNCED_MCP_FIELDS = (
|
||||||
"description",
|
"description",
|
||||||
"transport",
|
"transport",
|
||||||
@ -81,6 +76,17 @@ async def ensure_builtin_mcp_servers_in_db() -> None:
|
|||||||
try:
|
try:
|
||||||
async with pg_manager.get_async_session_context() as session:
|
async with pg_manager.get_async_session_context() as session:
|
||||||
any_changed = False
|
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():
|
for slug, config in _DEFAULT_MCP_SERVERS.items():
|
||||||
result = await session.execute(select(MCPServer).filter(MCPServer.slug == slug))
|
result = await session.execute(select(MCPServer).filter(MCPServer.slug == slug))
|
||||||
existing = result.scalar_one_or_none()
|
existing = result.scalar_one_or_none()
|
||||||
|
|||||||
@ -49,7 +49,7 @@ def test_update_mcp_server_status(monkeypatch):
|
|||||||
self.enabled = enabled
|
self.enabled = enabled
|
||||||
|
|
||||||
def to_dict(self):
|
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):
|
async def fake_set_server_enabled(db, name, enabled, updated_by=None):
|
||||||
captured["name"] = name
|
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)
|
monkeypatch.setattr("server.routers.mcp_router.set_server_enabled", fake_set_server_enabled)
|
||||||
|
|
||||||
client = TestClient(_build_app())
|
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
|
assert resp.status_code == 200, resp.text
|
||||||
payload = resp.json()
|
payload = resp.json()
|
||||||
assert payload["success"] is True
|
assert payload["success"] is True
|
||||||
assert payload["enabled"] is False
|
assert payload["enabled"] is False
|
||||||
assert payload["data"]["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):
|
def test_update_mcp_server_status_not_found(monkeypatch):
|
||||||
|
|||||||
@ -2,7 +2,37 @@ from __future__ import annotations
|
|||||||
|
|
||||||
from types import SimpleNamespace
|
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.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:
|
class _FakeClient:
|
||||||
@ -13,6 +43,61 @@ class _FakeClient:
|
|||||||
return self._tools
|
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):
|
async def test_get_enabled_mcp_tools_loads_latest_config_from_db(monkeypatch):
|
||||||
captured: list[dict] = []
|
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
|
assert tools[0].handle_tool_error is True
|
||||||
|
|
||||||
mcp_service.clear_mcp_cache()
|
mcp_service.clear_mcp_cache()
|
||||||
|
|
||||||
|
|||||||
@ -18,9 +18,9 @@ MCP(Model Context Protocol)是扩展智能体能力的重要方式。系统
|
|||||||
|
|
||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
"name": "sequentialthinking",
|
"name": "custom-remote-mcp",
|
||||||
"transport": "streamable_http",
|
"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 开发记录
|
||||||
|
|
||||||
<!-- 0.7.0 的内容请放在这里 -->
|
<!-- 0.7.0 的内容请放在这里 -->
|
||||||
|
- 调整内置 MCP 默认项:移除 `sequentialthinking` 的系统内置同步,启动同步时清理历史系统内置记录,保留用户手动创建的同名 MCP。
|
||||||
- 图片生成能力迁移为 Skill:Qwen-Image 从内置 Python 生成工具迁移到内置 Skill `image-gen`,模型调用与图片下载在 Agent 沙盒中完成,生成结果保存到 outputs 并通过 `present_artifacts` 展示,为多图片生成模型接入复用同一产物展示链路。
|
- 图片生成能力迁移为 Skill:Qwen-Image 从内置 Python 生成工具迁移到内置 Skill `image-gen`,模型调用与图片下载在 Agent 沙盒中完成,生成结果保存到 outputs 并通过 `present_artifacts` 展示,为多图片生成模型接入复用同一产物展示链路。
|
||||||
- 降低知识库路由与工具模块复杂度:示例问题生成迁移到知识库 utils,文件上传统一 100 MB 限制,URL 预处理入库路径与旧 `content_type=url` 行为收敛,并修复 uid、导出 MIME 与异常透传等路由问题。
|
- 降低知识库路由与工具模块复杂度:示例问题生成迁移到知识库 utils,文件上传统一 100 MB 限制,URL 预处理入库路径与旧 `content_type=url` 行为收敛,并修复 uid、导出 MIME 与异常透传等路由问题。
|
||||||
- 重构智能体配置语义:用户可见的 `AgentConfig` 收敛为数据库持久化的一级 `Agent`,内置 Python Agent 改为智能体后端;新增 `/api/agent` 管理与运行接口,聊天、运行任务、恢复审批和文件预览均从线程绑定的 Agent 解析运行时上下文,前端只提交 `agent_id`,并在模型配置页新增“智能体”管理页签。
|
- 重构智能体配置语义:用户可见的 `AgentConfig` 收敛为数据库持久化的一级 `Agent`,内置 Python Agent 改为智能体后端;新增 `/api/agent` 管理与运行接口,聊天、运行任务、恢复审批和文件预览均从线程绑定的 Agent 解析运行时上下文,前端只提交 `agent_id`,并在模型配置页新增“智能体”管理页签。
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user