Repository navigation
Description
Problem
When using MCPToolset to load remote MCP tools, tools with Optional[Union[...]] type hints fail with Gemini API validation errors:
google.genai.errors.ClientError: 400 Bad Request
Unable to submit request because `create_event` functionDeclaration `parameters.reminders`
schema didn't specify the schema type field.
The mcp server in use is workspace-mcp - link to function create_event
async def create_event(
service,
user_google_email: str,
summary: str,
start_time: str,
end_time: str,
calendar_id: str = "primary",
description: Optional[str] = None,
location: Optional[str] = None,
attendees: Optional[List[str]] = None,
timezone: Optional[str] = None,
attachments: Optional[List[str]] = None,
add_google_meet: bool = False,
reminders: Optional[Union[str, List[Dict[str, Any]]]] = None,
use_default_reminders: bool = True,
transparency: Optional[str] = None,
) -> str:... which emits the following json schema to clients
json schema
{
"name": "create_event",
"description": "Creates a new event.\n\nArgs:\n user_google_email (str): The user's Google email address. Required.\n summary (str): Event title.\n start_time (str): Start time (RFC3339, e.g., \"2023-10-27T10:00:00-07:00\" or \"2023-10-27\" for all-day).\n end_time (str): End time (RFC3339, e.g., \"2023-10-27T11:00:00-07:00\" or \"2023-10-28\" for all-day).\n calendar_id (str): Calendar ID (default: 'primary').\n description (Optional[str]): Event description.\n location (Optional[str]): Event location.\n attendees (Optional[List[str]]): Attendee email addresses.\n timezone (Optional[str]): Timezone (e.g., \"America/New_York\").\n attachments (Optional[List[str]]): List of Google Drive file URLs or IDs to attach to the event.\n add_google_meet (bool): Whether to add a Google Meet video conference to the event. Defaults to False.\n reminders (Optional[Union[str, List[Dict[str, Any]]]]): JSON string or list of reminder objects. Each should have 'method' (\"popup\" or \"email\") and 'minutes' (0-40320). Max 5 reminders. Example: '[{\"method\": \"popup\", \"minutes\": 15}]' or [{\"method\": \"popup\", \"minutes\": 15}]\n use_default_reminders (bool): Whether to use calendar's default reminders. If False, uses custom reminders. Defaults to True.\n transparency (Optional[str]): Event transparency for busy/free status. \"opaque\" shows as Busy (default), \"transparent\" shows as Available/Free. Defaults to None (uses Google Calendar default).\n\nReturns:\n str: Confirmation message of the successful event creation with event link.",
"inputSchema": {
"type": "object",
"properties": {
"user_google_email": {
"type": "string"
},
"summary": {
"type": "string"
},
"start_time": {
"type": "string"
},
"end_time": {
"type": "string"
},
"calendar_id": {
"default": "primary",
"type": "string"
},
"description": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"default": null
},
"location": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"default": null
},
"attendees": {
"anyOf": [
{
"items": {
"type": "string"
},
"type": "array"
},
{
"type": "null"
}
],
"default": null
},
"timezone": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"default": null
},
"attachments": {
"anyOf": [
{
"items": {
"type": "string"
},
"type": "array"
},
{
"type": "null"
}
],
"default": null
},
"add_google_meet": {
"default": false,
"type": "boolean"
},
"reminders": {
"anyOf": [
{
"type": "string"
},
{
"items": {
"additionalProperties": true,
"type": "object"
},
"type": "array"
},
{
"type": "null"
}
],
"default": null
},
"use_default_reminders": {
"default": true,
"type": "boolean"
},
"transparency": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"default": null
}
},
"required": [
"user_google_email",
"summary",
"start_time",
"end_time"
]
},
"outputSchema": {
"type": "object",
"properties": {
"result": {
"type": "string"
}
},
"required": [
"result"
],
"x-fastmcp-wrap-result": true
},
"_meta": {
"_fastmcp": {
"tags": []
}
}
}
Root Cause
The MCP server (FastMCP) generates JSON schemas with anyOf constructs that lack a top-level type field :
"reminders": {
"anyOf": [
{"type": "string"},
{"type": "array", "items": {"type": "object"}},
{"type": "null"}
],
"default": null
}This is valid JSON Schema but fails Gemini's stricter validation requirements.
Expected Behavior
ADK should sanitize MCP tool schemas to add fallback type fields to anyOf constructs before passing them to Gemini, similar to how it handles schemas from direct Python function declarations in src/google/adk/tools/_automatic_function_calling_util.py , with a TODO comment acknowledging: "Unclear why a Type is needed with 'anyOf' to avoid google.genai.errors.ClientError: 400 INVALID_ARGUMENT".
def _map_pydantic_type_to_property_schema(property_schema: Dict):
if 'type' in property_schema:
property_schema['type'] = _py_type_2_schema_type.get(
property_schema['type'], 'TYPE_UNSPECIFIED'
)
if property_schema['type'] == 'ARRAY':
_map_pydantic_type_to_property_schema(property_schema['items'])
for type_ in property_schema.get('anyOf', []):
if 'type' in type_:
type_['type'] = _py_type_2_schema_type.get(
type_['type'], 'TYPE_UNSPECIFIED'
)
# TODO: To investigate. Unclear why a Type is needed with 'anyOf' to
# avoid google.genai.errors.ClientError: 400 INVALID_ARGUMENT.
property_schema['type'] = type_['type']Proposed Fix
Apply the same anyOf handling logic to _sanitize_schema_formats_for_gemini() in src/google/adk/tools/_gemini_schema_util.py:139-186. The function should recursively add fallback type fields to any anyOf construct that lacks one, using the first non-null type as the fallback.
Reproduction
- Create a FastMCP server with a tool using
Optional[Union[str, List[Dict[str, Any]]]]type hint - Connect to it via
MCPToolsetwithStreamableHTTPConnectionParams - Attempt to use the tool - Gemini will reject the schema
Impact
This affects any MCP tool with union types, making it impossible to use many real-world MCP servers with ADK without modifying the remote server's source code
Additional info
If this helps I find that when downgrading to adk to v1.17.0 and pinning google-genai==1.44.0 , the issue did not occur. Please advise if this is better to post under the python-genai repo instead.
May be related with this issue : googleapis/python-genai#625
Activity
luutuankiet commentedon Dec 3, 2025
hi team @seanzhou1023 , understand from @boyangsvl that adk don't support Union types tool definitions
#55 (comment)
The recommended approach is to simplify the function definition. However my scenario is a bit nuanced as I am not the maintainer of the workspace-mcp tool thus cannot alter the source code of the mcp server.
Could you advise if adk allows manually intercepting the mcp function to gemini api converter and overriding the function declaration ? My hunch is I'd need to start from _gemini_schema_utils.py where the conversion happens. Psuedo code below is my starting point.
Many thanks in advance.
agent.py
luutuankiet commentedon Dec 3, 2025
After much thought, closing this issue as this is a google-genai library issue and not google-adk.
generate_content_async(...)which sends the requests to google-genai.Optional[List[str]]): Get simplified to proper schema with top-level typeOptional[Union[str, List[Dict]]]): Incorrectly gettype: "OBJECT"which fails the validationGoogle: Handle anyOf types for Gemini 3+ models
Google: Use parameters_json_schema for function declarations
Google: Use parameters_json_schema for function declarations
Google: Use JSON Schema fields instead of Google Schema conversion
Google: Use JSON Schema fields instead of Google Schema conversion
fix(gemini): emit only typed element schemas and translate type lists…
fix(gemini): derive the items schema Gemini requires from a tuple-sty…