anomalyco / anomalyco/opencode

[bug] Gemini rejects MCP tool with nullable array schema (type: "array","null"): languages.items predicate failed + any_of0.items` missing field

Open
#48,073 3 comments 1 reaction 1 assignee View on GitHub

@nexxeln is already working on this.

Since Sep 9, 2026.

Dominant language
TypeScript
Stars
209k
Forks
27.5k
Avg merge
7h 2m
Merged PRs (30d)
384

Description

Description

Description
Any request to a Gemini model fails with 400 INVALID_ARGUMENT when the @sylphx/pdf-reader-mcp MCP server is enabled — even a plain hello with no PDF involved. Gemini validates all function declarations up front, so one incompatible tool schema breaks every request. The same setup works fine on DeepSeek / OpenAI-compatible providers.
Error

  • GenerateContentRequest.tools[0].function_declarations[9].parameters.properties[languages].items: field predicate failed: $type == Type.ARRAY
  • GenerateContentRequest.tools[0].function_declarations[9].parameters.properties[languages].any_of[0].items: missing field.
    Root cause
    The MCP server's raw tools/list schema (queried directly, v4.1.3 = latest) declares:
    "languages": { "items": { "type": "string" }, "type": ["array", "null"] }
    This is valid JSON Schema, but after opencode/AI-SDK converts it to Gemini's function-calling schema, the multi-type array is split into anyOf, producing two invalid halves:
  1. top-level items left behind while type is no longer ARRAY → predicate failed: $type == Type.ARRAY
  2. a bare { "type": "array" } branch inside anyOf with no items → any_of[0].items: missing field (Gemini requires items on every array)
    The tool has ~15 similar union-typed fields (["string","null"], ["integer","null"], plus a nested sources.regions: ["array","null"]), so patching a single field is not enough — the converter/sanitizer needs to handle the pattern.
    Repro
  3. mcp.pdf-reader: npx @sylphx/pdf-reader-mcp (v4.1.3), enabled: true
  4. opencode run "hello" -m google/gemini-3.8-flash (also repros on gemini-2.5-flash-lite, gemini-3.5-flash-lite) → 400 above
  5. Set the server enabled: false, or a per-agent "pdf-reader_*": "deny" → hello-ok ✅ (verified A/B; deny proves the tools are excluded from declarations and the request then passes)
    Expected / proposed fix
    Extend sanitizeGemini in transform.ts (cf. #11889) to normalize multi-entry type arrays on the Gemini path, e.g. collapse type: ["array","null"] → type: "array", nullable: true keeping items on the array branch (same class of fix as LibreChat #13623 sanitizeGeminiSchema, and related to #3140 / #14509). Gemini-side nullable is the documented way to express optionality — no anyOf needed.
    Environment
  • opencode 1.18.29
  • @sylphx/pdf-reader-mcp@4.1.3 (npm latest, verified via tools/list over stdio)
  • models: google/gemini-3.8-flash (primary target), google/gemini-3.5-flash-lite
    Related
  • #11889 (sanitize non-object properties/required)
  • #14509 (anyOf sibling fields break all Gemini requests)
  • sst/opencode#3140
Plugins

pdf-reader

OpenCode version

v1.18.27

Steps to reproduce

No response

Screenshot and/or share link

No response

Operating System

Ubuntu 22.04

Terminal

Ptyxis

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.