Claude CodeのMCPツールが任意パラメータ省略で拒否される原因と回避策
Claude DesktopのCodeタブでMCPツールの任意パラメータを省略すると、nonoptionalの検証エラーで呼び出しが拒否される不具合の原因と回避策を一次資料から解説します。
Claude Desktop(claude.aiのデスクトップアプリ)のCodeタブでMCPサーバーのツールを呼び出すと、JSON Schemaに default を持つ任意パラメータを省略しただけで拒否されることがあります。エラーは expected: "nonoptional", received: "undefined" という検証エラーで、ツール本体のロジックに届く前にクライアント側で止まります。ツールが「AとBのどちらか一方だけを指定する」という排他ルールを持つ場合は、省略しても明示指定してもどちらも通らない手詰まりになります。GitHub issue #94718で2026年9月16日から報告が続き、原因と回避策が技術的に特定されています。
Claude CodeのMCPツールで何が起きているか
症状は大きく2種類あります。1つ目は、任意パラメータを省略しただけで起きる単純な拒否です。Home Assistant連携の ha-mcp サーバーでは、fields と attribute_keys という2つの任意パラメータがどちらもJSON Schemaで {"default": null} と宣言されています。
{
"error": "MCP error -32602: Input validation error: Invalid arguments for tool ha_get_state: [
{\"code\":\"invalid_type\",\"expected\":\"nonoptional\",\"path\":[\"fields\"],\"message\":\"Invalid input: expected nonoptional, received undefined\"},
{\"code\":\"invalid_type\",\"expected\":\"nonoptional\",\"path\":[\"attribute_keys\"],\"message\":\"Invalid input: expected nonoptional, received undefined\"}
]"
}この単純なケースは、省略していた値を明示的に渡せば回避できます。問題は2つ目のケースです。ha_call_service というツールは、通常のサービス呼び出し(domain/service/entity_id)と、ws_command を使う一発呼び出しのどちらか一方しか受け付けません。ws_command を省略すると先ほどと同じ「値が無い」エラーになりますが、ws_command を指定すると今度はツール自身のロジックが「サービス呼び出し用のパラメータは ws_command を指定したときに含めてはいけない」と拒否します。省略も指定もどちらも失敗するため、通常のサービス呼び出しが一切できません。同じ手詰まりの形は ha_get_integration(entry_id と domain のどちらか一方)や ha_get_entity(entity_id と unique_id のどちらか一方)、ha_manage_hacs(操作ごとに必須・禁止パラメータが切り替わる)でも再現しています。
原因は同梱された2つのZodバージョンの判定のズレ
Claude DesktopのCodeタブは、MCPサーバーのツールを自分のプロセスでいったん登録し直してからClaude Codeに渡します。この途中で、同じスキーマ検証ライブラリのZodが2つの版で使われています。Desktop 2.110.0と2.110.1のバンドルを読んだ報告者3人は、次の流れを突き止めました。
拒否が起きるまでの経路
- 1
サーバーから tools/list を取得
サーバーが返す
inputSchemaにdefaultを持つプロパティがあると、次の変換でそこだけ扱いが変わります。 - 2
Zod 4.5.4でJSON Schemaを変換
defaultは.prefault(default)に、必須でないプロパティは.optional()になります。Zod 4.5では、この組み合わせの内部印がoptin: "defaulted"です。 - 3
Agent SDKに登録
変換した型を
tool()とcreateSdkMcpServerで登録し直し、これがClaude CodeのmcpServersとして渡されます。 - 4
Zod 4.4.3で引数を検証
Agent SDKに同梱された別コピーのZod 4.4.3は、
optin === "optional"のときだけ省略可と判定します。"defaulted"は一致しないので、省略したキーがnonoptionalで落ちます。 - 5
サーバーに届く前に拒否
検証はローカルで走るため、
tools/callはサーバーへ転送されません。
default を宣言しないプロパティは、素の .optional()(optin: "optional")になります。Zod 4.4.3の検証も省略可と読むので、省略しても通ります。
不整合は最小構成でも再現します。issueの報告者が示した再現では、Zod 4.5.4で作った z.number().prefault(200).optional() を、Zod 4.4.3の z.object() に組み込んで空オブジェクトを検証します。すると limit だけが nonoptional になり、default のない別のフィールドは指摘されません。両方を同じ4.5.4で作れば成功し、limit は200になります。
規模も報告されています。ある報告者の再現では、FastMCP 3.4.7のサーバーは26ツール中24ツールが、必須引数だけの呼び出しを拒否されました。mcp SDK付属のFastMCP 1.30.0のサーバーは33ツール中15ツールです。
見分け方は次のとおりです。エラーに "expected":"nonoptional" があり、拒否されたパラメータが tools/list で default を宣言しているなら、この不整合を疑えます。サーバー単体の問題かどうかは、同じサーバーのstdioプロセスに引数を直接送ると切り分けられます。省略した引数が通れば、拒否はDesktop側で起きています。default を含むスキーマ機能は、MCPのツール定義がJSON Schema 2020-12の全機能に対応でまとめています。
どの環境で再現するか
issueの報告はすべてClaude DesktopのCodeタブからで、ターミナル単体の claude CLIの報告は含まれません。macOSとWindows、ローカルのstdioサーバーと mcp-remote 経由のリモートHTTPサーバーのどれでも同じ形で再現しているため、原因はクライアント側の変換と検証に絞られました。
| 環境 | 状況 |
|---|---|
| Claude Desktop Codeタブ・ローカルstdioサーバー(macOS) | 状況再現。複数の独立したサーバーで再現。macOS更新後も再現 |
| Claude Desktop Codeタブ・ローカルstdioサーバー(Windows 11) | 状況再現。ビルド26200・MSIX版で確認 |
Claude Desktop Codeタブ・リモートHTTPサーバー(mcp-remote 経由、macOS) | 状況再現。mcp-remote は引数をそのまま転送し、自前では検証しない |
| claude.aiのWebコネクタ経由・同一サーバー | 状況再現しない。同じ呼び出しが成功する対照実験で確認 |
Webコネクタ経由で同じ呼び出しが通るので、サーバーの実装ではなく、Claude DesktopのCodeタブの呼び出し経路に限った問題です。
なお、MCPのツール呼び出しがクライアント側の検証で拒否される不具合はこれが初めてではありません。tools/list の応答に含まれる ttlMs や cacheScope といった拡張フィールドが原因で呼び出しが拒否される別の不具合も報告されており、詳細はMCPのtools/list応答がttlMs/cacheScopeで拒否される問題と回避策にまとめています。原因は異なりますが、いずれもクライアント側の検証でツール呼び出しやツール一覧が拒否される症状です。
バージョンごとの経緯
GitHub issueのコメントを時系列で追うと、この不具合が入ったバージョンと、一部の報告者で解消が確認されたバージョンは次のとおりです。
| Desktop / Claude Codeのバージョン | 状況 |
|---|---|
| Claude Code 2.1.270 | 状況任意パラメータを省略する呼び出しが最後に成功したバージョン |
| Claude Code 2.1.271 | 状況同じ形の呼び出しで初めてnonoptional拒否が発生 |
| Claude Desktop 2.110.0〜2.110.1 | 状況同じ不具合が継続して再現。関連issue #94608は一度クローズされたが再現は解消していない |
| Claude Code 2.1.274(Desktop 2.2553.0) | 状況ローカル拡張の呼び出しで解消したという報告あり |
2.1.274での解消は、macOSのローカルDesktop拡張(Apify連携)の報告です。query だけを渡す search-apify-docs と、keywords だけを渡す search-actors が通りました。報告者は2.110.1を途中で試しておらず、どのビルドの修正かは断定していません。リモートHTTPサーバーとWindowsでの再検証は、このコメントの時点では報告されていません。なお、2.1.270のままのセッションがこの経路を避けられた理由は、報告者も追っていません。
nonoptionalエラーを回避する方法
対処はサーバーを自分で管理しているかどうかで変わります。
サーバーを管理していない場合にできること
省略していた任意パラメータに、スキーマ上有効な値を明示的に渡すと、単純な拒否(先ほどの ha_get_state の例)は避けられます。ただし制約が2つあります。
1つ目は、「AかBのどちらか一方」を要求するツールで、この方法が成り立たないことです。2つ目は、anyOf: [{"type": X}, {"type": "null"}] のようにnullを許す型で、Desktopが文字列の "null" を送る点です。Joplin連携の joplin-mcp で、引数の送り方ごとの結果が報告されています。
| 任意引数の送り方 | サーバーのstdioへ直接 | CodeタブからDesktop経由 |
|---|---|---|
| 省略 | サーバーのstdioへ直接通る | CodeタブからDesktop経由nonoptional で拒否 |
JSONの null | サーバーのstdioへ直接通る | CodeタブからDesktop経由JSON nullは送られない(文字列の "null" を送る) |
文字列の "null" | サーバーのstdioへ直接サーバー側のエラー | CodeタブからDesktop経由Codeタブはこの形を送る |
文字列パラメータだけの問題ではありません。find_notes の offset({"type":"integer","default":0})のように、整数など型付きの default も同じ形で拒否されます。
排他的なツールは、どちらの側からも詰みます。edit_note は old_string と position が排他で、省略すると nonoptional、"null" や "" を渡すと Cannot specify both old_string and position が返ります。get_note も同じ形で、start_line: "null" は start_line must be an integer or string representation of an integer で失敗します。
MCPサーバーを自分で公開している場合の対処
サーバーを実装・運用しているなら、tools/list が返す inputSchema.properties から default キーだけを取り除き、デフォルト値の適用はサーバー側のハンドラで続ける方法が有効です。Desktopの変換は default の無いプロパティを素の .optional() にするため、Zod 4.4.3の検証も通ります。報告者の検証では、default を外したツールは省略した3引数の呼び出しが通りました。同じCodeセッションで、default を残した別のDesktop拡張は expected nonoptional で失敗したままでした。
FastMCP 3.xでは、ミドルウェアで次のように書けます。
# FastMCP 3.x: tools/list が返すスキーマから default だけを構造的に取り除く
from fastmcp.server.middleware import Middleware
class StripDefaults(Middleware):
async def on_list_tools(self, context, call_next):
tools = await call_next(context)
return [
t.model_copy(update={"parameters": schema_without_property_defaults(t.parameters)})
for t in tools
]
server.add_middleware(StripDefaults())schema_without_property_defaults は、properties 直下の default だけを削る自作の関数です。
スキーマを書き換えるときは、キー名ではなく構造を辿り、properties 直下の default だけを対象にします。パラメータ自体が properties という名前を持つことがあるためです。
サーバーのコードを触れない場合は、別の報告者が示した方法もあります。サーバーの前に小さなstdioラッパーを挟み、tools/list の応答にある各ツールの inputSchema.properties 直下から default を削除します。サーバーは自分のデフォルトを適用し続けるので、呼び出しの挙動は変わりません。Desktopは claude_desktop_config.json をメモリに持ち、自分で書き戻します。起動コマンドを書き換えるのは、アプリを終了している間にします。
設定を直しても直らないときに確認すること
サーバー側の default を外しても、Codeタブは古い拒否を返し続けることがあります。default を外したツールを配備した報告者は、Claude Desktopを完全に終了して起動し直すまで、省略した引数が拒否されたと述べています。ツール一覧がアプリ全体の接続単位で保持されているためで、起動し直した後は同じ呼び出しが通りました。
ターミナルのClaude Codeには、/mcp から再接続する操作があります。
/mcp公式ドキュメントでは、接続中または失敗状態のサーバーでReconnectを選ぶと、ディスカバリーキャッシュのエントリが破棄され、次の接続でツール一覧が取り直されます。cached 状態のサーバーでは、その場で接続してエントリは残ります。ローカルのstdioサーバーは、Claude Codeの自動再接続の対象外です。
サーバーが list_changed 通知を送ったときの動きは、実行形態で異なります。対話型のターミナルセッションでは、Claude Codeが一覧を取り直します。-p モードとAgent SDKでは、更新されるのはツール一覧だけです。この仕組みはMCPのツール更新と接続断からの自動再接続の仕組みで扱っています。Desktopアプリがツール一覧を保持している今回の件は、これらの経路の外にあります。
Zodを別々に同梱する構成は変換と検証の意味をずらす
報告者がバンドルから読み取った内容では、今回のズレは、変換と検証が別々のZodで動いたことから来ています。Desktopが同梱するMCP用TypeScript SDK側は4.5.4、ツール実行を担うAgent SDK側は4.4.3でした。サーバーが任意のJSON Schemaを宣言し、クライアントがそれを別の型システムへ変換して検証するMCPでは、変換と検証を同じ版のライブラリで行わないと、同じスキーマの意味がずれます。MCPクライアントを自作・拡張する場合は、依存ライブラリを分けて同梱する構成が、この種のずれの入り口になります。
まとめ
default を宣言した任意パラメータの省略が nonoptional で拒否されたら、まずエラーの文言と、同じ呼び出しがWebコネクタやstdioへの直接送信で通るかを見てください。サーバーを管理しているなら default を外して、Desktopを完全に再起動します。自分で直せるのは、サーバーのスキーマと、Desktopの再起動までです。管理していないサーバーの排他的なツールは、Desktop側の修正が入るまで使えないことがあります。