diff --git a/mellea/stdlib/tools/mcp.py b/mellea/stdlib/tools/mcp.py index 2db66c0f1e..910f2d3590 100644 --- a/mellea/stdlib/tools/mcp.py +++ b/mellea/stdlib/tools/mcp.py @@ -57,6 +57,12 @@ class MCPToolSpec: Holds everything needed to inspect or instantiate a `MelleaTool` without keeping a live session open. + Reassign `name` and `description` before calling `as_mellea_tool` to + disambiguate tools that share a name across multiple servers, or to give the + model better selection hints. Reassigning `name` only changes the name shown + to the model; the original server-side name is preserved and used for the + actual tool invocation. + Args: name (str): Tool name as registered on the server. description (str): Human-readable description from the server. @@ -75,6 +81,7 @@ def __init__( ) -> None: """Store the spec fields and the transport config.""" self.name = name + self._server_tool_name = name self.description = description self.input_schema = input_schema self._connection = connection @@ -93,7 +100,7 @@ def as_mellea_tool(self) -> MelleaTool: """ return MelleaTool( self.name, - _make_sync_call(self._connection, self.name), + _make_sync_call(self._connection, self._server_tool_name), { "type": "function", "function": { diff --git a/test/stdlib/tools/test_mcp.py b/test/stdlib/tools/test_mcp.py index 2d43096c96..a0ff6e700a 100644 --- a/test/stdlib/tools/test_mcp.py +++ b/test/stdlib/tools/test_mcp.py @@ -75,6 +75,31 @@ async def test_produces_mellea_tool_with_correct_name(self, connection): assert isinstance(tool, MelleaTool) assert tool.name == "get_me" + @pytest.mark.asyncio + async def test_reassigned_name_calls_original_server_name(self, connection): + """Renaming a spec to disambiguate still invokes the server-side name.""" + called_with: list[str] = [] + + async def _capture(tool_name, *, arguments): + called_with.append(tool_name) + return _call_result() + + session = _make_session(_make_mcp_tool("get_me")) + session.call_tool = _capture + + with _mock_open_session(session): + specs = await discover_mcp_tools(connection) + + # A user reassigns name to avoid a collision with another server's tool. + specs[0].name = "github_get_me" + + with _mock_open_session(session): + tool = specs[0].as_mellea_tool() + assert tool.name == "github_get_me" + tool.run() + + assert called_with == ["get_me"] + @pytest.mark.asyncio async def test_json_schema_structure(self, connection): schema = {"type": "object", "properties": {"q": {"type": "string"}}}