MCPのエラーコードは-32000番台をどう割り当てるか
MCPはJSON-RPCの予約領域-32000〜-32099を、レガシーとMCP予約の2つに区分し、独自コードは-32768〜-32000の外に置くよう求めています。自作サーバーが新しいエラーコードを割り当てる基準をまとめました。
MCPのエラー応答はJSON-RPC 2.0の仕様にそのまま乗っています。標準コード5つは、解析エラーや不正なパラメータなど、実装を問わない一般的な失敗を表す共通コードです。問題はその先です。JSON-RPC自体が実装固有のサーバーエラー用に確保した100個のコード空間を、MCPはどう使い分けているのか。2026-07-28版の仕様は、この領域をレガシーとMCP予約の2つに区分し、新しいコードの置き場を予約範囲の外に定めています。自作MCPサーバーが独自のエラーコードを割り当てるとき、この区分を知らないと標準コードと衝突する余地を残します。
MCPのエラーコードはなぜ標準の5コードだけでは足りないのか
JSON-RPC 2.0が定義する標準エラーコードは、固定された意味を持つ5つと、実装が自由に使える-32000〜-32099の領域です。たとえば未知のツール名やプロンプト名を渡したとき、カーソルが期限切れのときはいずれも-32602(Invalid params)が返ります。
しかしMCP固有の失敗はこの5つには収まりません。HTTPヘッダーとリクエストボディの値が矛盾している、クライアントが宣言していない機能をサーバーが要求している、リクエストのプロトコルバージョンをサーバーが認識できない。これらはJSON-RPCの一般エラーではなく、MCPというプロトコルの構造に固有の失敗です。MCPは実装定義領域の中に、自分専用のコードを持つ必要がありました。
標準5コードはMCPの中で何を表しているか
標準コードの意味は仕様書の記述だけでは実感しにくいので、MCPの文脈でどう使われるかを見ておきます。
| コード | 名前 | MCPでの主な発生場面 |
|---|---|---|
-32700 | 名前ParseError | MCPでの主な発生場面受信したJSONそのものが構文的に壊れている |
-32600 | 名前InvalidRequestError | MCPでの主な発生場面jsonrpcやmethodの欠落など、リクエストの形が仕様に沿っていない |
-32601 | 名前MethodNotFoundError | MCPでの主な発生場面サーバーが実装していないメソッド、または広告していないサーバー機能配下のメソッド |
-32602 | 名前InvalidParamsError | MCPでの主な発生場面不明なツール名・引数の型不一致・期限切れカーソル・不正なログレベルなど |
-32603 | 名前InternalError | MCPでの主な発生場面受信側の内部で予期しない状態が発生し、リクエストを処理できない |
見落としやすいのは-32601(MethodNotFoundError)と-32021(MissingRequiredClientCapabilityError)の使い分けです。仕様はこの2つを明確に区別しています。サーバーが広告していない機能(たとえばprompts機能を実装していないサーバーにprompts/listを呼んだ場合)は-32601です。一方でクライアントが宣言していない機能をサーバー側が要求する場合は-32021になります。前者はサーバー機能の有無、後者はクライアント機能の有無が原因という違いで、どちらも「機能が足りない」という結果だけを見ると混同しやすいところです。
-32602(InvalidParamsError)がカバーする範囲もMCPでは広く、単一のコードで複数のカテゴリの検証失敗を束ねています。ツール呼び出しでは不明なツール名や引数の型不一致、プロンプト取得では未知のプロンプト名や必須引数の欠落、ページネーションでは無効・期限切れのカーソル、ロギングでは不正なログレベルの指定がここに含まれます。elicitationでは、クライアントのcapabilitiesに宣言されていないモードをサーバーが要求した場合も同じ-32602です。samplingでは、必要なツール実行結果が欠けている、あるいはツール実行結果と他の種類のコンテンツが混在しているケースが該当します。これだけ多様な失敗を1つのコードにまとめている以上、原因の切り分けはmessageとdataの記述に委ねられている、という設計だと分かります。
-32000から-32099までの2区分と、その外側の自由領域
2026-07-28版の仕様は、この100個のコード空間を2つに分け、新しいコードは予約範囲の外に置くよう定めています。表の3行目は、予約範囲の外側の扱いを並べたものです。
| 範囲 | 区分 | 扱い |
|---|---|---|
-32000〜-32019 | 区分レガシー | 扱い新しいコードの割り当ては禁止(MUST NOT)。新規実装は使用も避ける(SHOULD NOT)。受信側は意味を仮定しない(-32002を除く) |
-32020〜-32099 | 区分MCP予約 | 扱い仕様が定義したコードだけを発行でき、定義外の値は発行禁止(MUST NOT)。定義済みコードは指定された意味でのみ使う(MUST) |
-32768〜-32000の外側 | 区分自由領域(上の2区分の外) | 扱いアプリケーション定義のエラー。新しいコードの置き場として推奨(SHOULD)されるのはここ |
レガシー領域は、このポリシーが導入される前に各実装が独自に割り当てていたコードの受け皿です。仕様はこの範囲への新しいコードの割り当てそのものを禁止し、新規実装には使用自体も避けるよう求めています。受信側もこの範囲のコードに特定の意味を仮定してはいけません。唯一の例外が-32002で、これは2025-11-25以前の版で「リソースが見つからない」を意味していたコードです。現行版はこの意味を-32602(Invalid params)に統合したため新規実装は-32002を発行しませんが、クライアントは旧サーバーからの-32002を引き続き受け入れるべきだとされています。もう1つの旧コード-32042(URL elicitation required)は2025-11-25版限定の一時的なコードで、現行版には引き継がれていません。
過去の版が定義したコードは予約されたままで、再利用もされません。現行版を実装するサーバーは、-32002も-32042も発行してはいけない決まりです。リソースが存在しないときの返し方は、現行版では-32602に一本化されています。存在しないリソースに対して空のcontents配列を返してはいけません(MUST NOT)。内部エラーは-32603で返すのが推奨(SHOULD)です。サーバーはdataにuriを載せられ、仕様の例でも"data": { "uri": "file:///nonexistent.txt" }が付いています。
MCP予約領域は逆に、仕様に定義されていないコードを実装が勝手に発行することを禁じています。定義済みのコードも、仕様が指定した意味以外では使えません。この領域は仕様のスキーマファイルで一元管理され、定義済みのコードは3つです。
MCPが定義する3つのエラーコード
| コード | 名前 | 発生条件 |
|---|---|---|
-32020 | 名前HeaderMismatch | 発生条件HTTPヘッダーの値がリクエストボディの値と一致しない、または必須ヘッダーが欠落・不正 |
-32021 | 名前MissingRequiredClientCapability | 発生条件サーバーが要求する機能をクライアントがclientCapabilitiesで宣言していない |
-32022 | 名前UnsupportedProtocolVersion | 発生条件リクエストが指定するプロトコルバージョンをサーバーがサポートしていない |
いずれもHTTP経由では応答ステータスを400 Bad Requestにすることが仕様で定められています。HeaderMismatchErrorはHTTPのヘッダーとボディの不整合を検出したときに返り、たとえばMcp-Nameヘッダーの値がボディの値と食い違うと次のようになります。
{
"jsonrpc": "2.0",
"id": 1,
"error": {
"code": -32020,
"message": "Header mismatch: Mcp-Name header value 'foo' does not match body value 'bar'"
}
}HeaderMismatchが存在する理由はセキュリティです。ロードバランサーがHTTPヘッダーの値でルーティングを決め、MCPサーバー自身はリクエストボディの値で処理する。経路上の異なるコンポーネントがこう異なる値を信頼していると、両者の食い違いを突いた攻撃が成立します。Streamable HTTPのサーバー検証規則は、次のいずれかを検出したら必ずリクエストを拒否することを求めています。
- 必須ヘッダー(
MCP-Protocol-Version・Mcp-Method・Mcp-Name)のいずれかが欠落している - ヘッダーの値がボディの値と一致しない
- ヘッダーに不正な文字が含まれる
整数値の比較は文字列ではなく数値で行うのが望ましく(SHOULD)、42.0と42は同じ値です。Mcp-NameやMcp-Param-{Name}にBase64の退避表記が使われていれば、デコードしてからボディと比べます。
クライアント側の復旧手順も仕様にあります。Mcp-Param-*ヘッダーの欠落や不一致で-32020を受け取ったら、tools/listでinputSchemaの変更を確かめ、適切なヘッダーを付けて元のリクエストを再送します。ロードバランサーなどの中継役は、HTTPのエラーステータスさえ返せばよく、JSON-RPCのエラー本文までは求められていません。独自のゲートウェイを置く構成では、-32020の本文がクライアントに届くとは限りません。
MissingRequiredClientCapabilityErrorはdataフィールドにrequiredCapabilitiesを含めることが必須です。たとえばサーバーがelicitation機能を要求しているのにクライアントが宣言していない場合、応答は次のようになります。
{
"jsonrpc": "2.0",
"id": 1,
"error": {
"code": -32021,
"message": "Server requires the elicitation capability for this request",
"data": {
"requiredCapabilities": { "elicitation": {} }
}
}
}UnsupportedProtocolVersionErrorも同様にdata.supported(サーバーが対応する版の配列)とdata.requested(拒否された版)を必ず含みます。クライアントはこの配列から相互にサポートされる版を選び直してリトライする設計です。バージョン交渉の全体像はMCPのバージョニングと後方互換性にまとめています。
ツールの失敗はエラーコードで返すのか、結果で返すのか
独自コードを考える前に、そもそもJSON-RPCのエラーにすべき失敗かを見ます。ツール呼び出しの仕様は、失敗の報告経路を2つに分けています。
ツール呼び出しの2つの失敗経路
プロトコルエラー
未知のツール名、CallToolRequestのスキーマを満たさない不正なリクエスト、サーバー側の障害がここに入ります。モデルが自力で直しにくい失敗という位置づけで、クライアントはモデルに渡してもよい(MAY)とされます。
ツール実行エラー
外部APIの失敗、入力値の検証エラー(日付の形式違い・範囲外の値)、業務ロジック上のエラーが入ります。isError: trueを付けた結果として返し、クライアントはモデルに渡すべき(SHOULD)とされます。モデルが引数を直して再試行できるようにするためです。
ツール実行エラーの例は、仕様では次の形です。errorではなくresultの中に、モデルが読める文章を入れます。
{
"jsonrpc": "2.0",
"id": 4,
"result": {
"resultType": "complete",
"content": [
{
"type": "text",
"text": "Invalid departure date: must be in the future. Current date is 08/08/2025."
}
],
"isError": true
}
}この分類に従うと、ツールの中で起きる外部APIの失敗や入力値の不備は、そもそもerror.codeを割り当てる対象ではありません。独自コードが必要になる場面は、JSON-RPCのエラーとして返したいプロトコル層の失敗だけに絞られます。たとえばツール名が未知のときに返るのは-32602で、これもプロトコルエラーの側です。
判断の流れは次のとおりです。
失敗を返す層の選び方
- 1
JSON-RPCの形が壊れているか
JSONが壊れている、
jsonrpcやmethodが欠けているといった失敗は、標準5コードの-32700〜-32603で返します。 - 2
MCP仕様が定義した失敗か
ヘッダーとボディの食い違い、クライアント機能の不足、プロトコルバージョン非対応のいずれかなら、
-32020〜-32022をそのまま使います。 - 3
ツールの中身が失敗したか
リクエストは正しく届き、ツールの処理で失敗したなら、
isError: trueの結果で返します。 - 4
それでも独自コードが要るか
プロトコル層で仕様に無い失敗を区別したいときだけ、
-32768〜-32000の外側に置くのが推奨です。
自作サーバーが新しいエラーコードを割り当てるときの基準
MCP予約領域にコードを追加できるのは仕様そのものだけです。この領域は仕様のスキーマに記録されたコードだけが有効で、実装ごとに勝手に追記できる場所ではありません。では自作サーバーが独自のエラー(認証切れ、レート制限超過など)を区別したい場合はどうするか。仕様の推奨は明快です。JSON-RPCの予約範囲そのものを避け、-32768から-32000の外側に置くことが推奨(SHOULD)されています。この範囲外は「アプリケーション定義のエラー」に開放された領域で、実装がここに好きな値を割り当てることを妨げる規則はありません。
見落としやすいのはローカルエラーの扱いです。SDK内部で発生するリクエストタイムアウトのような、通信相手に届く前に自分の実装内で完結する失敗には、現行仕様はコードを割り当てていません。JSON-RPC形式でこうしたローカルエラーを表現する場合、仕様は「相手から受信したエラーと混同されない形にすべき」とだけ求めています。具体的な標準コードは将来のMCP予約領域の拡張に委ねられており、今それらしいコードを自分で決め打ちすると、後から仕様が同じコードを別の意味で定義したときに衝突します。
自作したエラーコードをデバッグする際は、実際にサーバーが返すJSON-RPC応答をそのまま確認できるMCP Inspectorが手早い手段です。接続そのものが失敗している場合の切り分けはMCPサーバーに接続できないときの切り分け手順を参照してください。
この区分は何を優先した設計か
エラーコードの空間を区分した設計は、後方互換と将来拡張の両立を優先しています。レガシー領域を凍結して意味の再解釈を禁じたのは、旧サーバーが返す値をクライアントが誤読しないための保険です。一方でMCP予約領域を仕様の専有物にしたのは、実装がばらばらに数字を割り当てて衝突する事態を防ぐためです。
裏を返せば、この設計はMCP仕様の管理者だけがエラーコードの語彙を増やせる体制を選んだということです。実装者が独自の失敗理由を細かく区別したいなら、コード自体ではなくerror.dataに構造化情報を積む方が仕様の意図に沿います。「不明なツール名」も「存在しないリソース」も「期限切れカーソル」も同じ-32602で表しているのは、この方針の表れです。コードは粗く、詳細はdataで伝える。この非対称な設計は、MCPサーバーを自作する際のエラー設計にもそのまま応用できます。実装全体の設計はMCPサーバー自作ガイド、接続後の権限や信頼境界はMCPセキュリティガイドで扱っています。
まとめ
自作サーバーで先に決めるのは、その失敗をJSON-RPCのエラーで返すか、isErrorの結果で返すかです。ツールの中身の失敗は後者で足り、独自コードが必要なプロトコル層の失敗だけを-32768〜-32000の外側に置きます。-32000〜-32099の中は、新しい番号を自作しない領域です。プロトコル全体の入り口はMCPとはで解説しています。