Description
Gemini 2.5 Flash consistently returns MALFORMED_FUNCTION_CALL when a function declaration's parameters_json_schema contains "title" fields — which Pydantic adds by default to every field and object in .json_schema() output.
This affects all ctx.sample(result_type=...) calls when using the GoogleGenaiSamplingHandler with Gemini 2.5 Flash, because FastMCP creates a final_response tool from the Pydantic schema. The sample_impl function calls compress_schema(schema, prune_titles=True) for validation purposes, but the actual tool schema sent to Gemini goes through _convert_tool_to_google_genai which passes tool.inputSchema directly — with titles intact.
Root Cause
Isolated via systematic testing:
# This schema WORKS (no titles):
{"properties": {"items": {"items": {"properties": {"x": {"type": "string"}}, ...}}}}
# This schema FAILS (with titles):
{"properties": {"items": {"title": "Items", "items": {"title": "Item", "properties": {"x": {"title": "X", "type": "string"}}, ...}}}}
$ref/$defs alone: works fine
description fields: works fine
title on any property or object: 100% failure on Gemini 2.5 Flash
- Gemini 2.5 Pro: unaffected (handles titles correctly)
- Gemini JSON mode (
response_json_schema): unaffected (handles titles correctly)
Reproduction
from google import genai
from google.genai import types
from pydantic import BaseModel, TypeAdapter
class Item(BaseModel):
index: int
label: str
class Result(BaseModel):
items: list[Item]
# This fails on gemini-2.5-flash:
schema = TypeAdapter(Result).json_schema() # has "title" fields
tool = types.Tool(function_declarations=[
types.FunctionDeclaration(name="respond", description=".", parameters_json_schema=schema)
])
resp = genai.Client().models.generate_content(
model="gemini-2.5-flash",
contents="Classify: apple, banana",
config=types.GenerateContentConfig(
tools=[tool],
tool_config=types.ToolConfig(
function_calling_config=types.FunctionCallingConfig(mode="ANY")
),
),
)
print(resp.candidates[0].finish_reason) # MALFORMED_FUNCTION_CALL
# Stripping titles fixes it:
def strip_titles(obj):
if isinstance(obj, dict):
return {k: strip_titles(v) for k, v in obj.items() if k != "title"}
if isinstance(obj, list):
return [strip_titles(i) for i in obj]
return obj
schema_clean = strip_titles(schema) # same schema without "title" keys
# Using schema_clean → FinishReason.STOP (works)
Suggested Fix
In _convert_tool_to_google_genai (google_genai.py line ~150), strip title fields from the schema before passing to Gemini:
def _convert_tool_to_google_genai(tool: MCPTool) -> GoogleTool:
schema = tool.inputSchema
# Strip 'title' fields — Gemini 2.5 Flash produces MALFORMED_FUNCTION_CALL
# when schemas contain Pydantic's auto-generated title annotations
schema = _strip_titles(schema)
return GoogleTool(
function_declarations=[
FunctionDeclaration(
name=tool.name,
description=tool.description or "",
parameters_json_schema=schema,
)
]
)
Note: compress_schema(schema, prune_titles=True) already exists in run.py and does this — but it's only applied for validation, not for the schema sent to Gemini.
Affected Versions
- fastmcp 3.2.3
- google-genai 1.72.0
- Gemini 2.5 Flash (affected), Gemini 2.5 Pro (not affected)
Description
Gemini 2.5 Flash consistently returns
MALFORMED_FUNCTION_CALLwhen a function declaration'sparameters_json_schemacontains"title"fields — which Pydantic adds by default to every field and object in.json_schema()output.This affects all
ctx.sample(result_type=...)calls when using theGoogleGenaiSamplingHandlerwith Gemini 2.5 Flash, because FastMCP creates afinal_responsetool from the Pydantic schema. Thesample_implfunction callscompress_schema(schema, prune_titles=True)for validation purposes, but the actual tool schema sent to Gemini goes through_convert_tool_to_google_genaiwhich passestool.inputSchemadirectly — with titles intact.Root Cause
Isolated via systematic testing:
$ref/$defsalone: works finedescriptionfields: works finetitleon any property or object: 100% failure on Gemini 2.5 Flashresponse_json_schema): unaffected (handles titles correctly)Reproduction
Suggested Fix
In
_convert_tool_to_google_genai(google_genai.py line ~150), strip title fields from the schema before passing to Gemini:Note:
compress_schema(schema, prune_titles=True)already exists inrun.pyand does this — but it's only applied for validation, not for the schema sent to Gemini.Affected Versions