[BUG] MCP FastMCP Pydantic model parameters serialized as JSON strings causing validation errors
Environment
- Platform: macOS (Darwin 24.5.0)
- Claude CLI version: Claude Code
- Operating System: macOS
- Terminal: Claude Code integrated terminal
Bug Description
When using FastMCP servers with Pydantic model parameters, Claude Code consistently fails with validation errors because it serializes Pydantic model objects as JSON strings instead of passing them as proper dictionary objects to the MCP server.
Error message:
Input validation error: '{"evaluation_id": "test-eval", "evaluation_name": "Test", "course_name": "Course", "professor_name": "Prof"}' is not of type 'object'
Related to existing issue #2747 which reports the same systematic problem with qdrant-store metadata parameters.
Steps to Reproduce
- Create a FastMCP server with a Pydantic model parameter:
from fastmcp import FastMCP, Context
from pydantic import BaseModel
mcp = FastMCP("test-server", version="1.0.0")
class TestModel(BaseModel):
name: str
age: int
@mcp.tool
def test_pydantic_model(data: TestModel) -> str:
"""Test tool with Pydantic model parameter."""
return f"Hello {data.name}, age {data.age}"
- Configure the server in Claude Code MCP settings
- Attempt to call the tool:
mcp__test-server__test_pydantic_model({
"data": {"name": "John", "age": 30}
})
- Observe the validation error
Expected Behavior
FastMCP should receive the Pydantic model parameter as a proper Python dictionary object that can be validated and instantiated by Pydantic.
According to FastMCP documentation and the MCP specification, complex object parameters should be passed as dictionary objects, not JSON strings.
Actual Behavior
Claude Code's MCP client serializes the dictionary parameter as a JSON string before sending it to the MCP server, causing Pydantic validation to fail because:
- Expected:
dict_type(Python dictionary) - Received:
input_type=str(JSON string) - Value:
'{"name": "John", "age": 30}'(serialized string instead of native dict)
Root Cause Analysis
This appears to be a systematic parameter serialization issue in Claude Code's MCP integration layer, specifically affecting dictionary/object type parameters. The issue occurs because:
- Claude Code receives the parameter as a proper object/dictionary
- Claude Code's MCP client serializes it to a JSON string during transmission
- FastMCP receives a string instead of the expected dictionary
- Pydantic validation fails because it expects an object, not a string
Impact
- Breaks FastMCP servers that use Pydantic models as parameters
- Limits MCP functionality to only simple primitive types (strings, integers, booleans)
- Forces workarounds like using individual parameters instead of structured objects
- Creates inconsistency between documented MCP capabilities and actual functionality
Workaround
Convert Pydantic model parameters to individual parameters:
# Instead of this (doesn't work):
@mcp.tool
def setup_evaluation(config: EvaluationConfig) -> str:
pass
# Use this (works):
@mcp.tool
def setup_evaluation(
evaluation_id: str,
evaluation_name: str,
course_name: str,
professor_name: str
) -> str:
# Create Pydantic model internally
config = EvaluationConfig(
evaluation_id=evaluation_id,
evaluation_name=evaluation_name,
course_name=course_name,
professor_name=professor_name
)
pass
Additional Context
Related Issues:
- Issue #2747: Same problem with qdrant-store metadata parameters
- Forum discussion: https://forum.cursor.com/t/issue-with-mcp-server-and-pydantic-model-object-as-tool-parameter-in-cursor/77110
Why this went unnoticed:
- Most MCP servers use simple parameter types
- FastMCP's Pydantic model feature is relatively new
- Documentation examples often show simple use cases
Technical Details:
- This affects all MCP clients that use Claude Code's MCP integration
- The issue is in the MCP client layer, not in FastMCP itself
- FastMCP works correctly with other MCP clients (confirmed by community)
This is a critical issue that prevents the use of modern structured parameter approaches in MCP servers, forcing developers to use less maintainable individual parameter patterns.
This issue has 15 comments on GitHub. Read the full discussion on GitHub ↗