From 671eaf0f03edca01b0fb5afb4eb914391eb292ca Mon Sep 17 00:00:00 2001 From: Bill Easton Date: Sun, 12 Apr 2026 11:51:49 -0500 Subject: [PATCH] fix: strip title fields from tool schemas for Gemini 2.5 Flash compatibility (#3861) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * fix: strip title fields from tool schemas for Gemini compatibility Gemini 2.5 Flash produces MALFORMED_FUNCTION_CALL when a function declaration's parameters_json_schema contains 'title' fields (which Pydantic adds by default). Strip them in _convert_tool_to_google_genai before passing to the API. Fixes #3860 Co-Authored-By: Claude Opus 4.6 (1M context) * Fix ty errors and lowest-deps test failure - Add assertions for non-None before subscripting FunctionDeclaration fields (ty check) - Test compress_schema directly instead of constructing FunctionDeclaration which may not support parameters_json_schema in google-genai==1.18.0 🤖 Generated with Claude Code Co-Authored-By: Claude Opus 4.6 (1M context) --------- Co-authored-by: Claude Opus 4.6 (1M context) --- .../client/sampling/handlers/google_genai.py | 10 +++++-- .../handlers/test_google_genai_handler.py | 28 +++++++++++++++++++ 2 files changed, 35 insertions(+), 3 deletions(-) diff --git a/src/fastmcp/client/sampling/handlers/google_genai.py b/src/fastmcp/client/sampling/handlers/google_genai.py index ad1a3d1e8..de46cb1a4 100644 --- a/src/fastmcp/client/sampling/handlers/google_genai.py +++ b/src/fastmcp/client/sampling/handlers/google_genai.py @@ -144,15 +144,19 @@ class GoogleGenaiSamplingHandler: def _convert_tool_to_google_genai(tool: MCPTool) -> GoogleTool: """Convert an MCP Tool to Google GenAI format. - Google's parameters_json_schema accepts standard JSON Schema format, - so we pass tool.inputSchema directly without conversion. + We prune ``title`` fields from the schema because Gemini 2.5 Flash + produces ``MALFORMED_FUNCTION_CALL`` when Pydantic's auto-generated + title annotations are present. """ + from fastmcp.utilities.json_schema import compress_schema + + schema = compress_schema(tool.inputSchema, prune_titles=True) return GoogleTool( function_declarations=[ FunctionDeclaration( name=tool.name, description=tool.description or "", - parameters_json_schema=tool.inputSchema, + parameters_json_schema=schema, ) ] ) diff --git a/tests/client/sampling/handlers/test_google_genai_handler.py b/tests/client/sampling/handlers/test_google_genai_handler.py index 7eb0da188..263f01fc5 100644 --- a/tests/client/sampling/handlers/test_google_genai_handler.py +++ b/tests/client/sampling/handlers/test_google_genai_handler.py @@ -432,3 +432,31 @@ def test_response_to_result_with_tools_mixed_content(): assert isinstance(tool_use, ToolUseContent) assert tool_use.type == "tool_use" assert tool_use.name == "search" + + +# ──────────────────────────────────────────────────────────── +# Test for title stripping (PR #3860) +# ──────────────────────────────────────────────────────────── + + +def test_convert_tool_strips_titles(): + """_convert_tool_to_google_genai should strip titles from inputSchema. + + We test via compress_schema directly rather than constructing a + FunctionDeclaration because older google-genai versions may not + support the parameters_json_schema field. + """ + from fastmcp.utilities.json_schema import compress_schema + + input_schema = { + "title": "Params", + "type": "object", + "properties": { + "query": {"title": "Query", "type": "string"}, + }, + } + + schema = compress_schema(input_schema, prune_titles=True) + assert "title" not in schema + assert "title" not in schema["properties"]["query"] + assert schema["properties"]["query"]["type"] == "string"