fix(mcp): resolve aliases that collide with built-in toolsets

This commit is contained in:
fangliquanflq 2026-08-14 18:48:45 +08:00
parent c896c09c42
commit fa79e52bec
3 changed files with 63 additions and 2 deletions

View File

@ -55,6 +55,22 @@ class TestGetToolset:
assert ts is not None
assert set(ts["tools"]) == {"web_search", "web_extract", "web_search_plus"}
def test_merges_colliding_mcp_alias_into_builtin_toolset(self, monkeypatch):
reg = ToolRegistry()
reg.register(
name="mcp__memory__read_graph",
toolset="mcp-memory",
schema=_make_schema("mcp__memory__read_graph", "Read graph"),
handler=_dummy_handler,
)
reg.register_toolset_alias("memory", "mcp-memory")
monkeypatch.setattr("tools.registry.registry", reg)
tools = resolve_toolset("memory")
assert "memory" in tools
assert "mcp__memory__read_graph" in tools
class TestResolveToolset:

View File

@ -460,6 +460,43 @@ class TestRegression_ToolsetScoping:
# core tools are never deferrable
assert "terminal" not in names
def test_builtin_name_collision_keeps_mcp_alias_searchable(self):
from tools.registry import registry
import model_tools
self._register("mcp__memory__read_graph", "mcp-memory")
registry.register_toolset_alias("memory", "mcp-memory")
source_result = model_tools.handle_function_call(
function_name="tool_search",
function_args={"query": "unrelated capability", "limit": 5},
enabled_toolsets=["memory"],
)
source_payload = json.loads(source_result)
assert {source["name"] for source in source_payload["available_sources"]} == {
"memory"
}
match_result = model_tools.handle_function_call(
function_name="tool_search",
function_args={"query": "read graph", "limit": 5},
enabled_toolsets=["memory"],
)
assert "mcp__memory__read_graph" in {
match["name"] for match in json.loads(match_result)["matches"]
}
call_result = model_tools.handle_function_call(
function_name="tool_call",
function_args={"name": "mcp__memory__read_graph", "arguments": {}},
enabled_toolsets=["memory"],
)
assert json.loads(call_result) == {
"ok": True,
"tool": "mcp__memory__read_graph",
}
# ---------------------------------------------------------------------------
# Catalog listing (skills-style progressive disclosure)

View File

@ -693,9 +693,17 @@ def get_toolset(name: str, *, include_registry: bool = True) -> Optional[Dict[st
return toolset if toolset else None
if toolset:
registry_toolsets = {name}
alias_target = registry.get_toolset_alias_target(name)
if alias_target:
registry_toolsets.add(alias_target)
merged_tools = sorted(
set(toolset.get("tools", []))
| set(registry.get_tool_names_for_toolset(name))
set(toolset.get("tools", [])).union(
*(
registry.get_tool_names_for_toolset(toolset_name)
for toolset_name in registry_toolsets
)
)
)
return {**toolset, "tools": merged_tools}