github / github/copilot-cli

Gemini models fail with 400 for any MCP tool whose array `items` has a union type (e.g. ["object","null"]); GPT/Claude unaffected

未关闭
#4,623 1 条评论 0 个 reaction 已指派 0 人 在 GitHub 查看

还没有人认领这个 Issue。

area:mcp area:models
主要语言
Shell
星标
11.2k
派生
1.9k
平均合并
14 小时 16 分钟
30 天内合并 PR
6

描述

gemini-mcp-repro.js

Describe the bug

Summary

Any MCP server that publishes a tool whose array items schema uses a union type containing
"object"
— e.g. {"type": ["object", "null"]} — makes every Gemini model fail with
400 Bad Request. The same schema works fine on GPT and Claude.

A single non-conformant tool, anywhere in the loaded MCP set, poisons the entire session. The error
names no tool, no server, and no field, so it is effectively undiagnosable without bisection.

Minimal reproduction

No third-party servers needed. Save as gemini-mcp-repro.js:

// node gemini-mcp-repro.js bad   (or: good)
const bad = (process.argv[2] || 'bad') === 'bad';

const inputSchema = {
  type: 'object',
  properties: {
    rows: {
      type: 'array',
      description: 'a list of row objects',
      // ONLY difference between the two modes:
      items: bad
        ? { type: ['object', 'null'], properties: { a: { type: 'string' } } }
        : { type: 'object',           properties: { a: { type: 'string' } } }
    }
  }
};

let buf = '';
process.stdin.on('data', d => {
  buf += d;
  let i;
  while ((i = buf.indexOf('\n')) >= 0) {
    const line = buf.slice(0, i).trim(); buf = buf.slice(i + 1);
    if (!line) continue;
    let m; try { m = JSON.parse(line); } catch { continue; }
    const send = r => process.stdout.write(JSON.stringify(r) + '\n');
    if (m.method === 'initialize') send({ jsonrpc:'2.0', id:m.id, result:{ protocolVersion:'2024-11-05', capabilities:{tools:{}}, serverInfo:{name:'repro',version:'1'} } });
    else if (m.method === 'tools/list') send({ jsonrpc:'2.0', id:m.id, result:{ tools:[{ name:'repro_tool', description:'repro', inputSchema }] } });
    else if (m.method === 'tools/call') send({ jsonrpc:'2.0', id:m.id, result:{ content:[{type:'text',text:'ok'}] } });
    else if (m.id !== undefined) send({ jsonrpc:'2.0', id:m.id, result:{} });
  }
});

Run:

CFG='{"mcpServers":{"repro":{"type":"stdio","command":"node","args":["./gemini-mcp-repro.js","bad"],"tools":["*"]}}}'

# FAILS
copilot -p "hi" --model gemini-3.7-flash --allow-all-tools \
  --disable-builtin-mcps --additional-mcp-config "$CFG"

# Same command with "good" instead of "bad" -> succeeds

Result matrix

bad (items.type: ["object","null"]) good (items.type: "object")
gemini-3.7-flash ❌ 400 ✅ ok
gemini-3.1-pro-preview ❌ 400 ✅ ok
claude-sonnet-4.6 ✅ ok ✅ ok
gpt-5.4 ✅ ok ✅ ok

Gemini-specific, and isolated to a single JSON-Schema node.

Scope of the trigger

I bisected each construct with the same one-tool harness. A union type is only fatal on an
object-typed node:

Schema shape Result
{type: "string"}
{type: ["string","null"]}
{type: ["string","null"], default: null}
{description: "..."} — no type at all
{enum: [...], type: ["string","null"]}
{type: "string", format: "uuid"}
type: ["array","null"] + items.type: "object"
type: "array" + items.type: ["string","null"]
type: "array" + items.type: ["object","null"] 400

The CLI already strips $schema and normalizes several unsupported keywords before dispatch, so the
sanitization layer exists — it just does not recurse into items for object-typed unions. Correct
OpenAPI 3.0 output would be "type": "object" + "nullable": true.

Second, independent trigger

A boolean schema"properties": { "value": true }, valid JSON Schema 2020-12 meaning "accept
anything" — also produces a Gemini-only 400 via the same harness. Likely the same root fix.

Why this matters

  • MCP servers commonly emit ["object","null"] — it is what several popular schema generators
    produce for a nullable list-of-objects parameter. I hit it from two unrelated servers
    independently.
  • The failure is total: one bad tool out of ~180 loaded tools broke every Gemini model for me for
    six days across every session on the machine.
  • It arrived via a silent auto-update of an MCP server, with no config change on my side.
  • The user-visible symptom is 400 Bad Request with no further detail, so the natural user response
    is "Gemini is broken" → switch back to Claude. I suspect that is why this is widely experienced but
    rarely reported. See #1274, #4155, google-gemini/gemini-cli#22179 (Atlassian/Jira MCP, same
    trigger), gh-aw#20833, anomalyco/opencode#8417 — all plausibly the same defect.

Asks

  1. Normalize object-typed unions and boolean schemas in the existing MCP schema sanitizer, so a
    non-conformant server degrades gracefully instead of bricking a model family.
  2. Failing that, make the error actionable — name the offending server, tool, and JSON path in
    the 400. Today there is no way to find it short of an O(n) bisection over every tool.

(2) is arguably the more important fix: it converts an undiagnosable outage into a one-line message.

Environment

  • Copilot CLI 1.0.81-x, Windows 11
  • Repro is platform-independent (plain Node stdio server)
  • Request IDs from my failures: <paste your own>
Affected version

GitHub Copilot CLI 1.0.81-9.

Steps to reproduce the behavior

All the repro details are captured in the above blob. This is a 100% repro and Claude help me craft that minimal repro

Few more related/similar issues:
https://github.com/github/copilot-cli/issues/4155
https://github.com/github/copilot-cli/issues/1274
https://github.com/google-gemini/gemini-cli/issues/22179
https://github.com/github/gh-aw/issues/20833
https://github.com/anomalyco/opencode/issues/8417

Expected behavior

No response

Additional context

No response

贡献指南

打开贡献指南

从这里开始

  1. 先读完整个 Issue,再读项目的贡献指南。
  2. 在 Issue 下留言说明你要接手 —— 这能避免两个人做同样的事。
  3. Fork 仓库,在一个分支上完成修改。
  4. 提交 Pull Request,并在描述里引用这个 Issue 编号。

调研方向

先运行链接的 gemini-mcp-repro.js harness,分别使用错误和正确的 schema 变体,然后定位 issue 中描述的现有 MCP schema sanitizer。跟踪它如何处理嵌套项和布尔 schema。完成的标准是:针对这些 schema 的 Gemini 请求不再失败,或者错误能够指出有问题的 server、tool 和 JSON path。

由索引模型根据 Issue 内容生成。

评估

技术栈
javascript, json, shell
领域
api, cli
Issue 类型
缺陷
难度
4/5
预计耗时
3-5 天
活跃度
活跃
描述清晰度
基本清楚
新手友好度
55/100

把新 issue 发到你的邮箱

精选适合新手参与的 GitHub issue 摘要。