Files
hhzscreate c16175e4f4 修复以下问题:
(1)新增 MCP server 工具数为 0,其原因为重连块被误缩进进工具清理循环
(2)测试连接成功、但持久连接 0 tools ,mcp连接在 A 任务打开、B 任务关闭,违反 anyio 同任务约束。改为每个 server 由“持有者任务”独占 open/hold/close。
(3)中途改 MCP 配置,老对话不加载新工具,改为每轮对话前全量对齐(增/删/换)。
(4)停止后全局开关仍开,状态轮询把它又连回来。导致无法手动单独停止server。改为停止后全局开关仍开,状态轮询把它又连回来
2026-07-12 00:56:47 +08:00

122 lines
3.6 KiB
Python
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""reconcile_registry_sync 回归测试。
场景:MCP 配置中途变化后,长生命周期的会话级注册表能否被“全量对齐”
(增/删/换),从而修复“老对话不加载新工具”。
"""
import sys
import types
from pathlib import Path
sys.path.insert(0, str(Path(__file__).resolve().parents[1]))
from backend.modules.mcp.client import McpClientManager
from backend.modules.tools.registry import ToolRegistry
from backend.modules.tools.base import Tool
class DummyTool(Tool):
def __init__(self, name):
self._name = name
@property
def name(self):
return self._name
@property
def description(self):
return self._name
@property
def parameters(self):
return {"type": "object", "properties": {}}
async def execute(self, **kwargs):
return self._name
def _mgr_with(wrappers):
mgr = McpClientManager()
mgr._connected = True
mgr._tool_wrappers = dict(wrappers)
return mgr
def test_reconcile_adds_new_tools():
"""老对话注册表里没有的新 MCP 工具,应被补齐。"""
reg = ToolRegistry()
reg.register(DummyTool("shell")) # 非 MCP 本地工具
a, b = DummyTool("mcp_demo_add"), DummyTool("mcp_demo_echo")
mgr = _mgr_with({"mcp_demo_add": a, "mcp_demo_echo": b})
changed = mgr.reconcile_registry_sync(reg)
assert changed == 2
assert reg.has_tool("mcp_demo_add") and reg.has_tool("mcp_demo_echo")
assert reg.has_tool("shell"), "本地工具不受影响"
def test_reconcile_removes_deleted_tools():
"""配置里删掉的 MCP 工具,应从注册表移除;本地工具保留。"""
reg = ToolRegistry()
reg.register(DummyTool("shell"))
reg.register(DummyTool("mcp_demo_add"))
reg.register(DummyTool("mcp_old_gone")) # manager 已无
keep = DummyTool("mcp_demo_add")
mgr = _mgr_with({"mcp_demo_add": keep})
changed = mgr.reconcile_registry_sync(reg)
# mcp_old_gone 被移除(1),mcp_demo_add 对象不同被替换(移除+新增=2)
assert not reg.has_tool("mcp_old_gone")
assert reg.has_tool("mcp_demo_add")
assert reg.get_tool("mcp_demo_add") is keep, "应替换为 manager 当前的 wrapper"
assert reg.has_tool("shell")
assert changed == 3
def test_reconcile_replaces_reconnected_wrapper():
"""重连后 wrapper 对象更换:注册表里的旧对象应被替换为新对象。"""
reg = ToolRegistry()
old = DummyTool("mcp_demo_add")
reg.register(old)
new = DummyTool("mcp_demo_add")
mgr = _mgr_with({"mcp_demo_add": new})
mgr.reconcile_registry_sync(reg)
assert reg.get_tool("mcp_demo_add") is new
assert reg.get_tool("mcp_demo_add") is not old
def test_reconcile_when_disconnected_removes_all_mcp():
"""MCP 未连接:注册表里的 MCP 工具应全部移除,本地工具保留。"""
reg = ToolRegistry()
reg.register(DummyTool("shell"))
reg.register(DummyTool("mcp_demo_add"))
mgr = McpClientManager()
mgr._connected = False
mgr._tool_wrappers = {}
changed = mgr.reconcile_registry_sync(reg)
assert not reg.has_tool("mcp_demo_add")
assert reg.has_tool("shell")
assert changed == 1
def test_reconcile_idempotent():
"""已对齐时再次调用不产生变更。"""
reg = ToolRegistry()
a = DummyTool("mcp_demo_add")
mgr = _mgr_with({"mcp_demo_add": a})
assert mgr.reconcile_registry_sync(reg) == 1
assert mgr.reconcile_registry_sync(reg) == 0
assert mgr.reconcile_registry_sync(reg) == 0
if __name__ == "__main__":
import pytest
sys.exit(pytest.main([__file__, "-v"]))