60e0ffc959
Upgrade checks / Notify on failure (push) Has been cancelled
Upgrade checks / Close issue on success (push) Has been cancelled
Schema Crash Test / Real-world schema crash test (232K schemas) (push) Has been cancelled
Run static analysis / static_analysis (push) Has been cancelled
Tests / Tests: Python 3.10 on ubuntu-latest (push) Has been cancelled
Tests / Tests: Python 3.13 on ubuntu-latest (push) Has been cancelled
Tests / Tests: Python 3.10 on windows-latest (push) Has been cancelled
Tests / Tests with lowest-direct dependencies (push) Has been cancelled
Tests / MCP conformance tests (push) Has been cancelled
Tests / Integration tests (push) Has been cancelled
Tests / Package install smoke (push) Has been cancelled
Upgrade checks / Static analysis (push) Has been cancelled
Upgrade checks / Tests: Python 3.10 on ubuntu-latest (push) Has been cancelled
Upgrade checks / Tests: Python 3.13 on ubuntu-latest (push) Has been cancelled
Upgrade checks / Tests: Python 3.10 on windows-latest (push) Has been cancelled
Upgrade checks / Integration tests (push) Has been cancelled
Update MCPServerConfig Schema / update-config-schema (push) Has been cancelled
Update SDK Documentation / update-sdk-docs (push) Has been cancelled
184 lines
5.4 KiB
Plaintext
184 lines
5.4 KiB
Plaintext
---
|
|
title: Calling Tools
|
|
sidebarTitle: Tools
|
|
description: Execute server-side tools and handle structured results.
|
|
icon: wrench
|
|
---
|
|
|
|
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[fastmcp.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": "user@example.com",
|
|
"subject": "Hello",
|
|
"body": "Welcome!"
|
|
},
|
|
meta={
|
|
"trace_id": "abc-123",
|
|
"request_source": "mobile_app"
|
|
}
|
|
)
|
|
```
|
|
|
|
See [Client Metadata](/servers/context#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 -> fastmcp.types.CallToolResult
|
|
|
|
if result.is_error:
|
|
print(f"Tool failed: {result.content}")
|
|
else:
|
|
print(f"Tool succeeded: {result.content}")
|
|
# Note: No automatic deserialization with call_tool_mcp()
|
|
```
|