FastMCP: Structured Inputs and Outputs
Relevant source files
The following files were used as context for generating this wiki page:The FastMCP framework provides robust support for structured inputs and outputs for tools. This allows developers to define tools that accept and return strongly-typed data structures, such as Pydantic BaseModels, dataclasses, and TypedDicts, instead of relying solely on primitive types or text. This feature enhances type safety, improves the discoverability of tool capabilities through automatically generated JSON schemas, and enables more reliable and predictable client-server integrations.
When a tool is defined with a structured return type, the server automatically generates an outputSchema for it. Upon successful execution, the tool call result will include a structuredContent field containing the JSON object, in addition to the standard content field which holds a text-based representation. Similarly, tools can accept complex JSON objects as inputs by using Pydantic models as type hints for their parameters.
Structured Outputs
A key feature of FastMCP is the ability for tools to return complex, typed data. The framework automatically handles the serialization and schema generation based on the Python type hints of the tool function.
Defining a Tool with Structured Output
To define a tool that returns a structured object, you can use a Pydantic BaseModel as the return type hint for the function. FastMCP will inspect this type hint to generate the outputSchema for the tool and to validate the output at runtime.
The following diagram illustrates the process from defining a tool to receiving a structured response.
Example: Weather Tool
This example defines a get_weather tool that returns a WeatherInfo Pydantic model.
# examples/fastmcp/weather_structured.py:11-20
class WeatherInfo(BaseModel):
"""Structured weather information."""
temperature: float
condition: str
humidity: int
wind_speed: float
@mcp.tool()
def get_weather(city: str) -> WeatherInfo:
"""Get the current weather for a city."""
# In a real scenario, this would call a weather API
return Weatherinfo(temperature=72.5, condition="sunny", humidity=45, wind_speed=5.2)
Sources: examples/fastmcp/weather_structured.py:11-20
When this tool is called, the client receives a response containing both a human-readable text content and a machine-readable structuredContent object.
Sources: tests/server/fastmcp/test_server.py:620-625, tests/server/fastmcp/test_integration.py:654-668
Supported Return Types
FastMCP supports a variety of types for structured output, each handled slightly differently.
| Return Type | structuredContent Format | Notes |
|---|---|---|
Pydantic BaseModel | The model serialized as a JSON object. | The outputSchema is directly derived from the model. |
dataclass | The dataclass serialized as a JSON object. | Similar to BaseModel. |
TypedDict | The dictionary as a JSON object. | The outputSchema is derived from the TypedDict definition. |
Primitive (e.g., int, str, bool) | {"result": <value>} | Primitive types are wrapped in a JSON object with a result key. |
list (e.g., list[int]) | {"result": [<values>]} | Lists are also wrapped in a JSON object with a result key. |
dict[str, T] | The dictionary as a JSON object. | The outputSchema reflects the value type T. |
dict[str, Any] | The dictionary as a JSON object. | The outputSchema is a generic object schema with no specific properties. |
Sources: tests/server/fastmcp/test_server.py:628-757, tests/server/fastmcp/test_tool_manager.py:403-524
Server-Side Validation and Error Handling
If a tool's return value does not conform to its declared return type hint, the server will raise a validation error. In this case, the tool call result will have isError set to True, and the structuredContent field will be None. The content field will contain a text description of the error.
Sources: tests/server/fastmcp/test_server.py:700-713
Structured Inputs
Tools can also be defined to accept complex, structured data as input arguments. This is achieved by using a Pydantic BaseModel as the type hint for a function parameter.
Defining a Tool with Structured Input
The process is similar to defining structured outputs. The @mcp.tool() decorator inspects the function's parameter type hints to generate the tool's inputSchema.
The diagram below shows the data flow for calling a tool with a structured input.
Example: User Processing Tool
This tool accepts a User object and a configuration flag.
# examples/fastmcp/complex_inputs.py:11-20
class User(BaseModel):
"""A model representing a user."""
name: str
age: int
@mcp.tool()
def process_user(user: User, send_confirmation: bool = True) -> str:
"""Processes a user and optionally sends a confirmation."""
message = f"Processed user: {user.name}, age {user.age}."
if send_confirmation:
message += " Confirmation sent."
return message
Sources: examples/fastmcp/complex_inputs.py:11-20
When calling this tool, the client must provide a JSON object for the user argument that conforms to the User model's schema.
Sources: tests/server/fastmcp/test_tool_manager.py:84-100
Internal Mechanics
The structured I/O functionality is primarily managed by the ToolManager and the FuncMetadata utility.
This diagram shows the relationship between ToolManager, Tool, and FuncMetadata.
- ToolManager: Manages the lifecycle of tools. When a tool is called via
call_toolwithconvert_result=True, it handles the conversion of the return value into the structured format. - Tool: Represents a single registered tool. It holds a reference to the underlying function and its
FuncMetadata. Theoutput_schemaproperty provides the JSON schema for the tool's return value. - FuncMetadata: A utility class that performs introspection on a function's signature (parameters and return type hints) to create Pydantic models and generate the corresponding JSON schemas for inputs and outputs.
Sources: tests/server/fastmcp/test_tool_manager.py, src/mcp/server/fastmcp/utilities/func_metadata.py
Summary
The structured input and output system in FastMCP is a powerful feature for building robust and self-documenting APIs. By leveraging Python's type hints and Pydantic's data validation capabilities, it allows for clear contracts between clients and servers, reduces the likelihood of runtime errors, and simplifies the handling of complex data. Developers can define tools that operate on rich data objects, and clients can discover and interact with these tools in a predictable, type-safe manner.