github / github/copilot-cli

MCP tool arguments with array-or-string union schema (anyOf) are stringified before reaching the server

オープン
#4,301 コメント 0 件 リアクション 0 件 担当者 0 名 GitHub で見る

まだ誰も着手していません。

area:mcp area:tools
主要言語
Shell
スター
11.2k
フォーク
1.9k
平均マージ
14時間 16分
マージ済み PR(30日)
6

説明

Describe the bug

When an MCP tool parameter's JSON Schema declares a union that includes both array and string (e.g. anyOf: [array, string] or a Zod z.union([z.array(z.string()), z.string(), z.null()])), Copilot CLI's MCP client flattens/stringifies the argument before sending it to the server -- even when a genuine JSON array is supplied. The MCP server (which correctly expects and validates an array) then rejects the call, because it receives a bare string instead.

This reproduces against the Obsidian Local REST API plugin's MCP server (obsidian-mcp), specifically its vault_patch and vault_read tools' target parameter, used for heading-path addressing (e.g. ["Parent Heading", "Child Heading"]).

Affected version

GitHub Copilot CLI 1.0.76 (Windows)

Steps to reproduce the behavior
  1. Register an MCP server exposing a tool whose parameter schema is z.union([z.array(z.string()), z.string(), z.null()]) (or the JSON-Schema equivalent anyOf: [{type: array, items: {type: string}}, {type: string}, {type: null}]).
  2. From Copilot CLI, call that tool with a genuine array value, e.g. target: ["Top", "Child"].
  3. Observe the server rejects the call as if a bare string were sent (e.g. A heading target must be an array of heading texts, not a bare string), even though the client was given a proper array.
  4. Direct/bypass test: calling the identical tool with the identical array directly against the server's /mcp JSON-RPC endpoint (bypassing the Copilot CLI client) succeeds and returns the correct result -- confirming the server is correct and the array is valid.
Root cause (isolated via a controlled A/B test)

We edited the MCP server's own schema for the affected parameter, removing the string option so the union became array | null only (no code changes elsewhere; identical Copilot CLI client, identical server, identical call). With that one change:

  • The exact same array argument (["Top", "Child"]) that previously failed now succeeded and correctly resolved a two-level nested heading.
  • A single-element array (["Top"]) also succeeded.

This isolates the bug precisely: Copilot CLI's MCP client only mis-serializes array arguments when the parameter's declared schema is a union that also includes string. Parameters that are plain/unconstrained JSON (no such union), or presumably schemas of array | null with no string sibling, pass arrays through untouched -- we independently confirmed a plain JSON-typed parameter (no union) on the same server correctly accepted and round-tripped a real array in the same session.

Reproduction table
Channel Argument Parameter schema Result
Copilot CLI MCP tool call target: ["Top","Child"] anyOf: [array, string, null] Fails -- arrives as a stringified value, rejected as "not a bare string"
Copilot CLI MCP tool call target: ["Top"] anyOf: [array, string, null] Fails -- same error
Copilot CLI MCP tool call target: [] (empty array) anyOf: [array, string, null] Fails -- same error
Copilot CLI MCP tool call target: null anyOf: [array, string, null] Fails -- null also arrives stringified
Direct /mcp JSON-RPC call (bypassing Copilot CLI) target: ["Top","Child"] (real array) same schema Succeeds
Copilot CLI MCP tool call, schema patched to remove string target: ["Top","Child"] union([array, null]) Succeeds -- correct nested-heading resolution
Copilot CLI MCP tool call, schema patched to remove string target: ["Top"] union([array, null]) Succeeds
Copilot CLI MCP tool call plain JSON-typed parameter (no union), given ["a","b"] no union constraint Succeeds -- array passed through untouched
Expected behavior

A genuine JSON array argument should be transmitted to the MCP server as an array, regardless of whether the parameter's schema also permits a string (or other) alternative in the same union/anyOf.

Additional context
  • This is likely related to #2634, which reports Copilot CLI's MCP tool-schema handling losing/narrowing fields and unions (including an anyOf mixing array and object) between what an MCP server advertises and what the model/client actually uses. That report is about schema-definition loss at tool-discovery time; this report is about argument-value corruption at call time for a union schema. Both point at the same general area of Copilot CLI's MCP layer mishandling anyOf/union types that include array, so they may share a root cause.
  • This is scoped narrowly: it is not "Copilot CLI can't send array arguments" -- arrays work fine for parameters with a plain array (or non-union) schema. The bug is specific to a schema union that pairs array with string (and possibly other non-array types).

コントリビューションガイド

コントリビューションガイドを開く

はじめの一歩

  1. issue を最後まで読み、次にプロジェクトのコントリビューションガイドを読みます。
  2. 着手することを issue にコメントします — 二人が同じ作業をするのを防げます。
  3. リポジトリをフォークし、ブランチを切って変更します。
  4. issue 番号を参照したプルリクエストを送ります。

調査の方向性

公開されたツールスキーマを呼び出し引数に変換する Copilot CLI MCP クライアントのパスから始め、文書化された /mcp JSON-RPC の再現手順を使って、直接リクエストと CLI を介したリクエストを比較します。anyOf に array、string、null が含まれる場合でも、array 値と null 値が型付きの値のまま維持されることを確認し、既存の単純な array のケースと非ユニオンのケースも引き続き動作することを確認します。

索引モデルが issue の本文から書いたものです。

評価

技術スタック
shell
領域
api, cli
issue の種類
バグ
難易度
4/5
見積もり時間
3〜5日
活発さ
静か
明瞭さ
明確に書かれている
初心者へのやさしさ
67/100

新しい issue をメールで受け取る

初心者向けの GitHub issue を短くまとめたダイジェスト。