Skip to content

Commit dd08f6c

Browse files
committed
Fix connector FastMCP tool calls
- use fastmcp call_tool with meta fallback instead of call_tool_mcp - normalize results to mcp CallToolResult and update connector test
1 parent f5ce905 commit dd08f6c

File tree

2 files changed

+28
-5
lines changed

2 files changed

+28
-5
lines changed

hud/environment/connection.py

Lines changed: 24 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44

55
import logging
66
from enum import Enum
7-
from typing import TYPE_CHECKING, Any
7+
from typing import TYPE_CHECKING, Any, cast
88

99
import mcp.types as mcp_types
1010

@@ -200,10 +200,31 @@ async def call_tool(
200200
trace_id = get_current_trace_id()
201201
meta = {"_hud_trace_id": trace_id} if trace_id else None
202202

203+
client = cast(Any, self.client)
203204
try:
204-
return await self.client.call_tool_mcp(name, args, _meta=meta)
205+
result = await client.call_tool(name=name, arguments=args, _meta=meta)
205206
except TypeError:
206-
return await self.client.call_tool_mcp(name, args)
207+
# Fallback for clients that don't accept _meta
208+
result = await client.call_tool(name=name, arguments=args)
209+
210+
# FastMCP and mcp-python use slightly different result shapes/types.
211+
# Normalize to mcp.types.CallToolResult for the rest of HUD.
212+
is_error = getattr(result, "isError", None)
213+
if is_error is None:
214+
is_error = getattr(result, "is_error", False)
215+
structured = getattr(result, "structuredContent", None)
216+
if structured is None:
217+
structured = getattr(result, "structured_content", None)
218+
219+
content = getattr(result, "content", None)
220+
if content is None:
221+
content = []
222+
223+
return mcp_types.CallToolResult(
224+
content=content,
225+
isError=bool(is_error),
226+
structuredContent=structured,
227+
)
207228

208229
async def list_resources(self) -> list[mcp_types.Resource]:
209230
"""Fetch resources from server and cache.

hud/environment/tests/test_connection.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -281,13 +281,15 @@ async def test_call_tool_strips_prefix(self) -> None:
281281

282282
mock_result = mcp_types.CallToolResult(content=[], isError=False)
283283
mock_client = MagicMock()
284-
mock_client.call_tool_mcp = AsyncMock(return_value=mock_result)
284+
mock_client.call_tool = AsyncMock(return_value=mock_result)
285285
connector.client = mock_client
286286

287287
await connector.call_tool("myprefix_tool1", {"arg": "value"})
288288

289289
# Prefix should be stripped
290-
mock_client.call_tool_mcp.assert_called_once_with("tool1", {"arg": "value"})
290+
mock_client.call_tool.assert_called_once_with(
291+
name="tool1", arguments={"arg": "value"}, _meta=None
292+
)
291293

292294
@pytest.mark.asyncio
293295
async def test_call_tool_raises_when_not_connected(self) -> None:

0 commit comments

Comments
 (0)