Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/gemini-mcp-schema-required.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@kilocode/cli": patch
---

Prevent Gemini requests from failing when MCP tool schemas contain `required` fields without matching object properties.
10 changes: 8 additions & 2 deletions packages/opencode/src/provider/transform.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down
47 changes: 47 additions & 0 deletions packages/opencode/test/provider/transform.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

please move the new tests to a new file in a kilocode folder

test("does not affect non-gemini providers", () => {
const openaiModel = {
providerID: "openai",
Expand Down