mirror of
https://github.com/PrefectHQ/fastmcp.git
synced 2026-08-26 23:44:17 +02:00
Update docs
This commit is contained in:
parent
5520102821
commit
c412a63d4e
7 changed files with 575 additions and 46 deletions
|
|
@ -37,10 +37,13 @@ Execute a tool using `call_tool()` with the tool name and arguments:
|
|||
async with client:
|
||||
# Simple tool call
|
||||
result = await client.call_tool("add", {"a": 5, "b": 3})
|
||||
# result -> list[mcp.types.TextContent | mcp.types.ImageContent | ...]
|
||||
# result -> CallToolResult with structured and unstructured data
|
||||
|
||||
# Access the result content
|
||||
print(result[0].text) # Assuming TextContent, e.g., '8'
|
||||
# Access structured data (automatically deserialized)
|
||||
print(result.data) # 8 (int) or {"result": 8} for primitive types
|
||||
|
||||
# Access traditional content blocks
|
||||
print(result.content[0].text) # "8" (TextContent)
|
||||
```
|
||||
|
||||
### Advanced Execution Options
|
||||
|
|
@ -72,21 +75,97 @@ async with client:
|
|||
|
||||
## Handling Results
|
||||
|
||||
Tool execution returns a list of content objects. The most common types are:
|
||||
<VersionBadge version="2.10.0" />
|
||||
|
||||
- **`TextContent`**: Text-based results with a `.text` attribute
|
||||
- **`ImageContent`**: Image data with image-specific attributes
|
||||
- **`BlobContent`**: Binary data content
|
||||
Tool execution returns a `CallToolResult` object with both structured and traditional content. FastMCP's standout feature is the `.data` property, which doesn't just provide raw JSON but actually hydrates complete Python objects including complex types like datetimes, UUIDs, and custom classes.
|
||||
|
||||
### CallToolResult Properties
|
||||
|
||||
<Card icon="code" title="CallToolResult Properties">
|
||||
<ResponseField name=".data" type="Any">
|
||||
**FastMCP exclusive**: Fully hydrated Python objects with complex type support (datetimes, UUIDs, custom classes). Goes beyond JSON to provide complete object reconstruction from output schemas.
|
||||
</ResponseField>
|
||||
|
||||
<ResponseField name=".content" type="list[mcp.types.ContentBlock]">
|
||||
Standard MCP content blocks (`TextContent`, `ImageContent`, `AudioContent`, etc.) available from all MCP servers.
|
||||
</ResponseField>
|
||||
|
||||
<ResponseField name=".structured_content" type="dict[str, Any] | None">
|
||||
Standard MCP structured JSON data as sent by the server, available from all MCP servers that support structured outputs.
|
||||
</ResponseField>
|
||||
|
||||
<ResponseField name=".is_error" type="bool">
|
||||
Boolean indicating if the tool execution failed.
|
||||
</ResponseField>
|
||||
</Card>
|
||||
|
||||
### Structured Data Access
|
||||
|
||||
FastMCP's `.data` property provides fully hydrated Python objects, not just JSON dictionaries. This includes complex type reconstruction:
|
||||
|
||||
```python
|
||||
from datetime import datetime
|
||||
from uuid import UUID
|
||||
|
||||
async with client:
|
||||
result = await client.call_tool("get_weather", {"city": "London"})
|
||||
|
||||
for content in result:
|
||||
if hasattr(content, 'text'):
|
||||
print(f"Text result: {content.text}")
|
||||
elif hasattr(content, 'data'):
|
||||
print(f"Binary data: {len(content.data)} bytes")
|
||||
# FastMCP reconstructs complete Python objects from the server's output schema
|
||||
weather = result.data # Server-defined WeatherReport object
|
||||
print(f"Temperature: {weather.temperature}°C at {weather.timestamp}")
|
||||
print(f"Station: {weather.station_id}")
|
||||
print(f"Humidity: {weather.humidity}%")
|
||||
|
||||
# The timestamp is a real datetime object, not a string!
|
||||
assert isinstance(weather.timestamp, datetime)
|
||||
assert isinstance(weather.station_id, UUID)
|
||||
|
||||
# Compare with raw structured JSON (standard MCP)
|
||||
print(f"Raw JSON: {result.structured_content}")
|
||||
# {"temperature": 20, "timestamp": "2024-01-15T14:30:00Z", "station_id": "123e4567-..."}
|
||||
|
||||
# Traditional content blocks (standard MCP)
|
||||
print(f"Text content: {result.content[0].text}")
|
||||
```
|
||||
|
||||
### Fallback Behavior
|
||||
|
||||
For tools without output schemas or when deserialization fails, `.data` will be `None`:
|
||||
|
||||
```python
|
||||
async with client:
|
||||
result = await client.call_tool("legacy_tool", {"param": "value"})
|
||||
|
||||
if result.data is not None:
|
||||
# Structured output available and successfully deserialized
|
||||
print(f"Structured: {result.data}")
|
||||
else:
|
||||
# No structured output or deserialization failed - use content blocks
|
||||
for content in result.content:
|
||||
if hasattr(content, 'text'):
|
||||
print(f"Text result: {content.text}")
|
||||
elif hasattr(content, 'data'):
|
||||
print(f"Binary data: {len(content.data)} bytes")
|
||||
```
|
||||
|
||||
### Primitive Type Unwrapping
|
||||
|
||||
<Tip>
|
||||
FastMCP servers automatically wrap non-object results (like `int`, `str`, `bool`) in a `{"result": value}` structure to create valid structured outputs. FastMCP clients understand this convention and automatically unwrap the value in `.data` for convenience, so you get the original primitive value instead of a wrapper object.
|
||||
</Tip>
|
||||
|
||||
```python
|
||||
async with client:
|
||||
result = await client.call_tool("calculate_sum", {"a": 5, "b": 3})
|
||||
|
||||
# FastMCP client automatically unwraps for convenience
|
||||
print(result.data) # 8 (int) - the original value
|
||||
|
||||
# Raw structured content shows the server-side wrapping
|
||||
print(result.structured_content) # {"result": 8}
|
||||
|
||||
# Other MCP clients would need to manually access ["result"]
|
||||
# value = result.structured_content["result"] # Not needed with FastMCP!
|
||||
```
|
||||
|
||||
## Error Handling
|
||||
|
|
@ -101,14 +180,32 @@ from fastmcp.exceptions import ToolError
|
|||
async with client:
|
||||
try:
|
||||
result = await client.call_tool("potentially_failing_tool", {"param": "value"})
|
||||
print("Tool succeeded:", result)
|
||||
print("Tool succeeded:", result.data)
|
||||
except ToolError as e:
|
||||
print(f"Tool failed: {e}")
|
||||
```
|
||||
|
||||
### Manual Error Checking
|
||||
|
||||
For more granular control, use `call_tool_mcp()` which returns the raw MCP protocol object with an `isError` flag:
|
||||
You can disable automatic error raising and manually check the result:
|
||||
|
||||
```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}")
|
||||
```
|
||||
|
||||
### Raw MCP Protocol Access
|
||||
|
||||
For complete control, use `call_tool_mcp()` which returns the raw MCP protocol object:
|
||||
|
||||
```python
|
||||
async with client:
|
||||
|
|
@ -119,6 +216,7 @@ async with client:
|
|||
print(f"Tool failed: {result.content}")
|
||||
else:
|
||||
print(f"Tool succeeded: {result.content}")
|
||||
# Note: No automatic deserialization with call_tool_mcp()
|
||||
```
|
||||
|
||||
## Argument Handling
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue