Skip to content

Gemini: sanitizeGemini doesn't handle anyOf/const patterns in MCP tool schemas #12908

@nxxxsooo

Description

@nxxxsooo

Bug Description

When using Google Gemini models (both via @ai-sdk/google API key and antigravity OAuth), MCP tools with anyOf/const patterns in their JSON Schema cause Gemini API to reject the request with errors like:

GenerateContentRequest.tools[0].function_declarations[32].parameters.properties[format].any_of[0].enum: only allowed for STRING type

Root Cause

ProviderTransform.schema() in packages/opencode/src/provider/transform.ts has a sanitizeGemini function (line 772) that converts integer enums to string enums, but does not flatten anyOf/const patterns that Gemini API rejects.

MCP servers (notably Obsidian Local REST API) generate tool schemas like:

{
  "format": {
    "anyOf": [
      { "const": "json" },
      { "const": "markdown" }
    ]
  }
}

Gemini expects this to be:

{
  "format": {
    "type": "string",
    "enum": ["json", "markdown"]
  }
}

The same issue affects fields with anyOf + enum patterns:

{
  "operation": {
    "anyOf": [
      { "const": "append", "description": "..." },
      { "const": "prepend", "description": "..." },
      { "const": "replace", "description": "..." }
    ]
  }
}

Affected Tools

All Obsidian REST API MCP tools with enum-like fields:

  • GetActiveFile / GetVaultFileformat field
  • PatchActiveFile / PatchVaultFilecontentType, operation, targetType fields
  • SearchVaultqueryType field
  • ExecuteTemplatecreateFile field

Suggested Fix

In sanitizeGemini, add handling for anyOf/oneOf arrays of const values:

// Flatten anyOf/oneOf with const values into enum
for (const keyword of ['anyOf', 'any_of', 'oneOf', 'one_of']) {
  if (Array.isArray(result[keyword])) {
    const constants = result[keyword]
      .filter((item: any) => item && typeof item === 'object' && 'const' in item)
    if (constants.length === result[keyword].length && constants.length > 0) {
      result.type = 'string'
      result.enum = constants.map((item: any) => String(item.const))
      delete result[keyword]
    }
  }
}

Environment

  • OpenCode version: 1.1.53
  • Provider: @ai-sdk/google (both API key and antigravity plugin)
  • MCP server: Obsidian Local REST API
  • OS: macOS (Apple Silicon)

Workaround

Currently the only workaround is to disable the problematic MCP server's tools using permissions, but this affects all providers — not just Gemini.

Metadata

Metadata

Assignees

Labels

No labels
No labels

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions