diff --git a/modules/mcp_client_manager.py b/modules/mcp_client_manager.py index c558a4b..54bf2ad 100644 --- a/modules/mcp_client_manager.py +++ b/modules/mcp_client_manager.py @@ -1073,6 +1073,11 @@ class MCPClientManager: tools: List[Dict[str, Any]] = [] alias_map: Dict[str, MCPToolBinding] = {} + # 防止“外部直接改配置文件但进程内缓存没刷新”: + # 这里在常规构建阶段主动重载一次,避免后续 discover/update 把旧缓存反写回磁盘。 + if ensure_discovery: + self.registry.reload() + servers = self.registry.list_enabled_servers() for server in servers: sid = str(server.get("id") or "").strip() diff --git a/test/test_mcp_integration.py b/test/test_mcp_integration.py index ef14d85..3a07c4d 100644 --- a/test/test_mcp_integration.py +++ b/test/test_mcp_integration.py @@ -210,6 +210,49 @@ class MCPIntegrationTest(unittest.TestCase): self.assertIn("extra_stdio", by_id, sync_result) self.assertTrue(by_id["extra_stdio"].get("success"), sync_result) + def test_build_openai_tools_reload_external_file_changes(self): + # 模拟外部直接改配置文件(registry._cache 仍是旧内容) + payload = { + "servers": [ + { + "id": "demo_stdio", + "name": "Demo MCP", + "transport": "stdio", + "enabled": True, + "command": sys.executable, + "args": [str(self.demo_script)], + "timeout_seconds": 10, + }, + { + "id": "extra_stdio", + "name": "Extra MCP", + "transport": "stdio", + "enabled": True, + "command": sys.executable, + "args": [str(self.demo_script)], + "timeout_seconds": 10, + }, + ] + } + self.registry_path.write_text(json.dumps(payload, ensure_ascii=False, indent=2), encoding="utf-8") + + tools, alias_map = self.manager.build_openai_tools( + ensure_discovery=True, + discovery_timeout_seconds=10, + ) + self.assertTrue(tools) + self.assertTrue(alias_map) + self.assertTrue( + any(getattr(binding, "server_id", "") == "extra_stdio" for binding in alias_map.values()), + alias_map, + ) + + # 关键断言:外部新增服务不应被旧缓存反写覆盖 + after = json.loads(self.registry_path.read_text(encoding="utf-8")) + ids = {str((item or {}).get("id") or "") for item in (after.get("servers") or [])} + self.assertIn("demo_stdio", ids) + self.assertIn("extra_stdio", ids) + def test_stdio_container_path_rewrite_from_workspace(self): fake_session = SimpleNamespace( mode="docker",