diff --git a/.changeset/gemini-mcp-schema-required.md b/.changeset/gemini-mcp-schema-required.md new file mode 100644 index 00000000000..b735f56ff6c --- /dev/null +++ b/.changeset/gemini-mcp-schema-required.md @@ -0,0 +1,5 @@ +--- +"@kilocode/cli": patch +--- + +Prevent Gemini requests from failing when MCP tool schemas contain `required` fields without matching object properties. diff --git a/packages/opencode/src/provider/transform.ts b/packages/opencode/src/provider/transform.ts index 18b94c5adf7..a3dc90a16e8 100644 --- a/packages/opencode/src/provider/transform.ts +++ b/packages/opencode/src/provider/transform.ts @@ -1487,9 +1487,15 @@ export function schema(model: Provider.Model, schema: JSONSchema7): JSONSchema7 } // Filter required array to only include fields that exist in properties - if (result.type === "object" && result.properties && Array.isArray(result.required)) { - result.required = result.required.filter((field: any) => field in result.properties) + // kilocode_change start - Gemini rejects required entries without matching properties + if (result.type === "object" && Array.isArray(result.required)) { + const properties = isPlainObject(result.properties) ? result.properties : undefined + result.required = properties ? result.required.filter((field: any) => field in properties) : [] + if (result.required.length === 0) { + delete result.required + } } + // kilocode_change end if (result.type === "array" && !hasCombiner(result)) { if (result.items == null) { diff --git a/packages/opencode/test/provider/transform.test.ts b/packages/opencode/test/provider/transform.test.ts index 709d8dac3a3..92e0bf11a79 100644 --- a/packages/opencode/test/provider/transform.test.ts +++ b/packages/opencode/test/provider/transform.test.ts @@ -934,6 +934,53 @@ describe("ProviderTransform.schema - gemini non-object properties removal", () = expect(result.properties.data.required).toEqual(["name"]) }) + // kilocode_change start + test("removes required from object array items with no properties", () => { + const schema = { + type: "object", + properties: { + issue_fields: { + type: "array", + items: { + type: "object", + required: ["type"], + }, + }, + }, + } as any + + const result = ProviderTransform.schema(geminiModel, schema) as any + + expect(result.properties.issue_fields.items.type).toBe("object") + expect(result.properties.issue_fields.items.required).toBeUndefined() + }) + + test("sanitizes gemini schemas routed through the Kilo Gateway", () => { + const gatewayGeminiModel = { + providerID: "kilocode", + api: { + id: "google/gemini-2.5-pro", + }, + } as any + const schema = { + type: "object", + properties: { + issue_fields: { + type: "array", + items: { + type: "object", + required: ["type"], + }, + }, + }, + } as any + + const result = ProviderTransform.schema(gatewayGeminiModel, schema) as any + + expect(result.properties.issue_fields.items.required).toBeUndefined() + }) + // kilocode_change end + test("does not affect non-gemini providers", () => { const openaiModel = { providerID: "openai",