Back to Fastmcp

Calling Tools

docs/clients/tools.mdx

3.2.45.3 KB
Original Source

import { VersionBadge } from '/snippets/version-badge.mdx'

<VersionBadge version="2.0.0" />

Use this when you need to execute server-side functions and process their results.

Tools are executable functions exposed by MCP servers. The client's call_tool() method executes a tool by name with arguments and returns structured results.

Basic Execution

python
async with client:
    result = await client.call_tool("add", {"a": 5, "b": 3})
    # result -> CallToolResult with structured and unstructured data

    # Access structured data (automatically deserialized)
    print(result.data)  # 8

    # Access traditional content blocks
    print(result.content[0].text)  # "8"

Arguments are passed as a dictionary. For multi-server clients, tool names are automatically prefixed with the server name (e.g., weather_get_forecast for a tool named get_forecast on the weather server).

Execution Options

The call_tool() method supports timeout control and progress monitoring:

python
async with client:
    # With timeout (aborts if execution takes longer than 2 seconds)
    result = await client.call_tool(
        "long_running_task",
        {"param": "value"},
        timeout=2.0
    )

    # With progress handler
    result = await client.call_tool(
        "long_running_task",
        {"param": "value"},
        progress_handler=my_progress_handler
    )

Structured Results

<VersionBadge version="2.10.0" />

Tool execution returns a CallToolResult object. The .data property provides fully hydrated Python objects including complex types like datetimes and UUIDs, reconstructed from the server's output schema.

python
from datetime import datetime
from uuid import UUID

async with client:
    result = await client.call_tool("get_weather", {"city": "London"})

    # FastMCP reconstructs complete Python objects
    weather = result.data
    print(f"Temperature: {weather.temperature}C at {weather.timestamp}")

    # Complex types are properly deserialized
    assert isinstance(weather.timestamp, datetime)
    assert isinstance(weather.station_id, UUID)

    # Raw structured JSON is also available
    print(f"Raw JSON: {result.structured_content}")
<Card icon="code" title="CallToolResult Properties"> <ResponseField name=".data" type="Any"> Fully hydrated Python objects with complex type support (datetimes, UUIDs, custom classes). FastMCP exclusive. </ResponseField> <ResponseField name=".content" type="list[mcp.types.ContentBlock]"> Standard MCP content blocks (`TextContent`, `ImageContent`, `AudioContent`, etc.). </ResponseField> <ResponseField name=".structured_content" type="dict[str, Any] | None"> Standard MCP structured JSON data as sent by the server. </ResponseField> <ResponseField name=".is_error" type="bool"> Boolean indicating if the tool execution failed. </ResponseField> </Card>

For tools without output schemas or when deserialization fails, .data will be None. Fall back to content blocks in that case:

python
async with client:
    result = await client.call_tool("legacy_tool", {"param": "value"})

    if result.data is not None:
        print(f"Structured: {result.data}")
    else:
        for content in result.content:
            if hasattr(content, 'text'):
                print(f"Text result: {content.text}")
<Tip> FastMCP servers automatically wrap primitive results (like `int`, `str`, `bool`) in a `{"result": value}` structure. FastMCP clients automatically unwrap this, so you get the original value in `.data`. </Tip>

Error Handling

By default, call_tool() raises a ToolError if the tool execution fails:

python
from fastmcp.exceptions import ToolError

async with client:
    try:
        result = await client.call_tool("potentially_failing_tool", {"param": "value"})
        print("Tool succeeded:", result.data)
    except ToolError as e:
        print(f"Tool failed: {e}")

To handle errors manually instead of catching exceptions, disable automatic error raising:

python
async with client:
    result = await client.call_tool(
        "potentially_failing_tool",
        {"param": "value"},
        raise_on_error=False
    )

    if result.is_error:
        print(f"Tool failed: {result.content[0].text}")
    else:
        print(f"Tool succeeded: {result.data}")

Sending Metadata

<VersionBadge version="2.13.1" />

The meta parameter sends ancillary information alongside tool calls for observability, debugging, or client identification:

python
async with client:
    result = await client.call_tool(
        name="send_email",
        arguments={
            "to": "[email protected]",
            "subject": "Hello",
            "body": "Welcome!"
        },
        meta={
            "trace_id": "abc-123",
            "request_source": "mobile_app"
        }
    )

See Client Metadata to learn how servers access this data.

Raw Protocol Access

For complete control, use call_tool_mcp() which returns the raw MCP protocol object:

python
async with client:
    result = await client.call_tool_mcp("my_tool", {"param": "value"})
    # result -> mcp.types.CallToolResult

    if result.isError:
        print(f"Tool failed: {result.content}")
    else:
        print(f"Tool succeeded: {result.content}")
        # Note: No automatic deserialization with call_tool_mcp()