Initial Checks
Description
The signature for FastMCP.call_tool:
async def call_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
from mcp.server.fastmcp import FastMCP
mcp = FastMCP(name='Example MCP Server')
@mcp.tool()
async def add_numbers(a: int, b: int) -> int:
"""Add two numbers."""
return a + b
...
# Within an async context call:
result = await mcp.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