diff --git a/tests/tools/test_mcp_dynamic_discovery.py b/tests/tools/test_mcp_dynamic_discovery.py index 948560d8ea..76fcb907a4 100644 --- a/tests/tools/test_mcp_dynamic_discovery.py +++ b/tests/tools/test_mcp_dynamic_discovery.py @@ -36,6 +36,34 @@ class TestRegisterServerTools: assert validate_toolset("my_srv") is True assert "mcp__my_srv__my_tool" in resolve_toolset("my_srv") + def test_colliding_static_toolset_name_merges_both_tool_sets(self, mock_registry): + """An MCP server named after a built-in toolset must not be shadowed. + + Regression: an MCP server registered as `homeassistant` (colliding + with the static `homeassistant` toolset) had its tools silently + dropped because get_toolset() returned the static definition without + consulting the alias registered by _register_server_tools(). + """ + from toolsets import TOOLSETS, get_toolset, resolve_toolset + + assert "homeassistant" in TOOLSETS # collision premise + static_tools = set(TOOLSETS["homeassistant"]["tools"]) + + server = MCPServerTask("homeassistant") + server._tools = [_make_mcp_tool("get_entities", "List HA entities")] + server.session = MagicMock() + + with patch("tools.registry.registry", mock_registry): + registered = _register_server_tools("homeassistant", server, {}) + assert "mcp__homeassistant__get_entities" in registered + + ts = get_toolset("homeassistant") + # Static built-ins are still present... + assert static_tools <= set(ts["tools"]) + # ...and the MCP server's tools are no longer shadowed. + assert "mcp__homeassistant__get_entities" in ts["tools"] + assert "mcp__homeassistant__get_entities" in resolve_toolset("homeassistant") + class TestRefreshTools: """Tests for MCPServerTask._refresh_tools nuke-and-repave cycle."""