Skip to content

Commit 30f32e3

Browse files
GWealecopybara-github
authored andcommitted
fix: coerce non-string enum values to strings on string-typed Gemini schemas
Some MCP servers declare a string-typed field whose enum lists integer values. Gemini requires enum members to match the declared string type, so the tool declaration was rejected and the integration failed. Normalize enum values to their string form when the effective (non-null) type is string, leaving numeric enums on numeric types untouched. Close #3401 Co-authored-by: George Weale <gweale@google.com> PiperOrigin-RevId: 963579568
1 parent 8995005 commit 30f32e3

2 files changed

Lines changed: 59 additions & 0 deletions

File tree

src/google/adk/tools/_gemini_schema_util.py

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414

1515
from __future__ import annotations
1616

17+
import json
1718
import re
1819
from typing import Any
1920
from typing import Optional
@@ -100,6 +101,21 @@ def _sanitize_schema_type(
100101
if is_array:
101102
schema.setdefault("items", {"type": "string"})
102103

104+
effective_type = schema_type
105+
if isinstance(schema_type, list):
106+
non_null = [t for t in schema_type if t != "null"]
107+
effective_type = non_null[0] if non_null else None
108+
if effective_type == "string" and isinstance(schema.get("enum"), list):
109+
# Gemini rejects non-string enum values on a string-typed field; some
110+
# servers emit integer enums on string fields, so render them in their
111+
# JSON form. A null member is dropped: nullability is carried by the
112+
# schema type, not by an enum entry.
113+
schema["enum"] = [
114+
v if isinstance(v, str) else json.dumps(v)
115+
for v in schema["enum"]
116+
if v is not None
117+
]
118+
103119
return schema
104120

105121

tests/unittests/tools/test_gemini_schema_util.py

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -217,6 +217,49 @@ def test_to_gemini_schema_enum(self):
217217
gemini_schema = _to_gemini_schema(openapi_schema)
218218
assert gemini_schema.enum == ["a", "b", "c"]
219219

220+
def test_to_gemini_schema_stringifies_int_enum_on_string_type(self):
221+
openapi_schema = {"type": "string", "enum": [256, 512, 1024]}
222+
gemini_schema = _to_gemini_schema(openapi_schema)
223+
assert gemini_schema.type == Type.STRING
224+
assert gemini_schema.enum == ["256", "512", "1024"]
225+
226+
def test_to_gemini_schema_stringifies_nested_int_enum(self):
227+
openapi_schema = {
228+
"type": "object",
229+
"properties": {
230+
"p": {"type": "string", "enum": [256, 512]},
231+
},
232+
}
233+
gemini_schema = _to_gemini_schema(openapi_schema)
234+
assert gemini_schema.properties["p"].type == Type.STRING
235+
assert gemini_schema.properties["p"].enum == ["256", "512"]
236+
237+
def test_to_gemini_schema_int_enum_on_integer_type_unchanged(self):
238+
openapi_schema = {"type": "integer", "enum": [1, 2]}
239+
gemini_schema = _to_gemini_schema(openapi_schema)
240+
assert gemini_schema.type == Type.INTEGER
241+
assert gemini_schema.enum == [1, 2]
242+
243+
def test_to_gemini_schema_nullable_string_enum(self):
244+
openapi_schema = {"type": ["string", "null"], "enum": [256]}
245+
gemini_schema = _to_gemini_schema(openapi_schema)
246+
assert gemini_schema.type == Type.STRING
247+
assert gemini_schema.nullable
248+
assert gemini_schema.enum == ["256"]
249+
250+
def test_to_gemini_schema_drops_null_enum_member(self):
251+
openapi_schema = {"type": ["string", "null"], "enum": ["a", None]}
252+
gemini_schema = _to_gemini_schema(openapi_schema)
253+
assert gemini_schema.type == Type.STRING
254+
assert gemini_schema.nullable
255+
assert gemini_schema.enum == ["a"]
256+
257+
def test_to_gemini_schema_stringifies_bool_enum_as_json(self):
258+
openapi_schema = {"type": "string", "enum": [True, False]}
259+
gemini_schema = _to_gemini_schema(openapi_schema)
260+
assert gemini_schema.type == Type.STRING
261+
assert gemini_schema.enum == ["true", "false"]
262+
220263
def test_to_gemini_schema_required(self):
221264
openapi_schema = {
222265
"type": "object",

0 commit comments

Comments
 (0)