Claude Codeのinput_schemaエラー「JSON schema is invalid」の原因と対処
tools.N.custom.input_schema: JSON schema is invalidの400エラーは、MCPツールのスキーマ不備をAPIが拒否したもの。Nの意味、環境変数で再発する理由、切り分け手順をまとめます。
API Error: 400 ... tools.N.custom.input_schema: JSON schema is invalid は、リクエストに載せたツールの1つが、APIのJSON Schema検証に落ちたときのエラーです。1つのツールが原因でも、リクエスト全体が拒否されます。会話は先に進まなくなります。
原因はほぼMCPサーバーが返したツール定義です。直し方は、該当サーバーを無効にするか、サーバー側のスキーマを直すか、Claude Codeを更新するかの3択になります。
この記事は、エラー文言から原因を特定して直す手順を扱います。ルート直下のanyOfなどを平坦化する仕組みそのものは、ルートレベルanyOf/oneOf/allOfの平坦化に譲ります。
エラーの2つの形と、Nの意味
公式のエラー一覧には、同じ検証が出す2つの形が載っています。
API Error: 400 ... tools.N.custom.input_schema: JSON schema is invalid
API Error: 400 ... tools.N.custom.input_schema.properties: Property keys should match pattern '^[a-zA-Z0-9_.-]{1,64}$'| 形 | 意味 |
|---|---|
JSON schema is invalid | 意味スキーマがJSON Schema draft 2020-12として正しくない |
Property keys should match pattern | 意味トップレベルのプロパティ名が、メッセージに引用されたパターンに合わない |
tools.の後ろの数字は、ツール名ではありません。そのリクエストのツール一覧での位置です。数字を手がかりにツール名を検索しても出てきません。この点が、エラー文だけでは原因を特定しにくい最大の理由です。
プロパティ名のルールは、1〜64文字で、使えるのはASCIIの英字・数字・_・.・-だけです。日本語のキー、スペースを含むキー、user:nameのようなコロン入りのキーは、このパターンから外れます。
Claude Codeは通常、このエラーを出さないはずだった
Claude Codeは、MCPサーバーのツールを読み込む時点で、APIと同じ検証のうち2つを自前で走らせています。トップレベルのプロパティ名と、draft 2020-12メタスキーマへの適合です。落ちたツールは除外され、同じサーバーの他のツールは使えます。除外の理由はサーバーのログに残り、Claudeにも伝わります。「あのツールはなぜ無いのか」とClaudeに尋ねれば答えが返ります。
つまり、このエラーが画面に出るのは、除外の仕組みが働かなかった場合です。公式ドキュメントから読み取れる条件は3つです。
- Claude Codeがv2.1.216より前のバージョンである。このバージョンより前は、どの環境でも検査が走っていなかった
- 除外がフィーチャーフラグで有効になる仕組みなのに、フラグが取得できていない
- ツールが
$schemaで2020-12以外の方言を宣言している
フラグ取得が止まる環境
除外はAnthropicから取得するフィーチャーフラグで有効になります。取得されない環境では、検査は走ってログに「このツールは弾かれる」と記録するだけで、スキーマはそのままAPIへ送られます。
フラグ取得が止まる条件は、公式の環境変数ページに列挙されています。
DISABLE_GROWTHBOOK、DISABLE_TELEMETRY、DO_NOT_TRACK、CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFICのいずれかを設定したセッション- Amazon Bedrock、Claude Platform on AWS、Google CloudのAgent Platform、Microsoft Foundryなどのサードパーティプロバイダー経由のセッション。ただし、Claude Codeを組み込むホスト側が
CLAUDE_CODE_PROVIDER_MANAGED_BY_HOSTを設定している場合は除く - Claude apps gatewayのセッション
- エアギャップ環境のように、フラグが一度も届いたことがないマシン
プライバシーのためにテレメトリを切った環境や、クラウド経由で使う社内環境が、そのまま該当します。ここでエラーが出るのは設定ミスではなく、除外機能の対象外になっているためです。除外が効く環境なら、別の人は同じサーバーで何も起きません。同じ.mcp.jsonを使っていても、メンバーによって症状が分かれるときは、この違いを疑う価値があります。
自分のシェルに該当する変数が入っているかは、次のコマンドで確認できます。
env | grep -E '^(DISABLE_GROWTHBOOK|DISABLE_TELEMETRY|DO_NOT_TRACK|CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC)='何か表示されても、それだけで取得が止まるとは限りません。どの値で取得が止まるかは、環境変数ページの各変数の行に書かれています。
フラグ取得が止まると、ほかにも使えなくなる機能があります。/skill-doctorやclaude importコマンドがその例です。これらが動かないなら、同じ理由で除外も効いていない、と推測できます。公式の判定方法ではなく、筆者の推測です。
更新した直後の最初のセッション
v2.1.216に更新しても、除外がすぐ効くとは限りません。インストールや更新の後の最初のセッションでは、フラグ依存の機能が欠けていることがあります。フラグはそのセッションの中で取得され、マシンに保存されます。保存されたフラグが効くのは次のセッションからです。更新後に一度エラーが出ても、Claude Codeを起動し直して再現するか確かめてから、サーバーを疑っても遅くありません。
2020-12以外の方言を宣言したスキーマ
$schemaに別の方言を書いたスキーマは、メタスキーマ検査の対象外です。Claude Code側では通過します。ところがAPIは2020-12として検証するため、400になる場合があります。プロパティ名の検査は方言に関係なく適用されます。
原因のサーバーを見つける手順
エラー文にツール名が無いので、サーバー単位で絞り込みます。
- Claude Codeをv2.1.216以降に更新する。
claude update
claude --version- v2.1.216以降なら、各MCPサーバーのログに、入力スキーマが拒否されるツールを名指しした行が出ていないか確認する。見つかれば、そのサーバーとツールが原因です。
- ログに無ければ、
/mcpパネルでサーバーを1つずつ切り替えて無効にし、エラーが消える組み合わせを探す。無効化は設定を消さずに接続だけを止めるので、戻すのも簡単です。
> /mcp
(サーバーを1つ無効にして、同じ操作をもう一度試す)3の絞り込みは、サーバーが多いほど時間がかかります。半分ずつ無効にして絞り込むと早く済みます。
/mcpでの無効化は、プロジェクトごとの選択として~/.claude.jsonのdisabledMcpServersに記録されます。サーバーの定義は消えません。名前の似た設定にdisabledMcpjsonServersがありますが、こちらは.mcp.jsonに書かれたサーバーを承認するかどうかを決める別の設定です。/mcpのトグルとは関係がないので、切り分けの途中で混同しないようにします。
どのサーバーを疑うか迷うときは、先に設定済みのサーバーを一覧にします。
claude mcp list各サーバーの横に✔ Connectedや✘ Failed to connectのような状態が出ます。接続に成功しているサーバーが、スキーマの不備を持つ候補です。接続できていないサーバーのツールは読み込まれず、リクエストにも載らないため、まず候補から外せます。
原因のサーバーが見つかったら、作者に修正を求めるか、そのサーバーを外します。急ぎなら外すのが確実です。完全に外すときはclaude mcp remove <名前>を使います。一時的に止めるだけなら、先ほどの/mcpでの無効化のほうが、設定が残るぶん戻しやすい方法です。
自分で作ったMCPサーバーの直し方
MCPサーバーの開発者側では、tools/listが返すinputSchemaを次の観点で見直します。
- トップレベルの
propertiesのキーが、^[a-zA-Z0-9_.-]{1,64}$に合っている - スキーマがdraft 2020-12のメタスキーマに合っている
$schemaを書くならhttps://json-schema.org/draft/2020-12/schemaにする(省略も可)
プロパティ名の確認は、サーバーのテストに次のような数行を足すと機械的に済みます。次の例は確認用の最小の書き方です。
// tools/list の結果 tools に対して、トップレベルのキー名を検査する
const re = /^[a-zA-Z0-9_.-]{1,64}$/;
for (const tool of tools) {
const keys = Object.keys(tool.inputSchema?.properties ?? {});
for (const k of keys) {
if (!re.test(k)) console.error(`${tool.name}: 不正なキー "${k}"`);
}
}メタスキーマへの適合は、2020-12に対応したバリデーターで確認します。メタスキーマに違反するスキーマは、Claude Code側の検査で除外の対象になります。
次のようなスキーマは、キー名の面で引っかかります。
{
"type": "object",
"properties": {
"user name": { "type": "string" },
"ユーザーID": { "type": "string" }
}
}user_nameとuser_idのような名前に直せば通ります。説明を日本語で付けたいときは、キー名ではなくdescriptionに書きます。
直したスキーマがいつ反映されるか
スキーマを直したら、除外されていたツールはClaude Codeが次にそのサーバーのツールを読み込んだときに戻ります。
サーバーがlist_changed通知を送る実装なら、再接続は要りません。対話型のターミナルセッションでは、通知を受けてClaude Codeが更新後の一覧を取り直します。-pフラグの非対話モードやAgent SDKでは、通知で更新されるのはツール一覧だけです。
取り直しに失敗したときは、前回取得したツール一覧が次の成功まで保たれます。v2.1.214より前は、一時的なエラーでツール一覧が空に置き換わっていました。古いバージョンで「直したのにツールが消えたまま」になる場合は、この挙動が絡んでいる可能性があります。
似た名前のエラーとの見分け方
--json-schemaフラグで渡した自分のスキーマが弾かれる場合は、Error: --json-schema is not a valid JSON Schemaという別のエラーです。CLIが実行前に出すもので、API側の400とは起きる層が違います。そちらはClaude Code --json-schemaエラーの直し方にまとめています。
MCPのスキーマ全般で何が許されるか、$refや合成キーワードの扱いはMCPのツール定義とJSON Schema 2020-12の対応範囲に詳しくあります。ルート直下にanyOfなどを持つツールは、除外ではなく平坦化される別の経路を通ります。この処理はフラグ取得が止まる環境でも、独自の挙動を保ちます。その仕組みはルートレベルanyOf/oneOf/allOfの平坦化が扱っています。
まとめ
Nは位置番号で、ツール名ではありません。Claude Code v2.1.216以降は、検査で落ちるMCPツールを除外します。それでもエラーが出るのは、フラグ取得が止まる環境か、2020-12以外の方言を宣言したスキーマの場合です。
対処は、更新、サーバーのログ確認、サーバーを1つずつ無効化する切り分け、の順に進めます。サーバー作者なら、トップレベルのキー名と2020-12への適合を直します。