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
MCPToolset with StreamableHTTPConnectionParams
- 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
Problem
When using
MCPToolsetto load remote MCP tools, tools withOptional[Union[...]]type hints fail with Gemini API validation errors:The mcp server in use is workspace-mcp - link to function
create_event... 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
anyOfconstructs that lack a top-leveltypefield :This is valid JSON Schema but fails Gemini's stricter validation requirements.
Expected Behavior
ADK should sanitize MCP tool schemas to add fallback
typefields toanyOfconstructs before passing them to Gemini, similar to how it handles schemas from direct Python function declarations insrc/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".Proposed Fix
Apply the same
anyOfhandling logic to_sanitize_schema_formats_for_gemini()insrc/google/adk/tools/_gemini_schema_util.py:139-186. The function should recursively add fallbacktypefields to anyanyOfconstruct that lacks one, using the first non-null type as the fallback.Reproduction
Optional[Union[str, List[Dict[str, Any]]]]type hintMCPToolsetwithStreamableHTTPConnectionParamsImpact
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