@@ -258,20 +258,23 @@ annotation is compatible. Otherwise, they will return unstructured results.
258
258
Structured output supports these return types:
259
259
- Pydantic models (BaseModel subclasses)
260
260
- 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.
267
269
268
270
Structured results are automatically validated against the output schema
269
271
generated 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.
271
273
272
274
** 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.
275
278
276
279
** Note:** In cases where a tool function's return type annotation
277
280
causes the tool to be classified as structured _ and this is undesirable_ ,
@@ -322,6 +325,37 @@ def get_statistics(data_type: str) -> dict[str, float]:
322
325
return {" mean" : 42.5 , " median" : 40.0 , " std_dev" : 5.2 }
323
326
324
327
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
+
325
359
# Lists and other types are wrapped automatically
326
360
@mcp.tool ()
327
361
def list_cities () -> list[str ]:
0 commit comments