Claude Media
MCPのツール定義がJSON Schema 2020-12の全機能に対応

MCPのツール定義がJSON Schema 2020-12の全機能に対応

MCPの2026-07-28仕様改訂で、ツール定義のJSON Schemaが2020-12の全キーワードに対応しました。structuredContentの型拡大と$ref解決の安全要件も解説します。

MCPの2026-07-28仕様改訂で、ツール定義のinputSchemaとoutputSchemaが使えるJSON Schemaのキーワードが「2020-12の任意のキーワード」に広がりました。

$refやanyOf・oneOf・allOfを使ったスキーマも、仕様の想定内です。同時にstructuredContentはJSONオブジェクト限定から任意のJSON値になり、$refの解決と合成キーワードの計算量には新しい安全要件が加わりました。SEP-2106として提案されたこの変更は、MCPサーバーの実装者と、そのサーバーをClaude Codeで動かす利用者の両方に関わります。

自分のMCPサーバーは直す必要があるのか

結論から言うと、既存の定義が無効になる変更ではありません。2026-07-28版で変わったのは3点で、いずれも「できることが増える」か「実装側に守る原則が増える」かのどちらかです。

くらべる

2025-11-25版と2026-07-28版の違い

変更前

2025-11-25版まで

structuredContentは「JSONオブジェクトとして返される」と明記されていました。$refのネットワーク解決や合成キーワードの計算量については、basicのJSON Schema節に規定がありません。

変更後

2026-07-28版以降

inputSchema・outputSchemaは2020-12の任意のキーワードを許容し、structuredContentは任意のJSON値(配列・文字列・数値・真偽値・null)を返せます。

$refの自動解決は既定で禁止、合成キーワードには妥当な上限を設けることがSHOULDです。

スキーマ方言の既定が2020-12である点は、2025-11-25版と変わりません。両版とも、$schemaを省略したスキーマは2020-12として扱われ、$schemaで別の方言を明示することもできます。$schemaでdraft-07を明示したスキーマは、2026-07-28版でも仕様上の例にそのまま載っています。

広がったのは、方言を省略した2020-12スキーマで使えるキーワードの範囲です。ただしoutputSchemaに限っては、draft-07を明示していてもクライアント側の検証器が接続前に拒否する不具合が報告されています。詳しくはMCP outputSchemaがdraft-07で拒否される原因と対処法にまとめました。

structuredContentに配列を返せるようになった

ユーザー一覧を返すツールの結果は、配列のまま次のように書けます。

{
  "content": [{ "type": "text", "text": "Found 2 users: Alice and Bob." }],
  "structuredContent": [
    { "id": "1", "name": "Alice", "email": "alice@example.com" },
    { "id": "2", "name": "Bob", "email": "bob@example.com" }
  ]
}

2026-07-28版のtoolsには、構造化結果を返すツールは互換性のため、シリアライズしたJSONをTextContentブロックにも返すべき(SHOULD)という規定もあります。

対応するoutputSchemaは"type": "array"で、itemsにオブジェクトのスキーマを置きます。2025-11-25版の文面どおりに読めば、この結果は範囲外でした。

宣言したスキーマとの整合性を取る責任は、型が広がっても残ります。サーバーはoutputSchemaに適合する構造化結果を返す義務(MUST)があり、クライアントはその結果を検証することが推奨(SHOULD)されています。

クライアント側の実装には1つ注意点があります。結果を常にオブジェクトとして読むパーサーは、配列や文字列を返す新しいツールで壊れます。型を決め打ちせず、outputSchemaのtypeを見て分岐する作りにしておくと、サーバー側の型拡大に追随できます。

$refと合成キーワードに課された上限

$refはJSON Schema 2020-12で絶対URIを指せます。仕様はここに制約を課しました。実装はネットワークのURIに解決される$refを自動で辿ってはいけません(MUST NOT)。

任意で外部参照を取得するモードを用意してもよいものの、既定では無効にする必要があります。有効にするなら、ホストの許可リストを持つか、最低でもループバック・リンクローカル・プライベートアドレスを拒否し、タイムアウトとサイズ上限を設け、辿ったURIをログに残すことが推奨されています。外部$refが解決できずに検証できなかったスキーマは、黙って「何でも通す」扱いにせず拒否するのがSHOULDです。

ツール定義は外部のMCPサーバーから届く、信頼度の低い入力です。そこに含まれる$refをそのまま辿れば、検証器自身がSSRF(サーバー側リクエスト偽造)の踏み台になります。

合成キーワード(anyOf・oneOf・allOf・if/then/else)と$defsにも上限があります。表現力が高い反面、検証コストが跳ね上がるためです。仕様はスキーマの最大の深さ、サブスキーマの総数、検証1回あたりの時間予算といった妥当な上限を設け、悪意あるスキーマが検証器へのサービス拒否(DoS)に働くのを防ぐことをSHOULDとしています。

2つの要件に共通するのは、具体的な数値を決めていない点です。「深さ5まで」のような値は仕様になく、上限をいくつにするかは各実装の判断になります。

手元の検証ライブラリでの挙動

仕様の「自動解決しない」を、実在の検証ライブラリで確かめた例を示します。Node.jsでAjv 8.20.0(2020-12対応版)に、存在しない外部URLを指す$refのスキーマを渡しました。

npm i ajv@8
node t.mjs

t.mjsは、外部$refを持つスキーマのコンパイル、配列スキーマでの検証、ルート直下のanyOfの検証の3つを実行します。要点は次のコードです。

