Skip to content

fix(ai): send Gemini tool schemas as parametersJsonSchema - #51009

Merged
rekram1-node merged 1 commit into
v2from
gemini-json-schema
Sep 24, 2026
Merged

rekram1-node merged 1 commit into
v2from
gemini-json-schema

Conversation

@rekram1-node

@rekram1-node rekram1-node commented Sep 24, 2026 •

Copy link
Copy Markdown
Collaborator

Problem

The Gemini protocol (Google AI Studio, Vertex Gemini, and Gemini on the Zen gateway) sent tool schemas in the legacy parameters field. That field only accepts Google's OpenAPI-subset schema, so GeminiToolSchema.convert projected every tool schema into it. The projection was lossy and still produced schemas Gemini rejects:

  • Rejected requests. Vertex, and Zen (which reaches Gemini through Vertex), require a type on every node, so any const, untyped enum, or untyped field fails with "didn't specify the schema type field". The API also rejects properties on non-object types. Tuples, prefixItems, and items: true fail everywhere. Vercel's MCP toolset (169 tools, sent in 5 chunks) failed in 4 of 5 chunks on Vertex and Zen, and 3 of 5 on the API.
  • Wrong arguments when accepted. $ref/$defs, additionalProperties, and oneOf detail are dropped and enum values are stringified, so Gemini fills arguments with the wrong types. In forced tool calls: integer enums came back as "3", referenced objects and oneOf unions came back as JSON strings, and additionalProperties maps came back empty (0/3 correct in each scenario).

Change

  • gemini.ts: tool declarations use parametersJsonSchema, which accepts standard JSON Schema.
  • New utils/gemini-json-schema.ts: rewrites only the shapes parametersJsonSchema still rejects, each verified live:
    • required entries with no declared property are removed.
    • Draft-04 boolean exclusiveMinimum/exclusiveMaximum become the numeric form (seen in a real Vercel tool).
    • Draft-07 tuple items: [...] (+ additionalItems) become prefixItems (+ items).
    • Gemini only accepts a recursive $ref loop that passes through an optional property or potentially empty array items. Other self-references become {}. The main real-world case is Zod's JSON-value schema, whose loop goes through additionalProperties. Wrapping the ref as nullable does not satisfy Gemini (tested).
    • Data under const, default, enum, examples, and dependentRequired is never rewritten.
  • GeminiToolSchema stays for the separate toolSchema: "gemini" compatibility mode on OpenAI-compatible routes, which this PR does not touch.

Verification

Live through the V2 client on Gemini API, Vertex, and Zen (gemini-3.8-flash). The corpus has 45 schemas, including a regression case for every past Gemini schema fix, plus Vercel's full MCP toolset:

Before After
Gemini API 43/50 accepted 47/50
Vertex 38/50 47/50
Zen 38/50 47/50
Forced tool calls validated against the original schema wrong types in 6/7 scenarios 12/12 correct on each target

The only remaining rejections are synthetic schemas nested 24+ levels deep. parametersJsonSchema allows about 15 nested properties levels, versus about 31 for parameters. No real schema tested comes close; Vercel's deepest is 13.

Known limitation, unchanged by this PR: Gemini cannot reliably fill a free-form "any JSON value" parameter with either field.

Tests: gemini.test.ts covers the normalizer rules and sending standard JSON Schema unchanged. The three cassettes whose request bodies contain tool declarations (google-vertex/calls-a-tool, google-vertex/continues-after-a-tool-result, pdf/gemini-tool-result) were re-recorded. bun test in packages/ai: 1505 pass. bun run check: 35/35.

The Gemini protocol projected tool schemas into Google's legacy OpenAPI
subset for the `parameters` field. The projection dropped $ref, $defs,
and additionalProperties, stringified enum values, and still produced
schemas Gemini rejects: Vertex (and the Zen gateway, which reaches Gemini
through Vertex) requires a type on every node, and the API rejects
properties on non-object types. Most of Vercel's MCP tools failed, and
accepted calls came back with wrong argument types ("3" for an integer
enum, JSON strings for referenced objects).

Send tool schemas in `parametersJsonSchema`, which accepts standard JSON
Schema, and rewrite only the shapes it still rejects: `required` entries
without a declared property, draft-04 boolean exclusive bounds, draft-07
tuple items, and recursive $ref loops that do not pass through an
optional property or potentially empty array items.
@rekram1-node
rekram1-node merged commit 88a9688 into v2 Sep 24, 2026
9 of 10 checks passed
@rekram1-node
rekram1-node deleted the gemini-json-schema branch September 24, 2026 01:28
@rekram1-node rekram1-node changed the title fix(ai): send Gemini tool schemas as parametersJsonSchema fix(ai): send Gemini tool schemas as JSON Schema on every route Sep 24, 2026
@rekram1-node rekram1-node changed the title fix(ai): send Gemini tool schemas as JSON Schema on every route fix(ai): send Gemini tool schemas as parametersJsonSchema Sep 24, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant