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
No fields configured for issues without a type.

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions