Feature: #6351 - Agent selection, tool resolution & framework integration
Made-with: Cursor
This commit is contained in:
+4
-3
@@ -13,6 +13,10 @@ out/
|
|||||||
.env
|
.env
|
||||||
.env.local
|
.env.local
|
||||||
.env.*.local
|
.env.*.local
|
||||||
|
.venv
|
||||||
|
/venv
|
||||||
|
tools/src/uv.lock
|
||||||
|
|
||||||
|
|
||||||
# User configuration (copied from .example)
|
# User configuration (copied from .example)
|
||||||
config.yaml
|
config.yaml
|
||||||
@@ -69,9 +73,6 @@ exports/*
|
|||||||
|
|
||||||
.claude/settings.local.json
|
.claude/settings.local.json
|
||||||
|
|
||||||
.venv
|
|
||||||
/venv
|
|
||||||
|
|
||||||
docs/github-issues/*
|
docs/github-issues/*
|
||||||
core/tests/*dumps/*
|
core/tests/*dumps/*
|
||||||
|
|
||||||
|
|||||||
@@ -584,11 +584,19 @@ class CredentialTesterAgent:
|
|||||||
self._tool_registry.load_mcp_config(mcp_config_path)
|
self._tool_registry.load_mcp_config(mcp_config_path)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
|
agent_dir = Path(__file__).parent
|
||||||
registry = MCPRegistry()
|
registry = MCPRegistry()
|
||||||
registry.initialize()
|
registry.initialize()
|
||||||
registry_configs = registry.load_agent_selection(Path(__file__).parent)
|
if (agent_dir / "mcp_registry.json").is_file():
|
||||||
|
self._tool_registry.set_mcp_registry_agent_path(agent_dir)
|
||||||
|
registry_configs, selection_max_tools = registry.load_agent_selection(agent_dir)
|
||||||
if registry_configs:
|
if registry_configs:
|
||||||
self._tool_registry.load_registry_servers(registry_configs)
|
self._tool_registry.load_registry_servers(
|
||||||
|
registry_configs,
|
||||||
|
preserve_existing_tools=True,
|
||||||
|
log_collisions=True,
|
||||||
|
max_tools=selection_max_tools,
|
||||||
|
)
|
||||||
except Exception:
|
except Exception:
|
||||||
logger.warning("MCP registry config failed to load", exc_info=True)
|
logger.warning("MCP registry config failed to load", exc_info=True)
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +0,0 @@
|
|||||||
{
|
|
||||||
"profile": "all"
|
|
||||||
}
|
|
||||||
|
|
||||||
@@ -1,44 +0,0 @@
|
|||||||
{
|
|
||||||
"servers": [
|
|
||||||
{
|
|
||||||
"name": "hive-tools",
|
|
||||||
"version": "1.0.0",
|
|
||||||
"tags": ["core", "productivity"],
|
|
||||||
"profiles": ["all", "core", "productivity"],
|
|
||||||
"mcp_config": {
|
|
||||||
"transport": "stdio",
|
|
||||||
"command": "uv",
|
|
||||||
"args": ["run", "python", "mcp_server.py", "--stdio"],
|
|
||||||
"cwd": "tools",
|
|
||||||
"description": "Hive tools MCP server providing core utilities"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "coder-tools",
|
|
||||||
"version": "1.0.0",
|
|
||||||
"tags": ["coding", "productivity"],
|
|
||||||
"profiles": ["all", "coding", "productivity"],
|
|
||||||
"mcp_config": {
|
|
||||||
"transport": "stdio",
|
|
||||||
"command": "uv",
|
|
||||||
"args": ["run", "python", "coder_tools_server.py", "--stdio"],
|
|
||||||
"cwd": "tools",
|
|
||||||
"description": "Unsandboxed file/code tools for code generation"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "tools",
|
|
||||||
"version": "1.0.0",
|
|
||||||
"tags": ["web", "productivity"],
|
|
||||||
"profiles": ["all", "general"],
|
|
||||||
"mcp_config": {
|
|
||||||
"transport": "stdio",
|
|
||||||
"command": "uv",
|
|
||||||
"args": ["run", "python", "mcp_server.py", "--stdio"],
|
|
||||||
"cwd": "tools",
|
|
||||||
"description": "Aden tools MCP server providing web/file utilities"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
|
|
||||||
@@ -401,14 +401,16 @@ class MCPRegistry:
|
|||||||
|
|
||||||
# ── load_agent_selection ────────────────────────────────────────
|
# ── load_agent_selection ────────────────────────────────────────
|
||||||
|
|
||||||
def load_agent_selection(self, agent_path: Path) -> list[dict[str, Any]]:
|
def load_agent_selection(self, agent_path: Path) -> tuple[list[dict[str, Any]], int | None]:
|
||||||
"""Load mcp_registry.json from an agent directory and resolve servers.
|
"""Load mcp_registry.json from an agent directory and resolve servers.
|
||||||
|
|
||||||
Returns list of plain dicts compatible with ToolRegistry.register_mcp_server().
|
Returns:
|
||||||
|
(server_config_dicts, max_tools) for :meth:`ToolRegistry.load_registry_servers`.
|
||||||
|
``max_tools`` is ``None`` when omitted or invalid in JSON.
|
||||||
"""
|
"""
|
||||||
registry_json_path = agent_path / "mcp_registry.json"
|
registry_json_path = agent_path / "mcp_registry.json"
|
||||||
if not registry_json_path.exists():
|
if not registry_json_path.exists():
|
||||||
return []
|
return [], None
|
||||||
|
|
||||||
selection = json.loads(registry_json_path.read_text(encoding="utf-8"))
|
selection = json.loads(registry_json_path.read_text(encoding="utf-8"))
|
||||||
|
|
||||||
@@ -437,15 +439,16 @@ class MCPRegistry:
|
|||||||
continue
|
continue
|
||||||
validated[field] = value
|
validated[field] = value
|
||||||
|
|
||||||
|
max_tools = validated.get("max_tools")
|
||||||
configs = self.resolve_for_agent(
|
configs = self.resolve_for_agent(
|
||||||
include=validated.get("include"),
|
include=validated.get("include"),
|
||||||
tags=validated.get("tags"),
|
tags=validated.get("tags"),
|
||||||
exclude=validated.get("exclude"),
|
exclude=validated.get("exclude"),
|
||||||
profile=validated.get("profile"),
|
profile=validated.get("profile"),
|
||||||
max_tools=validated.get("max_tools"),
|
max_tools=max_tools,
|
||||||
versions=validated.get("versions"),
|
versions=validated.get("versions"),
|
||||||
)
|
)
|
||||||
return [self._server_config_to_dict(c) for c in configs]
|
return [self._server_config_to_dict(c) for c in configs], max_tools
|
||||||
|
|
||||||
# ── resolve_for_agent ───────────────────────────────────────────
|
# ── resolve_for_agent ───────────────────────────────────────────
|
||||||
|
|
||||||
@@ -552,12 +555,14 @@ class MCPRegistry:
|
|||||||
)
|
)
|
||||||
continue
|
continue
|
||||||
|
|
||||||
# Check tool count cap before adding (FR-56)
|
# Check tool count cap before adding (FR-56), using manifest tool list when present.
|
||||||
|
# When ``tools`` is empty (e.g. ``add_local``), counts are unknown here—callers should
|
||||||
|
# pass the same ``max_tools`` to ToolRegistry.load_registry_servers to cap registration.
|
||||||
manifest_tools = manifest.get("tools", [])
|
manifest_tools = manifest.get("tools", [])
|
||||||
server_tool_count = len(manifest_tools)
|
server_tool_count = len(manifest_tools)
|
||||||
if max_tools is not None and server_tool_count == 0:
|
if max_tools is not None and server_tool_count == 0:
|
||||||
logger.debug(
|
logger.debug(
|
||||||
"Server '%s' has no declared tools in manifest, skipping max_tools check",
|
"Server '%s' has no tools list in manifest; max_tools enforced at registration",
|
||||||
name,
|
name,
|
||||||
)
|
)
|
||||||
elif max_tools is not None and total_tools + server_tool_count > max_tools:
|
elif max_tools is not None and total_tools + server_tool_count > max_tools:
|
||||||
|
|||||||
@@ -1429,12 +1429,18 @@ class AgentRunner:
|
|||||||
|
|
||||||
def _load_registry_mcp_servers(self, agent_path: Path) -> None:
|
def _load_registry_mcp_servers(self, agent_path: Path) -> None:
|
||||||
"""Load and register MCP servers selected via ``mcp_registry.json``."""
|
"""Load and register MCP servers selected via ``mcp_registry.json``."""
|
||||||
|
registry_json = agent_path / "mcp_registry.json"
|
||||||
|
if registry_json.is_file():
|
||||||
|
self._tool_registry.set_mcp_registry_agent_path(agent_path)
|
||||||
|
else:
|
||||||
|
self._tool_registry.set_mcp_registry_agent_path(None)
|
||||||
|
|
||||||
from framework.runner.mcp_registry import MCPRegistry
|
from framework.runner.mcp_registry import MCPRegistry
|
||||||
|
|
||||||
try:
|
try:
|
||||||
registry = MCPRegistry()
|
registry = MCPRegistry()
|
||||||
registry.initialize()
|
registry.initialize()
|
||||||
server_configs = registry.load_agent_selection(agent_path)
|
server_configs, selection_max_tools = registry.load_agent_selection(agent_path)
|
||||||
except Exception as exc:
|
except Exception as exc:
|
||||||
logger.warning(
|
logger.warning(
|
||||||
"Failed to load MCP registry servers for '%s': %s",
|
"Failed to load MCP registry servers for '%s': %s",
|
||||||
@@ -1446,7 +1452,12 @@ class AgentRunner:
|
|||||||
if not server_configs:
|
if not server_configs:
|
||||||
return
|
return
|
||||||
|
|
||||||
results = self._tool_registry.load_registry_servers(server_configs)
|
results = self._tool_registry.load_registry_servers(
|
||||||
|
server_configs,
|
||||||
|
preserve_existing_tools=True,
|
||||||
|
log_collisions=True,
|
||||||
|
max_tools=selection_max_tools,
|
||||||
|
)
|
||||||
loaded = [result for result in results if result["status"] == "loaded"]
|
loaded = [result for result in results if result["status"] == "loaded"]
|
||||||
skipped = [result for result in results if result["status"] != "loaded"]
|
skipped = [result for result in results if result["status"] != "loaded"]
|
||||||
|
|
||||||
|
|||||||
@@ -90,9 +90,16 @@ async def create_queen(
|
|||||||
try:
|
try:
|
||||||
registry = MCPRegistry()
|
registry = MCPRegistry()
|
||||||
registry.initialize()
|
registry.initialize()
|
||||||
registry_configs = registry.load_agent_selection(queen_pkg_dir)
|
if (queen_pkg_dir / "mcp_registry.json").is_file():
|
||||||
|
queen_registry.set_mcp_registry_agent_path(queen_pkg_dir)
|
||||||
|
registry_configs, selection_max_tools = registry.load_agent_selection(queen_pkg_dir)
|
||||||
if registry_configs:
|
if registry_configs:
|
||||||
results = queen_registry.load_registry_servers(registry_configs)
|
results = queen_registry.load_registry_servers(
|
||||||
|
registry_configs,
|
||||||
|
preserve_existing_tools=True,
|
||||||
|
log_collisions=True,
|
||||||
|
max_tools=selection_max_tools,
|
||||||
|
)
|
||||||
logger.info("Queen: loaded MCP registry servers: %s", results)
|
logger.info("Queen: loaded MCP registry servers: %s", results)
|
||||||
except Exception:
|
except Exception:
|
||||||
logger.warning("Queen: MCP registry config failed to load", exc_info=True)
|
logger.warning("Queen: MCP registry config failed to load", exc_info=True)
|
||||||
|
|||||||
@@ -32,7 +32,7 @@ class _FakeRegistry:
|
|||||||
|
|
||||||
def load_agent_selection(self, agent_path: Path):
|
def load_agent_selection(self, agent_path: Path):
|
||||||
self.loaded_paths.append(agent_path)
|
self.loaded_paths.append(agent_path)
|
||||||
return list(self._returned_configs)
|
return list(self._returned_configs), None
|
||||||
|
|
||||||
|
|
||||||
def test_agent_runner_loads_registry_selected_servers(tmp_path, monkeypatch):
|
def test_agent_runner_loads_registry_selected_servers(tmp_path, monkeypatch):
|
||||||
@@ -61,7 +61,7 @@ def test_agent_runner_loads_registry_selected_servers(tmp_path, monkeypatch):
|
|||||||
monkeypatch.setattr(AgentRunner, "_resolve_default_model", staticmethod(lambda: "test-model"))
|
monkeypatch.setattr(AgentRunner, "_resolve_default_model", staticmethod(lambda: "test-model"))
|
||||||
monkeypatch.setattr(
|
monkeypatch.setattr(
|
||||||
"framework.runner.tool_registry.ToolRegistry.register_mcp_server",
|
"framework.runner.tool_registry.ToolRegistry.register_mcp_server",
|
||||||
lambda self, server_config, use_connection_manager=True: (
|
lambda self, server_config, use_connection_manager=True, **kwargs: (
|
||||||
registered.append(server_config) or 1
|
registered.append(server_config) or 1
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
@@ -95,7 +95,7 @@ def test_agent_runner_skips_registry_when_no_servers_selected(tmp_path, monkeypa
|
|||||||
monkeypatch.setattr(AgentRunner, "_resolve_default_model", staticmethod(lambda: "test-model"))
|
monkeypatch.setattr(AgentRunner, "_resolve_default_model", staticmethod(lambda: "test-model"))
|
||||||
monkeypatch.setattr(
|
monkeypatch.setattr(
|
||||||
"framework.runner.tool_registry.ToolRegistry.register_mcp_server",
|
"framework.runner.tool_registry.ToolRegistry.register_mcp_server",
|
||||||
lambda self, server_config, use_connection_manager=True: (
|
lambda self, server_config, use_connection_manager=True, **kwargs: (
|
||||||
registered.append(server_config) or 1
|
registered.append(server_config) or 1
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
@@ -135,7 +135,7 @@ def test_agent_runner_logs_actual_registry_load_results(tmp_path, monkeypatch):
|
|||||||
monkeypatch.setattr(AgentRunner, "_resolve_default_model", staticmethod(lambda: "test-model"))
|
monkeypatch.setattr(AgentRunner, "_resolve_default_model", staticmethod(lambda: "test-model"))
|
||||||
monkeypatch.setattr(
|
monkeypatch.setattr(
|
||||||
"framework.runner.tool_registry.ToolRegistry.load_registry_servers",
|
"framework.runner.tool_registry.ToolRegistry.load_registry_servers",
|
||||||
lambda self, server_configs: [
|
lambda self, server_configs, **kwargs: [
|
||||||
{"server": "jira", "status": "loaded", "tools_loaded": 2, "skipped_reason": None},
|
{"server": "jira", "status": "loaded", "tools_loaded": 2, "skipped_reason": None},
|
||||||
{
|
{
|
||||||
"server": "slack",
|
"server": "slack",
|
||||||
@@ -223,7 +223,7 @@ def test_integration_real_registry_to_agent_runner(tmp_path, monkeypatch):
|
|||||||
registered: list[dict] = []
|
registered: list[dict] = []
|
||||||
monkeypatch.setattr(
|
monkeypatch.setattr(
|
||||||
"framework.runner.tool_registry.ToolRegistry.register_mcp_server",
|
"framework.runner.tool_registry.ToolRegistry.register_mcp_server",
|
||||||
lambda self, server_config, use_connection_manager=True: (
|
lambda self, server_config, use_connection_manager=True, **kwargs: (
|
||||||
registered.append(server_config) or 1
|
registered.append(server_config) or 1
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -6,6 +6,39 @@ from framework.runner.mcp_client import MCPTool
|
|||||||
from framework.runner.tool_registry import ToolRegistry
|
from framework.runner.tool_registry import ToolRegistry
|
||||||
|
|
||||||
|
|
||||||
|
def _patch_connection_manager_for_fake_stdio(monkeypatch, tool_map: dict[str, list[str]]) -> None:
|
||||||
|
"""Avoid spawning real stdio MCP processes; return in-memory clients per server name."""
|
||||||
|
|
||||||
|
class FakeMCPClient:
|
||||||
|
def __init__(self, config: Any):
|
||||||
|
self.config = config
|
||||||
|
|
||||||
|
def connect(self) -> None:
|
||||||
|
return
|
||||||
|
|
||||||
|
def disconnect(self) -> None:
|
||||||
|
return
|
||||||
|
|
||||||
|
def list_tools(self) -> list[MCPTool]:
|
||||||
|
names = tool_map.get(self.config.name, [])
|
||||||
|
return [_make_tool(n, self.config.name) for n in names]
|
||||||
|
|
||||||
|
def call_tool(self, tool_name: str, arguments: dict[str, Any]) -> Any:
|
||||||
|
raise NotImplementedError
|
||||||
|
|
||||||
|
class FakeManager:
|
||||||
|
def acquire(self, config: Any) -> FakeMCPClient:
|
||||||
|
return FakeMCPClient(config)
|
||||||
|
|
||||||
|
def release(self, _server_name: str) -> None:
|
||||||
|
return
|
||||||
|
|
||||||
|
monkeypatch.setattr(
|
||||||
|
"framework.runner.mcp_connection_manager.MCPConnectionManager.get_instance",
|
||||||
|
lambda: FakeManager(),
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
def _make_tool(name: str, server_name: str) -> MCPTool:
|
def _make_tool(name: str, server_name: str) -> MCPTool:
|
||||||
return MCPTool(
|
return MCPTool(
|
||||||
name=name,
|
name=name,
|
||||||
@@ -25,46 +58,21 @@ def test_registry_first_wins_collisions(monkeypatch):
|
|||||||
"s1": ["tool_common", "tool_hive"],
|
"s1": ["tool_common", "tool_hive"],
|
||||||
"s2": ["tool_common", "tool_coder"],
|
"s2": ["tool_common", "tool_coder"],
|
||||||
}
|
}
|
||||||
|
_patch_connection_manager_for_fake_stdio(monkeypatch, tool_map)
|
||||||
|
|
||||||
from framework.runner import mcp_client as mcp_client_mod
|
|
||||||
|
|
||||||
class FakeMCPClient:
|
|
||||||
def __init__(self, config: Any):
|
|
||||||
self.config = config
|
|
||||||
self._tools: list[MCPTool] = []
|
|
||||||
|
|
||||||
def connect(self) -> None:
|
|
||||||
return
|
|
||||||
|
|
||||||
def disconnect(self) -> None:
|
|
||||||
return
|
|
||||||
|
|
||||||
def list_tools(self) -> list[MCPTool]:
|
|
||||||
names = tool_map.get(self.config.name, [])
|
|
||||||
return [_make_tool(n, self.config.name) for n in names]
|
|
||||||
|
|
||||||
def call_tool(self, tool_name: str, arguments: dict[str, Any]) -> Any:
|
|
||||||
raise NotImplementedError
|
|
||||||
|
|
||||||
monkeypatch.setattr(mcp_client_mod, "MCPClient", FakeMCPClient)
|
|
||||||
|
|
||||||
from framework.runner import mcp_registry_resolver as resolver_mod
|
|
||||||
|
|
||||||
# Return server configs in the desired deterministic order.
|
|
||||||
resolved_servers = [
|
resolved_servers = [
|
||||||
{"name": "s1", "transport": "stdio", "command": "fake", "args": [], "cwd": None},
|
{"name": "s1", "transport": "stdio", "command": "fake", "args": [], "cwd": None},
|
||||||
{"name": "s2", "transport": "stdio", "command": "fake", "args": [], "cwd": None},
|
{"name": "s2", "transport": "stdio", "command": "fake", "args": [], "cwd": None},
|
||||||
]
|
]
|
||||||
monkeypatch.setattr(
|
|
||||||
resolver_mod,
|
|
||||||
"resolve_registry_servers",
|
|
||||||
lambda **kwargs: resolved_servers,
|
|
||||||
)
|
|
||||||
|
|
||||||
registry = ToolRegistry()
|
registry = ToolRegistry()
|
||||||
added = registry.load_registry_servers(include=["s1"], tags=None, exclude=None, profile=None)
|
registry.load_registry_servers(
|
||||||
|
resolved_servers,
|
||||||
|
log_summary=False,
|
||||||
|
preserve_existing_tools=True,
|
||||||
|
log_collisions=True,
|
||||||
|
)
|
||||||
|
|
||||||
assert added == 3 # tool_common + tool_hive + tool_coder
|
|
||||||
assert registry.has_tool("tool_common") is True
|
assert registry.has_tool("tool_common") is True
|
||||||
assert registry.has_tool("tool_hive") is True
|
assert registry.has_tool("tool_hive") is True
|
||||||
assert registry.has_tool("tool_coder") is True
|
assert registry.has_tool("tool_coder") is True
|
||||||
@@ -81,48 +89,25 @@ def test_registry_precedence_over_existing_mcp_servers(monkeypatch):
|
|||||||
"s1": ["tool_common", "tool_hive"],
|
"s1": ["tool_common", "tool_hive"],
|
||||||
"s2": ["tool_common", "tool_coder"],
|
"s2": ["tool_common", "tool_coder"],
|
||||||
}
|
}
|
||||||
|
_patch_connection_manager_for_fake_stdio(monkeypatch, tool_map)
|
||||||
from framework.runner import mcp_client as mcp_client_mod
|
|
||||||
|
|
||||||
class FakeMCPClient:
|
|
||||||
def __init__(self, config: Any):
|
|
||||||
self.config = config
|
|
||||||
|
|
||||||
def connect(self) -> None:
|
|
||||||
return
|
|
||||||
|
|
||||||
def disconnect(self) -> None:
|
|
||||||
return
|
|
||||||
|
|
||||||
def list_tools(self) -> list[MCPTool]:
|
|
||||||
names = tool_map.get(self.config.name, [])
|
|
||||||
return [_make_tool(n, self.config.name) for n in names]
|
|
||||||
|
|
||||||
def call_tool(self, tool_name: str, arguments: dict[str, Any]) -> Any:
|
|
||||||
raise NotImplementedError
|
|
||||||
|
|
||||||
monkeypatch.setattr(mcp_client_mod, "MCPClient", FakeMCPClient)
|
|
||||||
|
|
||||||
from framework.runner import mcp_registry_resolver as resolver_mod
|
|
||||||
|
|
||||||
resolved_servers = [
|
resolved_servers = [
|
||||||
{"name": "s1", "transport": "stdio", "command": "fake", "args": [], "cwd": None},
|
{"name": "s1", "transport": "stdio", "command": "fake", "args": [], "cwd": None},
|
||||||
{"name": "s2", "transport": "stdio", "command": "fake", "args": [], "cwd": None},
|
{"name": "s2", "transport": "stdio", "command": "fake", "args": [], "cwd": None},
|
||||||
]
|
]
|
||||||
monkeypatch.setattr(
|
|
||||||
resolver_mod,
|
|
||||||
"resolve_registry_servers",
|
|
||||||
lambda **kwargs: resolved_servers,
|
|
||||||
)
|
|
||||||
|
|
||||||
registry = ToolRegistry()
|
registry = ToolRegistry()
|
||||||
registry.register_mcp_server(
|
registry.register_mcp_server(
|
||||||
{"name": "pre", "transport": "stdio", "command": "fake", "args": [], "cwd": None}
|
{"name": "pre", "transport": "stdio", "command": "fake", "args": [], "cwd": None}
|
||||||
)
|
)
|
||||||
|
|
||||||
added = registry.load_registry_servers(include=None, tags=None, exclude=None, profile=None)
|
registry.load_registry_servers(
|
||||||
|
resolved_servers,
|
||||||
|
log_summary=False,
|
||||||
|
preserve_existing_tools=True,
|
||||||
|
log_collisions=True,
|
||||||
|
)
|
||||||
|
|
||||||
assert added == 2 # only tool_hive + tool_coder; tool_common is preserved from "pre"
|
|
||||||
assert registry.get_server_tool_names("pre") == {"tool_common", "tool_pre"}
|
assert registry.get_server_tool_names("pre") == {"tool_common", "tool_pre"}
|
||||||
assert registry.get_server_tool_names("s1") == {"tool_hive"}
|
assert registry.get_server_tool_names("s1") == {"tool_hive"}
|
||||||
assert registry.get_server_tool_names("s2") == {"tool_coder"}
|
assert registry.get_server_tool_names("s2") == {"tool_coder"}
|
||||||
@@ -135,50 +120,21 @@ def test_registry_max_tools_cap(monkeypatch):
|
|||||||
"s1": ["tool_a", "tool_b"],
|
"s1": ["tool_a", "tool_b"],
|
||||||
"s2": ["tool_c"],
|
"s2": ["tool_c"],
|
||||||
}
|
}
|
||||||
|
_patch_connection_manager_for_fake_stdio(monkeypatch, tool_map)
|
||||||
from framework.runner import mcp_client as mcp_client_mod
|
|
||||||
|
|
||||||
class FakeMCPClient:
|
|
||||||
def __init__(self, config: Any):
|
|
||||||
self.config = config
|
|
||||||
|
|
||||||
def connect(self) -> None:
|
|
||||||
return
|
|
||||||
|
|
||||||
def disconnect(self) -> None:
|
|
||||||
return
|
|
||||||
|
|
||||||
def list_tools(self) -> list[MCPTool]:
|
|
||||||
names = tool_map.get(self.config.name, [])
|
|
||||||
return [_make_tool(n, self.config.name) for n in names]
|
|
||||||
|
|
||||||
def call_tool(self, tool_name: str, arguments: dict[str, Any]) -> Any:
|
|
||||||
raise NotImplementedError
|
|
||||||
|
|
||||||
monkeypatch.setattr(mcp_client_mod, "MCPClient", FakeMCPClient)
|
|
||||||
|
|
||||||
from framework.runner import mcp_registry_resolver as resolver_mod
|
|
||||||
|
|
||||||
resolved_servers = [
|
resolved_servers = [
|
||||||
{"name": "s1", "transport": "stdio", "command": "fake", "args": [], "cwd": None},
|
{"name": "s1", "transport": "stdio", "command": "fake", "args": [], "cwd": None},
|
||||||
{"name": "s2", "transport": "stdio", "command": "fake", "args": [], "cwd": None},
|
{"name": "s2", "transport": "stdio", "command": "fake", "args": [], "cwd": None},
|
||||||
]
|
]
|
||||||
monkeypatch.setattr(
|
|
||||||
resolver_mod,
|
|
||||||
"resolve_registry_servers",
|
|
||||||
lambda **kwargs: resolved_servers,
|
|
||||||
)
|
|
||||||
|
|
||||||
registry = ToolRegistry()
|
registry = ToolRegistry()
|
||||||
added = registry.load_registry_servers(
|
registry.load_registry_servers(
|
||||||
include=None,
|
resolved_servers,
|
||||||
tags=None,
|
log_summary=False,
|
||||||
exclude=None,
|
preserve_existing_tools=True,
|
||||||
profile=None,
|
|
||||||
max_tools=2,
|
max_tools=2,
|
||||||
)
|
)
|
||||||
|
|
||||||
assert added == 2
|
|
||||||
assert registry.has_tool("tool_a") is True
|
assert registry.has_tool("tool_a") is True
|
||||||
assert registry.has_tool("tool_b") is True
|
assert registry.has_tool("tool_b") is True
|
||||||
assert registry.has_tool("tool_c") is False
|
assert registry.has_tool("tool_c") is False
|
||||||
|
|||||||
@@ -214,7 +214,7 @@ def test_load_registry_servers_retries_when_registration_returns_zero(monkeypatc
|
|||||||
registry = ToolRegistry()
|
registry = ToolRegistry()
|
||||||
attempts = {"count": 0}
|
attempts = {"count": 0}
|
||||||
|
|
||||||
def fake_register(server_config, use_connection_manager=True):
|
def fake_register(server_config, use_connection_manager=True, **kwargs):
|
||||||
attempts["count"] += 1
|
attempts["count"] += 1
|
||||||
return 0 if attempts["count"] == 1 else 2
|
return 0 if attempts["count"] == 1 else 2
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1 @@
|
|||||||
{
|
{ "include": ["hive-tools"] }
|
||||||
"include": ["jira"],
|
|
||||||
"max_tools": 10
|
|
||||||
}
|
|
||||||
Reference in New Issue
Block a user