This document explains how FastMCP's OpenAPI integration works, what features are supported, and how to extend it. The OpenAPI functionality is split across two main files:
server/openapi.py- High-level FastMCP server implementation and MCP component creationutilities/openapi.py- Low-level OpenAPI parsing and intermediate representation
OpenAPI Spec → Parse → HTTPRoute IR → Create MCP Components → FastMCP Server
OpenAPI specifications are parsed into an intermediate representation (IR) that normalizes differences between OpenAPI 3.0 and 3.1:
- Input: Raw OpenAPI spec (dict)
- Output: List of
HTTPRouteobjects with normalized parameter information - Key Classes:
HTTPRoute- Represents a single operationParameterInfo- Represents a parameter with location, style, explode, etc.RequestBodyInfo- Represents request body informationResponseInfo- Represents response information
HTTPRoute objects are converted into FastMCP components based on route mapping rules:
- Tools (
OpenAPITool) - HTTP operations that can be called - Resources (
OpenAPIResource) - HTTP endpoints that return data - Resource Templates (
OpenAPIResourceTemplate) - Parameterized resources
FastMCP supports various OpenAPI parameter serialization styles and formats:
query- Query string parameterspath- Path parametersheader- HTTP headerscookie- Cookie parameters (parsed but not used in requests)
form(default) - Standard query parameter formatexplode=true(default):?tags=red&tags=blueexplode=false:?tags=red,blue
deepObject- Object parameters with bracket notationexplode=true:?filter[name]=John&filter[age]=30explode=false: Falls back to JSON string (non-standard, logs warning)
simple(default) - Comma-separated for arrays:/users/1,2,3
simple(default) - Standard header format
- String arrays with
explode=true/false - Number arrays with
explode=true/false - Boolean arrays with
explode=true/false - Complex object arrays (basic support, may not handle all cases)
- Objects with
deepObjectstyle andexplode=true - Objects with other styles fall back to JSON serialization
- Strings, numbers, booleans
- Enums
- Default values
application/json- JSON request bodies
- Object schemas with properties
- Array schemas
- Primitive schemas
- Schema references (
$refto local schemas only) - Required properties
- Default values
application/json- Parsed as JSONtext/*- Returned as textapplication/xml- Returned as text- Other types - Returned as binary
- Success response schemas (200, 201, 202, 204)
- Object response wrapping for MCP compliance
- Schema compression (removes unused
$defs)
Routes are mapped to MCP component types using RouteMap configurations:
RouteMap(
methods=["GET", "POST"], # HTTP methods to match
pattern=r"/api/users/.*", # Regex pattern for path
mcp_type=MCPType.RESOURCE_TEMPLATE, # Target component type
tags={"user"}, # OpenAPI tags to match (AND condition)
mcp_tags={"fastmcp-user"} # Tags to add to created components
)- All routes become Tools by default
- Use route maps to override specific patterns
MCPType.TOOL- Callable operationsMCPType.RESOURCE- Static data endpointsMCPType.RESOURCE_TEMPLATE- Parameterized data endpointsMCPType.EXCLUDE- Skip route entirely
- Parameter Name Collisions - When path/query parameters have same names as request body properties, non-body parameters get
__locationsuffixes - Complex Array Serialization - Limited support for arrays containing objects
- Cookie Parameters - Parsed but not used in requests
- Non-standard Combinations - e.g.,
deepObjectwithexplode=false
- Content Type Priority - Only first available content type is used
- Nested Objects - Deep nesting may not serialize correctly
- Binary Content - No support for file uploads or binary data
- Multiple Content Types - Only JSON-compatible types are used for output schemas
- Error Responses - Not used for MCP output schema generation
- Response Headers - Not captured or exposed
- External References -
$refto external files not supported - Circular References - May cause issues in schema processing
- Polymorphism -
oneOf/anyOf/allOflimited support
- "Unknown tool/resource" - Check route mapping configuration
- Parameter not found - Check for name collisions or incorrect style/explode
- Invalid request format - Check parameter serialization and content types
- Schema validation errors - Check for external refs or complex schemas
# Parse routes to inspect intermediate representation
routes = parse_openapi_to_http_routes(openapi_spec)
for route in routes:
print(f"{route.method} {route.path}")
for param in route.parameters:
print(f" {param.name} ({param.location}): style={param.style}, explode={param.explode}")
# Check component creation
server = FastMCP.from_openapi(openapi_spec, client)
tools = await server.get_tools()
print(f"Created {len(tools)} tools: {list(tools.keys())}")- Set
FASTMCP_LOG_LEVEL=DEBUGto see detailed parameter processing - Look for warnings about non-standard parameter combinations
- Check for schema parsing errors in logs
- Add style handling in
utilities/openapi.py-ParameterInfoclass - Implement serialization logic in
server/openapi.py-OpenAPITool.run() - Add tests for parsing and serialization
- Extend request body handling in
OpenAPITool.run() - Add response parsing logic for new types
- Update content type priority in utilities
Use route_map_fn for complex routing logic:
def custom_mapper(route: HTTPRoute, current_type: MCPType) -> MCPType:
if route.path.startswith("/admin"):
return MCPType.EXCLUDE
return current_type
server = FastMCP.from_openapi(spec, client, route_map_fn=custom_mapper)- Test parameter parsing with various styles/explode combinations
- Test route mapping with different patterns and tags
- Test schema generation and compression
- Mock HTTP client to verify actual request parameters
- Test end-to-end component creation and execution
- Test error handling and edge cases
async def test_parameter_style():
# 1. Create OpenAPI spec with specific parameter configuration
spec = {"openapi": "3.1.0", ...}
# 2. Parse and create components
routes = parse_openapi_to_http_routes(spec)
tool = OpenAPITool(mock_client, routes[0], ...)
# 3. Execute and verify request parameters
await tool.run({"param": "value"})
actual_params = mock_client.request.call_args.kwargs["params"]
assert actual_params == expected_paramsOpenAPI functionality is tested across multiple files in tests/server/openapi/:
test_basic_functionality.py- Core component creation and executiontest_explode_integration.py- Parameter explode behaviortest_deepobject_style.py- DeepObject style parameter encodingtest_parameter_collisions.py- Parameter name collision handlingtest_openapi_path_parameters.py- Path parameter serializationtest_configuration.py- Route mapping and MCP namestest_description_propagation.py- Schema and description handling
When adding new OpenAPI features, create focused test files rather than adding to existing monolithic files.
This document should be updated when new OpenAPI features are added or when edge cases are discovered and addressed.