From 1a4261ad4b66cdeb39d39110a086bd6112b17516 Mon Sep 17 00:00:00 2001 From: Google Team Member Date: Mon, 27 Oct 2025 12:07:11 -0700 Subject: [PATCH] fix: Fix issue with MCP tools throwing an error Fixes: https://github.com/google/adk-python/issues/3082 PiperOrigin-RevId: 824620839 --- src/google/adk/tools/_gemini_schema_util.py | 26 ++++++------ .../tools/test_gemini_schema_util.py | 42 ++++++++++++++++++- 2 files changed, 54 insertions(+), 14 deletions(-) diff --git a/src/google/adk/tools/_gemini_schema_util.py b/src/google/adk/tools/_gemini_schema_util.py index df76f3c8..3d7fbaec 100644 --- a/src/google/adk/tools/_gemini_schema_util.py +++ b/src/google/adk/tools/_gemini_schema_util.py @@ -75,20 +75,15 @@ def _to_snake_case(text: str) -> str: def _sanitize_schema_type(schema: dict[str, Any]) -> dict[str, Any]: - if ("type" not in schema or not schema["type"]) and schema.keys().isdisjoint( - schema - ): + if not schema: schema["type"] = "object" if isinstance(schema.get("type"), list): - nullable = False - non_null_type = None - for t in schema["type"]: - if t == "null": - nullable = True - elif not non_null_type: - non_null_type = t - if not non_null_type: - non_null_type = "object" + types_no_null = [t for t in schema["type"] if t != "null"] + nullable = len(types_no_null) != len(schema["type"]) + if "array" in types_no_null: + non_null_type = "array" + else: + non_null_type = types_no_null[0] if types_no_null else "object" if nullable: schema["type"] = [non_null_type, "null"] else: @@ -96,6 +91,13 @@ def _sanitize_schema_type(schema: dict[str, Any]) -> dict[str, Any]: elif schema.get("type") == "null": schema["type"] = ["object", "null"] + schema_type = schema.get("type") + is_array = schema_type == "array" or ( + isinstance(schema_type, list) and "array" in schema_type + ) + if is_array and "items" not in schema: + schema["items"] = {"type": "string"} + return schema diff --git a/tests/unittests/tools/test_gemini_schema_util.py b/tests/unittests/tools/test_gemini_schema_util.py index f4c594d8..9bc4d1a2 100644 --- a/tests/unittests/tools/test_gemini_schema_util.py +++ b/tests/unittests/tools/test_gemini_schema_util.py @@ -68,6 +68,11 @@ class TestToGeminiSchema: "object_nullable": {"type": "null"}, "multi_types_nullable": {"type": ["string", "null", "integer"]}, "empty_default_object": {}, + "empty_list_type": {"type": []}, + "multi_type_with_array_nullable": { + "type": ["string", "array", "null"] + }, + "multi_type_with_array_nonnullable": {"type": ["integer", "array"]}, }, } gemini_schema = _to_gemini_schema(openapi_schema) @@ -93,6 +98,23 @@ class TestToGeminiSchema: assert gemini_schema.properties["empty_default_object"].type == Type.OBJECT assert gemini_schema.properties["empty_default_object"].nullable is None + assert gemini_schema.properties["empty_list_type"].type == Type.OBJECT + assert not gemini_schema.properties["empty_list_type"].nullable + + assert ( + gemini_schema.properties["multi_type_with_array_nullable"].type + == Type.ARRAY + ) + assert gemini_schema.properties["multi_type_with_array_nullable"].nullable + + assert ( + gemini_schema.properties["multi_type_with_array_nonnullable"].type + == Type.ARRAY + ) + assert not gemini_schema.properties[ + "multi_type_with_array_nonnullable" + ].nullable + def test_to_gemini_schema_nested_objects(self): openapi_schema = { "type": "object", @@ -137,6 +159,20 @@ class TestToGeminiSchema: gemini_schema = _to_gemini_schema(openapi_schema) assert gemini_schema.items.properties["name"].type == Type.STRING + def test_to_gemini_schema_array_without_items_gets_default(self): + openapi_schema = {"type": "array"} + gemini_schema = _to_gemini_schema(openapi_schema) + assert gemini_schema.type == Type.ARRAY + assert not gemini_schema.nullable + assert gemini_schema.items.type == Type.STRING + + def test_to_gemini_schema_nullable_array_without_items_gets_default(self): + openapi_schema = {"type": ["array", "null"]} + gemini_schema = _to_gemini_schema(openapi_schema) + assert gemini_schema.type == Type.ARRAY + assert gemini_schema.nullable + assert gemini_schema.items.type == Type.STRING + def test_to_gemini_schema_any_of(self): openapi_schema = { "anyOf": [{"type": "string"}, {"type": "integer"}], @@ -533,8 +569,10 @@ class TestToGeminiSchema: "type": "string", }, "next_page_token": { - "anyOf": [{"type": "string"}, {"type": "null"}], - "default": None, + "any_of": [ + {"type": "string"}, + {"type": ["object", "null"]}, + ], "description": ( "The nextPageToken to fetch the next page of results." ),