Problem
_single_pass_optimize() strips user-defined title property from tool inputSchema.properties when another property is named type. The title key remains in required, creating an unsatisfiable schema.
MRE
from fastmcp import FastMCP
import asyncio
mcp = FastMCP("test")
@mcp.tool()
def broken(dashboard_id: str, title: str, type: str = "vis") -> str:
return ""
async def main():
tools = await mcp.list_tools()
schema = tools[0].to_mcp_tool().inputSchema
print("title" in schema["properties"]) # False
print("title" in schema["required"]) # True
asyncio.run(main())
Output: False then True. Expected: both True.
Cause
prune_titles heuristic in _single_pass_optimize pops title from any dict that also contains a JSON Schema keyword key (type, required, etc.) — but in the properties dict these are parameter names, not schema keywords.
FastMCP 3.1.1, Python 3.13.
Problem
_single_pass_optimize()strips user-definedtitleproperty from toolinputSchema.propertieswhen another property is namedtype. Thetitlekey remains inrequired, creating an unsatisfiable schema.MRE
Output:
FalsethenTrue. Expected: bothTrue.Cause
prune_titlesheuristic in_single_pass_optimizepopstitlefrom any dict that also contains a JSON Schema keyword key (type,required, etc.) — but in thepropertiesdict these are parameter names, not schema keywords.FastMCP 3.1.1, Python 3.13.