diff --git a/packages/opencode/src/provider/transform.ts b/packages/opencode/src/provider/transform.ts index 7ed89cb2599c..01de2dfc7ed0 100644 --- a/packages/opencode/src/provider/transform.ts +++ b/packages/opencode/src/provider/transform.ts @@ -792,6 +792,12 @@ export namespace ProviderTransform { } } + // Infer type "object" when properties/required exist but type is missing + // (common in MCP tool schemas like Notion MCP) + if (!result.type && (result.properties || result.required)) { + result.type = "object" + } + // Remove properties/required from non-object types (Gemini rejects these) if (result.type && result.type !== "object") { delete result.properties diff --git a/packages/opencode/test/provider/transform.test.ts b/packages/opencode/test/provider/transform.test.ts index 02bb5278fc7d..8de192ce6b91 100644 --- a/packages/opencode/test/provider/transform.test.ts +++ b/packages/opencode/test/provider/transform.test.ts @@ -398,6 +398,49 @@ describe("ProviderTransform.schema - gemini non-object properties removal", () = expect(result.properties.data.required).toEqual(["name"]) }) + test("infers type 'object' when properties exist but type is missing", () => { + const schema = { + type: "object", + properties: { + data: { + description: "The data", + properties: { page_id: { type: "string" } }, + required: ["page_id"], + }, + }, + } as any + + const result = ProviderTransform.schema(geminiModel, schema) as any + + expect(result.properties.data.type).toBe("object") + expect(result.properties.data.properties).toBeDefined() + expect(result.properties.data.required).toEqual(["page_id"]) + }) + + test("infers type 'object' for array items with properties but no type", () => { + const schema = { + type: "object", + properties: { + title: { + type: "array", + items: { + properties: { + annotations: { + type: "object", + properties: { bold: { type: "boolean" } }, + }, + }, + }, + }, + }, + } as any + + const result = ProviderTransform.schema(geminiModel, schema) as any + + expect(result.properties.title.items.type).toBe("object") + expect(result.properties.title.items.properties).toBeDefined() + }) + test("does not affect non-gemini providers", () => { const openaiModel = { providerID: "openai",