[BUG] MCP tool parameters using $ref enum types are serialized as null
Title: [BUG] MCP tool parameters using $ref enum types are serialized as null
---
Preflight Checklist
- [x] I have searched for existing issues
- [x] This is a single bug report (not multiple issues)
- [x] I am on the latest version of Claude Code
What's Wrong?
When an MCP tool parameter's JSON Schema uses a $ref pointer to an enum definition in $defs, Claude serializes the parameter value as null regardless of what the model intends to send. The model correctly identifies the value to send (visible in its reasoning), but the serialized tool call payload contains null.
Inline enum values (without $ref) work correctly.
What Should Happen?
Claude should resolve $ref pointers in $defs and serialize the intended enum value. Both schema shapes represent the same constraint and should produce the same behavior.
Steps to Reproduce
- Create an MCP server with two tools. One uses an inline enum, the other uses a
$refenum:
from fastmcp import FastMCP
from enum import Enum
from typing import Optional, Literal
from pydantic import Field
mcp = FastMCP("test-server")
class Priority(str, Enum):
HIGH = "high"
MEDIUM = "medium"
LOW = "low"
# Tool A: inline Literal enum (WORKS)
@mcp.tool()
def create_task_inline(
title: str = Field(description="Task title"),
priority: Optional[Literal["high", "medium", "low"]] = Field(
default=None, description="Task priority"
),
) -> str:
return f"Created task: title={title}, priority={priority}"
# Tool B: $ref enum via Pydantic class (BROKEN)
@mcp.tool()
def create_task_ref(
title: str = Field(description="Task title"),
priority: Optional[Priority] = Field(
default=None, description="Task priority"
),
) -> str:
return f"Created task: title={title}, priority={priority}"
- The two tools produce different JSON Schema shapes:
Tool A (inline, works):
{
"priority": {
"anyOf": [
{"enum": ["high", "medium", "low"], "type": "string"},
{"type": "null"}
],
"default": null
}
}
Tool B ($ref, broken):
{
"$defs": {
"Priority": {"enum": ["high", "medium", "low"], "type": "string"}
},
"priority": {
"anyOf": [
{"$ref": "#/$defs/Priority"},
{"type": "null"}
],
"default": null
}
}
- Ask Claude to call both tools with
priority="high".
- Tool A receives
priority="high". Tool B receivespriority=null.
Additional patterns tested
The bug also affects non-nullable $ref enums with defaults:
{
"$defs": {
"OutputFormat": {"enum": ["mp4", "webm"], "type": "string"}
},
"output_format": {
"$ref": "#/$defs/OutputFormat",
"default": "mp4"
}
}
When the model sets output_format="webm", the server receives null. Depending on server validation/normalization, this either fails validation or is treated as omitted and falls back to the default "mp4".
Test matrix
| Parameter type | Schema shape | Value sent | Value received |
|---|---|---|---|
| Optional[Literal["high", "medium", "low"]] | inline enum | "high" | "high" |
| Optional[Priority] (str Enum class) | $ref to $defs | "high" | null |
| str | {"type": "string"} | "test" | "test" |
| Optional[str] | anyOf[string, null] | "test" | "test" |
| bool | {"type": "boolean"} | true | true |
| int | {"type": "integer"} | 3 | 3 |
Only $ref enum parameters are affected. All primitive types and inline enums work.
Error Messages/Logs
No error is raised. The value is silently sent as null, making the bug difficult to detect. If the server has null-stripping logic or default fallbacks, the user gets wrong output with no indication of failure.
Is this a regression?
Unknown
Claude Code Version
1.0.33
Platform
Anthropic API
Operating System
Linux
Terminal/Shell
zsh
Additional Information
- Reproducible on both Claude Code (CLI) and Claude.ai (web) with connected MCP servers.
- The Pydantic
$refpattern is standard JSON Schema. MCP servers using Pydantic v2str, Enumtypes may hit this when their tool schema exposes those enums through$refdefinitions. - Python's
fastmcpframework (used by many MCP servers) produces$refschemas by default for enum types. - Related Cursor forum report with the same root cause: https://forum.cursor.com/t/mcp-server-cant-handle-enums-from-my-python-fastmcp-server/99092
- Related MCP Inspector issue: https://github.com/modelcontextprotocol/inspector/issues/672
- Server-side workaround: replace
$refenum types with inlineLiteral[...]in tool signatures at codegen time. This produces the working schema shape without changing runtime behavior.
This issue has 5 comments on GitHub. Read the full discussion on GitHub ↗