@@ -258,20 +258,23 @@ annotation is compatible. Otherwise, they will return unstructured results.
258258Structured output supports these return types:
259259- Pydantic models (BaseModel subclasses)
260260- TypedDicts
261- - Dataclasses
262- - NamedTuples
263- - ` dict[str, T] `
264- - Primitive types (str, int, float, bool) - wrapped in ` {"result": value} `
265- - Generic types (list, tuple, set) - wrapped in ` {"result": value} `
266- - Union types and Optional - wrapped in ` {"result": value} `
261+ - Dataclasses and other classes with type hints
262+ - ` dict[str, T] ` (where T is any JSON-serializable type)
263+ - Primitive types (str, int, float, bool, bytes, None) - wrapped in ` {"result": value} `
264+ - Generic types (list, tuple, Union, Optional, etc.) - wrapped in ` {"result": value} `
265+
266+ Classes without type hints cannot be serialized for structured output. Only
267+ classes with properly annotated attributes will be converted to Pydantic models
268+ for schema generation and validation.
267269
268270Structured results are automatically validated against the output schema
269271generated from the annotation. This ensures the tool returns well-typed,
270- validated data that clients can easily process:
272+ validated data that clients can easily process.
271273
272274** Note:** For backward compatibility, unstructured results are also
273- returned. Unstructured results are provided strictly for compatibility
274- with previous versions of FastMCP.
275+ returned. Unstructured results are provided for backward compatibility
276+ with previous versions of the MCP specification, and are quirks-compatible
277+ with previous versions of FastMCP in the current version of the SDK.
275278
276279** Note:** In cases where a tool function's return type annotation
277280causes the tool to be classified as structured _ and this is undesirable_ ,
@@ -322,6 +325,37 @@ def get_statistics(data_type: str) -> dict[str, float]:
322325 return {" mean" : 42.5 , " median" : 40.0 , " std_dev" : 5.2 }
323326
324327
328+ # Ordinary classes with type hints work for structured output
329+ class UserProfile :
330+ name: str
331+ age: int
332+ email: str | None = None
333+
334+ def __init__ (self , name : str , age : int , email : str | None = None ):
335+ self .name = name
336+ self .age = age
337+ self .email = email
338+
339+
340+ @mcp.tool ()
341+ def get_user (user_id : str ) -> UserProfile:
342+ """ Get user profile - returns structured data"""
343+ return UserProfile(
name = " Alice" ,
age = 30 ,
email = " [email protected] " )
344+
345+
346+ # Classes WITHOUT type hints cannot be used for structured output
347+ class UntypedConfig :
348+ def __init__ (self , setting1 , setting2 ):
349+ self .setting1 = setting1
350+ self .setting2 = setting2
351+
352+
353+ @mcp.tool ()
354+ def get_config () -> UntypedConfig:
355+ """ This returns unstructured output - no schema generated"""
356+ return UntypedConfig(" value1" , " value2" )
357+
358+
325359# Lists and other types are wrapped automatically
326360@mcp.tool ()
327361def list_cities () -> list[str ]:
0 commit comments