import Ajv2020 from "ajv/dist/2020.js";
const ajv = new Ajv2020({ strict: false });
ajv.compile({ type: "object", properties: { a: { $ref: "https://example.com/schemas/user.json" } } });

3つ分の出力は次のとおりです。

1: MissingRefError - can't resolve reference https://example.com/schemas/user.json from id #
2: true false
3: true false

1行目のとおり、Ajvはネットワークからスキーマを取得せずにMissingRefErrorで失敗し、外部URLへのアクセスは起きません。仕様が求める「解決できない外部$refは拒否する」と同じ向きの挙動です。2行目は、配列スキーマが[{ "id": "1" }]を通し、オブジェクト{ "id": "1" }を弾いた結果です。structuredContentが配列のとき、outputSchemaがどう効くかを示しています。3行目は、pathとurlのどちらか一方を必須にするanyOfが、{ "path": "a" }を通し空オブジェクトを弾いた結果です。

Claude CodeはMCPツールの定義をどう扱うか

Claude Codeは、MCPサーバーのツール定義をClaude APIへ送る前に、独自の処理を挟みます。流れは次のとおりです。

手順

ツール定義がAPIに届くまで

  1. 1

    サーバーのツール一覧を読み込む

    tools/listの結果から各ツールのinputSchemaを取り出します。

  2. 2

    ルート直下の合成キーワードを平坦化する

    Claude APIはスキーマのルート直下のanyOf・oneOf・allOfを受け付けないため、1つのオブジェクトにまとめ、パラメータの組み合わせをツールの説明文の先頭に足します。propertiesの中にネストした合成キーワードは、そのまま送信されます。

  3. 3

    2つのチェックを自前で実行する

    トップレベルのプロパティ名(ASCII文字・数字・_・.・-のみ、1〜64文字)と、2020-12メタスキーマへの適合を確認します。メタスキーマ検査は、$schemaを省略したスキーマと2020-12を明示したスキーマが対象です。2020-12以外の方言を$schemaで宣言したスキーマはメタスキーマ検査を受けませんが、トップレベルのプロパティ名の検査は適用されます。

  4. 4

    失敗したツールだけを除外する

    失敗したツールは単体で除外され、同じサーバーの他のツールは使えます。理由はサーバーのログに記録され、Claudeにも伝わるので、ツールが無い理由をClaudeに尋ねられます。

平坦化の中身は分岐ごとに違います。allOfは各分岐のpropertiesとrequiredをすべて反映します。anyOf・oneOfはpropertiesをマージし、requiredを説明文側に回します。公式ドキュメントも、サーバーはClaudeが選んだ引数をそのまま受け取るため、組み合わせの検証はサーバー側で続けるよう求めています。2つのチェックはv2.1.216で入り、それ以前は、どの環境でも実行されていませんでした。

フラグが届かない環境では、ルート直下の合成キーワードを持つツールは平坦化されず、そのツール1つだけがスキップされ、理由がサーバーのログに残ります。ルート直下に合成キーワードを持つサーバーを使うなら、実際にツールが見えているかを確かめるのが確実です。詳細はClaude CodeのMCPでルートレベルanyOf/oneOf/allOfを平坦化する仕組みで扱っています。

APIが400を返したときのエラー文言は、公式のエラーページに次の2形式で載っています。数字Nは、リクエストのツール一覧での位置であって、ツール名ではありません。1つ目の形式はスキーマがJSON Schema 2020-12として無効という意味で、2つ目は、トップレベルのプロパティ名がメッセージに引用されたパターンに合わないという意味です。Claude Codeがv2.1.216より前なら、claude updateで更新します。

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}$'

MCPサーバー開発者が確認する2点

自分のサーバーを2026-07-28版に合わせるときの確認ポイントは次の2つです。

チェック

確認する2点

  • 合成キーワードの配置

    ルート直下のanyOf・oneOf・allOfは、Claude Code経由だと平坦化され、制約が説明文頼みの緩いものになります。厳密さが要る制約は、propertiesの中に置く設計が確実です。

  • $refの解決ポリシー

    外部URLへの$refを動的に解決する設計なら、許可リストと拒否条件をサーバー側で明文化します。ローカルの$defsだけで足りる構造にすれば、この問題自体を避けられます。

claude -p --json-schemaの検証とは別物

ここまでのJSON Schema 2020-12は、MCPのツール定義の話です。Claude Codeのclaude -p --json-schemaが使うJSON Schemaとは別の仕組みです。

--json-schemaは、Claudeの応答そのものを指定した構造に固定する、構造化出力の機能です。構造化出力のドキュメントは、SDKがdraft-07で検証し、新しい方言を宣言したスキーマは拒否されると書いています。Zodなど2020-12を既定で出力するライブラリからスキーマを作る場合は、target: "draft-7"の明示が必要です。エラーメッセージや挙動はClaude Code --json-schemaエラーの直し方にまとめてあります。

つまり、MCPのツール定義のスキーマと、--json-schemaに渡す応答用のスキーマは別々に検証されます。どちらのスキーマの話かを取り違えると、直す場所を間違えます。

まとめ

今回の改訂は、MCPサーバーの書き手にとっては表現の自由度が広がった変更で、クライアントの実装者にとっては$refと合成キーワードの扱いに原則が加わった変更です。サーバー側の立場なら合成キーワードと$refの置き方、クライアント側の立場ならstructuredContentの型と$refの解決ポリシーが、見直す節になります。

サーバーを自作する手順はMCPサーバー自作ガイドにあります。

同じ2026-07-28版で変わったOAuth認可は、リモートMCPのOAuth認証で扱っています。

この記事を共有:XはてブLinkedIn
MCP をもっと見る →