Description
RecursionError with Self-Referencing Pydantic Models in fastapi-mcp 0.3.3
Bug Description
When using fastapi-mcp 0.3.3
with Pydantic 2.7.2
, applications crash with a RecursionError
when Pydantic models contain self-referencing fields. The error occurs during OpenAPI schema generation in the resolve_schema_references
function.
Environment
- fastapi-mcp: 0.3.3
- Pydantic: 2.7.2
- FastAPI: 0.109.1
- Python: 3.10
Error Details
Error Location: /site-packages/fastapi_mcp/openapi/utils.py:50
in resolve_schema_references
Error Message:
RecursionError: maximum recursion depth exceeded while calling a Python object
Stack Trace Pattern:
File "/site-packages/fastapi_mcp/openapi/utils.py", line 50, in resolve_schema_references
schema_part[key] = resolve_schema_references(value, reference_schema)
File "/site-packages/fastapi_mcp/openapi/utils.py", line 53, in resolve_schema_references
schema_part[key] = [
File "/site-packages/fastapi_mcp/openapi/utils.py", line 54, in <listcomp>
resolve_schema_references(item, reference_schema) if isinstance(item, dict) else item for item in value
# ... (repeats infinitely)
Root Cause
The resolve_schema_references
function in fastapi-mcp cannot handle self-referencing Pydantic models properly, causing infinite recursion when processing OpenAPI schemas that contain circular references.
Minimal Reproduction Example
# models.py
from __future__ import annotations
from typing import Optional, List
from pydantic import BaseModel
from enum import Enum
class MessageType(str, Enum):
ROOT = "root"
TEXT = "text"
class ChatMessage(BaseModel):
"""Self-referencing model that triggers the bug"""
id: str
type: MessageType
content: Optional[List[ChatMessage]] = None # Self-reference causes recursion
# main.py
from fastapi import FastAPI
from fastapi_mcp import FastApiMCP
from models import ChatMessage
app = FastAPI()
@app.post("/test", response_model=ChatMessage)
async def test_endpoint():
return ChatMessage(id="test", type=MessageType.ROOT)
# This line causes RecursionError
mcp = FastApiMCP(app, include_tags=["mcp"])
mcp.mount()
if __name__ == "__main__":
import uvicorn
uvicorn.run(app, host="0.0.0.0", port=8000)
Steps to Reproduce
-
Install dependencies:
pip install fastapi-mcp==0.3.3 pydantic==2.7.2 fastapi==0.109.1
-
Create the files above (
models.py
andmain.py
) -
Run the application:
python main.py
-
Expected: Application starts successfully
-
Actual: Application crashes with
RecursionError
Workaround
Currently, the only workaround is to disable fastapi-mcp functionality:
# Comment out these lines to avoid the crash
# mcp = FastApiMCP(app, include_tags=["mcp"])
# mcp.mount()
Analysis
The issue appears to be in the resolve_schema_references
function which doesn't implement cycle detection for self-referencing schemas. When processing a schema that references itself (like List[ChatMessage]
within ChatMessage
), the function recursively calls itself without tracking visited schemas, leading to infinite recursion.
Suggested Fix
The resolve_schema_references
function should implement cycle detection, similar to how Pydantic itself handles self-referencing models. Possible approaches:
- Visited Set: Track visited schema references to detect cycles
- Reference Substitution: Replace circular references with
$ref
pointers - Depth Limiting: Implement maximum recursion depth
Impact
This bug prevents the use of fastapi-mcp with any application that uses self-referencing Pydantic models, which is a common pattern for:
- Tree structures
- Chat/message systems
- Hierarchical data models
- Graph-like data structures
Additional Context
This issue appears to be related to compatibility between fastapi-mcp's OpenAPI schema processing and Pydantic 2.7.x's schema generation for self-referencing models. The problem doesn't occur when fastapi-mcp is disabled, indicating the issue is specific to the fastapi-mcp schema processing pipeline.
Expected Behavior
fastapi-mcp should handle self-referencing Pydantic models gracefully, either by:
- Properly resolving circular references in OpenAPI schemas
- Gracefully skipping problematic models with appropriate warnings
- Using Pydantic's built-in mechanisms for handling self-references