Initial Checks
Description
The signature for FastMCP.call_tool:
asyncdefcall_tool(self, name: str, arguments: dict[str, Any]) ->Sequence[ContentBlock] |dict[str, Any]:
i.e. it says the return type is either a sequence of ContentBlock or a dict.
However, it seems to be more like either just a sequence of ContentBlock or a tuple of: sequence of ContentBlock and a dict:
Sequence[ContentBlock] |tuple[Sequence[ContentBlock], dict[str, Any]]
That is because it always passes convert_result=True to self._tool_manager.call_tool (it would be nice if that was exposed as a parameter). And the convert_result result function (in mcp.server.fastmcp.utilities.func_metadata) is implemented like that but doesn't have any useful type hints.
This has changed after version 1.9.4 (i.e. in 1.9.4 it was still just a sequence of ContentBlock).
Example Code
frommcp.server.fastmcpimportFastMCPmcp=FastMCP(name='Example MCP Server')
@mcp.tool()asyncdefadd_numbers(a: int, b: int) ->int:
"""Add two numbers."""returna+b
...
# Within an async context call:result=awaitmcp.call_tool(
"add_numbers",
arguments={"a": 1, "b": 2}
)
print('result:', result)
# Output:result: ([TextContent(type='text', text='3', annotations=None, meta=None)], {'result': 3})Python & MCP Python SDK
Initial Checks
Description
The signature for FastMCP.call_tool:
i.e. it says the return type is either a sequence of ContentBlock or a dict.
However, it seems to be more like either just a sequence of ContentBlock or a tuple of: sequence of ContentBlock and a dict:
That is because it always passes
convert_result=Truetoself._tool_manager.call_tool(it would be nice if that was exposed as a parameter). And theconvert_resultresult function (inmcp.server.fastmcp.utilities.func_metadata) is implemented like that but doesn't have any useful type hints.This has changed after version
1.9.4(i.e. in1.9.4it was still just a sequence of ContentBlock).Example Code
Python & MCP Python SDK