From 70f94685e6bec059ca2e0f62037f0dacd8b5fef9 Mon Sep 17 00:00:00 2001 From: Sahil Singh Date: Tue, 18 Aug 2026 19:22:56 +0530 Subject: [PATCH] fix(gemini): inject missing items schema for array typed mcp tools (#10578) (#10605) --- open-sse/translator/helpers/geminiHelper.ts | 28 ++++++++++++ tests/unit/gemini-array-items.test.ts | 47 +++++++++++++++++++++ 2 files changed, 75 insertions(+) create mode 100644 tests/unit/gemini-array-items.test.ts diff --git a/open-sse/translator/helpers/geminiHelper.ts b/open-sse/translator/helpers/geminiHelper.ts index 623e39cba3..8a67b07bc5 100644 --- a/open-sse/translator/helpers/geminiHelper.ts +++ b/open-sse/translator/helpers/geminiHelper.ts @@ -727,5 +727,33 @@ export function cleanJSONSchemaForAntigravity(schema: unknown): unknown { injectObjectType(cleaned); + // Phase 8: Ensure array types have an items schema (#10578). + // Gemini strictly requires array parameters to define their `items` schema. + // If an MCP tool defines an array but forgets the items, inject a safe default. + function ensureArrayItems(obj: unknown): void { + if (!obj || typeof obj !== "object") return; + + if (Array.isArray(obj)) { + for (const item of obj) { + ensureArrayItems(item); + } + return; + } + + const record = obj as JsonRecord; + if (record.type === "array" && !record.items) { + record.items = { type: "string" }; + } + + // Recurse into remaining values. + for (const value of Object.values(record)) { + if (value && typeof value === "object") { + ensureArrayItems(value); + } + } + } + + ensureArrayItems(cleaned); + return cleaned; } diff --git a/tests/unit/gemini-array-items.test.ts b/tests/unit/gemini-array-items.test.ts new file mode 100644 index 0000000000..52db074c6f --- /dev/null +++ b/tests/unit/gemini-array-items.test.ts @@ -0,0 +1,47 @@ +import { describe, it } from "node:test"; +import assert from "node:assert/strict"; +import { cleanJSONSchemaForAntigravity } from "../../open-sse/translator/helpers/geminiHelper"; + +type SchemaNode = { + type?: string; + properties?: Record; + items?: SchemaNode; + required?: string[]; + [key: string]: unknown; +}; + +describe("Gemini array items sanitizer (#10578)", () => { + it("should inject a string items schema for array types that are missing it", () => { + const sloppySchema = { + type: "object", + properties: { + emails: { + type: "array", + }, + }, + required: ["emails"], + }; + + const cleanedSchema = cleanJSONSchemaForAntigravity(sloppySchema as unknown) as SchemaNode; + + assert.equal(cleanedSchema.properties?.emails?.type, "array"); + assert.ok(cleanedSchema.properties?.emails?.items, "items should be injected"); + assert.equal(cleanedSchema.properties?.emails?.items?.type, "string"); + }); + + it("should safely ignore arrays that already have valid items", () => { + const goodSchema = { + type: "object", + properties: { + tags: { + type: "array", + items: { type: "number" }, + }, + }, + }; + + const cleanedSchema = cleanJSONSchemaForAntigravity(goodSchema as unknown) as SchemaNode; + + assert.equal(cleanedSchema.properties?.tags?.items?.type, "number"); + }); +});