Apigee での MCP デプロイのトラブルシューティング

このページは Apigee と Apigee ハイブリッドに適用されます。

Apigee Edge のドキュメントを表示する

このページでは、MCP 検出プロキシのデプロイに関する問題のトラブルシューティングと解決方法について説明します。このページでは、MCP デプロイの非同期プロビジョニング ライフサイクルを理解し、エラーコードを解決して、ランタイム接続を確認できます。

MCP のデプロイについて

MCP Discovery Proxy のデプロイは、非同期のマルチステップ プロセスです。これにより、プロキシのデプロイが完了する前に、構成が Apigee と Google Cloud インフラストラクチャ全体に完全に伝播されます。

これらのダウンストリーム プロビジョニング手順が実行されている間、UI にはプロキシがプロビジョニング ステータスで表示されます。プロセスが完了すると、ステータスが [デプロイ済み] に変わります。このステータスは、すべてのダウンストリーム コンポーネントが完全にプロビジョニングされ、プロキシがトラフィックを処理する準備が整っていることを確認します。

トラブルシューティング

以降のセクションでは、Apigee で MCP を使用する際に発生する可能性のあるエラーと既知の問題について説明します。

問題: バックエンドは、1 つのリクエストの場所(クエリ文字列、ヘッダー、Cookie)からツール引数を読み取りますが、OpenAPI 仕様ではパラメータ in が別の場所で宣言されているため、引数はバックエンド リクエストの別の場所に到着します。呼び出しが失敗するか、引数が存在しないかのように動作します。

症状: tools/call リクエストは MCP レイヤで成功し、MCP クライアントは引数を提供しますが、バックエンドは値が欠落しているかのように応答します。たとえば、必須値の 400 や、送信された値の代わりにデフォルトが適用されます。

原因: OpenAPI 仕様でパラメータが in: header または in: cookie として宣言されているため、Apigee はその値をクエリ文字列ではなく、リクエスト ヘッダーまたは Cookie ヘッダーで送信します。クエリ文字列からのみ値を読み取るバックエンドには、この値は表示されません。バックエンド リクエストでのツール引数の配置をご覧ください。

解決策: 値がどこに属するかを決定し、仕様とバックエンドを一致させます。

  • クエリ文字列の値を保持するには、パラメータ in: query を宣言します。
  • ヘッダーまたは Cookie として送信するには、宣言をそのままにして、バックエンドのその場所から値を読み取ります。

問題: MCP クライアントが行う HTTP リクエストで受信される Cookie(ブラウザベースのクライアントからのセッション Cookie など)が、一部のツール呼び出しでバックエンドに到達しなくなります。

症状: バックエンドは、呼び出し元が認証されていないか、セッションにないかのように動作し、動作に一貫性がありません。同じオペレーションが一部の呼び出しで機能し、他の呼び出しでは機能しません。

原因: オペレーションで 1 つ以上の in: cookie パラメータが宣言されており、影響を受ける呼び出しで少なくとも 1 つのパラメータが指定されています。Cookie ヘッダーは、追加されるのではなく、全体として書き換えられます。そのため、これらの呼び出しでは、宣言された Cookie パラメータのみが渡され、他のすべての Cookie は破棄されます。Cookie パラメータを指定しなかった呼び出しは、ヘッダーを変更せずに渡すため、一部の呼び出しのみが失敗します。バックエンドへのリクエストの Cookie をご覧ください。

解決策: バックエンドが必要とするすべての Cookie を、それを必要とする各オペレーションの in: cookie パラメータとして宣言します。これにより、その値はクライアントのリクエストから継承されるのではなく、常にツール呼び出しによって提供されます。宣言されていない Cookie を保持する設定はありません。

ツール呼び出しとメタデータのエラー レスポンス ステータス コード

Apigee MCP エンドポイントは、Model Context Protocol(MCP)仕様と関連する OAuth 標準に準拠した HTTP ステータス コードを返します。この動作を理解することで、MCP クライアントまたは AI エージェントでエラーを正しく処理できます。

次の表に、Apigee MCP エンドポイントから返されるステータス コードの概要を示します。

シナリオ HTTP ステータス コード レスポンスの本文
tools/call でツールの実行が失敗する(たとえば、バックエンド API が 4xx または 5xx エラーを返す。検証エラーや必須フィールドの欠落など)。 200 OK result.isError が true に設定され、エラーの詳細が content に設定された CallToolResult。これにより、MCP クライアントと LLM エージェントはエラーを読み取って復元できます(たとえば、不足している入力を再プロンプトして再試行するなど)。セッションは終了しません。
OAuth Protected Resource Metadata(PRM)が構成されていないか、/.well-known/oauth-protected-resource/mcp に見つかりません。 404 Not Found リクエストされたロケーションで保護されたリソースのメタデータが利用できないことを示します。
アクセス トークンがない、無効になっている、または期限が切れています。 401 Unauthorized OAuth ディスカバリ フローを駆動する WWW-Authenticate チャレンジが含まれています。
アクセス トークンのスコープが不十分です。 403 Forbidden ステップアップ認証フローを推進するための WWW-Authenticate チャレンジが含まれています。

例: ツール実行エラー レスポンスの本文

ツール呼び出しが失敗すると、Apigee MCP は HTTP 200 OK を返し、JSON-RPC result オブジェクトで失敗を報告します。isError は true に設定されます。バックエンドから返された元のエラーは保持されます。バックエンド レスポンスの本文は、result.content[].text にそのまま(文字列として)配置されます。たとえば、バックエンドが HTTP 404 と本文 {"error":"User 42 not found"} を返す場合、MCP レスポンスは次のようになります。

{
  "jsonrpc": "2.0",
  "id": 1,
  "result": {
    "content": [
      {
        "type": "text",
        "text": "{\"error\":\"User 42 not found\"}"
      }
    ],
    "isError": true
  }
}

id は、tools/call リクエストの id と一致します。バックエンドがレスポンス本文を返さない場合、text には生成されたサマリー(Operation failed (HTTP 500). など)が含まれます。

MCP クライアントは、HTTP ステータス コードではなく result.isError を検査して、ツールの呼び出しの失敗を検出する必要があります。クライアント コードが、ツール実行エラーを通知する 4xx または 5xx HTTP ステータスを特に想定している場合、エラーは検出されません。レスポンスが HTTP 200 OK であるためです。このようなクライアントを更新して、result.isError と content 配列を読み取るようにします。

プロトコル レベルのエラー(形式が正しくない JSON-RPC リクエストなど)は、ツール実行エラーとは異なります。これらのエラーは、result.isError ではなく、JSON-RPC error オブジェクト({"jsonrpc": "2.0", "error": {"code": -32700, ...}} など)で引き続き返されます。

推奨事項: 標準に準拠した MCP クライアントは、デフォルトのロジックでこれらのステータス コードを処理するため、変更は必要ありません。以前に、標準以外のステータス コードを処理するためにクライアントサイドの回避策を追加した場合(tools/call エラーの 2xx 以外のレスポンス本文を解析する、PRM エンドポイントからの 500 レスポンスを「メタデータが構成されていない」として扱うなど)、その回避策を削除することをおすすめします。そうしないと、クライアントは修正されたステータス コードを正常に処理する代わりに、古い処理ロジックを引き続き使用する可能性があります。

ブラウザベースの MCP 呼び出しの CORS ポリシー

Apigee MCP 呼び出しがブラウザから直接行われる場合(たとえば、MCP インスペクタが「Via Proxy」ではなく「Direct」設定を使用する場合)、クロスオリジン リソース シェアリング(CORS)ポリシーが必要です。MCP Discovery Proxy テンプレートには、ブラウザベースの呼び出しを有効にするために、デフォルトでプロキシ バンドルのプロキシ エンドポイントのリクエスト PreFlow に Apigee CORS ポリシーが含まれています。

ブラウザから直接呼び出しを行うときに CORS 関連の問題が発生した場合は、MCP Discovery プロキシ バンドルのプロキシ エンドポイントのリクエスト PreFlow で CORS ポリシーの設定を調整する必要があります。

MCP エンドポイントにリクエストを送信する際の JSON 解析エラー

MCP エンドポイントにリクエストを送信すると、次のようなエラー メッセージ(または別のエラーコードとメッセージ)が表示されることがあります。

{
  "error": {
    "code": -32700,
    "message": "JSON parse error"
  },
  "id": null,
  "jsonrpc": "2.0"
}

その場合は、次の情報を確認することをおすすめします。

  • 正しい MCP エンドポイント URL を入力している。
  • リクエストの形式が正しい。
  • サポートされているメソッドにアクセスしている。
  • サポート対象の地域で MCP Discovery Proxy を有効にして構成している。容量制限があるリージョンの一覧については、デプロイの失敗をご覧ください。

デプロイに失敗した

MCP Discovery Proxy のデプロイが失敗し、UI に「失敗」ステータスが表示された場合は、エラー メッセージで詳細を確認します。一般的な障害には、次のようなものがあります。

  • ネットワーク構成に失敗しましたまたは OAS でサポートされていないスキーマ: これらのエラーは通常、OpenAPI 仕様に問題があることを示しています。仕様が有効であり、サポートされているバージョンを使用していることを確認してください。
  • リージョンの容量制限(Apigee のみ): ロードバランサのプロビジョニングの失敗に関連するエラーが表示される場合や、プロビジョニング ステータスが [デプロイ済み] に変更されない場合は、次のいずれかのリージョンで一時的なインフラストラクチャ容量制限が発生している可能性があります。
    • asia-east2
    • asia-northeast3
    • asia-southeast2
    • australia-southeast1
    • europe-central2
    • europe-west12
    • europe-west9
    • me-central2
    • us-central2

    このエラーを解決するには、別のリージョンの環境にプロキシをデプロイしてみてください。この障害モードは、ランタイム インフラストラクチャが独自のクラスタである Apigee ハイブリッドには適用されません。

  • クラスタで MCP が有効になっていない(Apigee ハイブリッドのみ): MCP プロキシのデプロイがプロビジョニング ステータスのまま無期限に続き、デプロイ済みにならない場合は、クラスタで MCP が有効になっていることを確認します。以下のクラスタ側の診断をご覧ください。

クラスタ側の診断(Apigee ハイブリッドのみ)

Apigee ハイブリッド MCP データプレーンは独自の Kubernetes クラスタ内で実行されるため、クラスタ内の障害モードは、クラスタに対して kubectl を使用して診断されます。次の診断チェックリストを順番に実施します。

ステップ 1: クラスタで MCP が有効になっていることを確認する

MCP は、バニラ Kubernetes Deployment ではなく、ApigeeDeployment カスタム リソース(Apigee オペレーターによって管理)としてデプロイされます。MCP ApigeeDeployment を確認します。

kubectl -n APIGEE_NAMESPACE get apigeedeployment -l app=apigee-mcp-server

想定される出力(リソース名には、組織から派生した接尾辞が含まれます。ここでは ORG_CR_SUFFIX と表示されます)。

NAME                                     STATE     NESTEDSTATE   AGE
apigee-mcp-server-default-ORG_CR_SUFFIX       running                 1h

コマンドが No resources found in APIGEE_NAMESPACE namespace. を返した場合、MCP は有効になっていません。Apigee ハイブリッドで MCP を有効にするに戻り、enableMcpServer: true が overrides.yaml の最上位キーとして設定されていること、apigee-org リリースの前に apigee-operator Helm リリースをアップグレードしたことを確認します。正しい helm コマンドについては、有効化手順のステップ 3 とステップ 4 をご覧ください。

ステップ 2: MCP Pod が実行されていることを確認する

kubectl -n APIGEE_NAMESPACE get pods -l app=apigee-mcp-server

想定される出力(Pod のサフィックスは異なります):

NAME                                                              READY   STATUS    RESTARTS   AGE
apigee-mcp-server-default-ORG_CR_SUFFIX-1170-abcd1234   2/2     Running   0          1h
apigee-mcp-server-default-ORG_CR_SUFFIX-1170-efgh5678   2/2     Running   0          1h

Pod 名は apigee-mcp-server-default-ORG_CR_SUFFIX-REV-POD_HASH テンプレートに従います。ここで、REV は 4 桁のハイブリッド リリース リビジョン(ハイブリッド 1.17.0 の場合は 1170 など)で、POD_HASH は 8 文字の英数字の接尾辞で、Pod ごとに異なります。

すべての Pod の READY 列に 2/2、STATUS 列に Running が表示されている必要があります。Pod が Running でない場合、kubectl -n APIGEE_NAMESPACE describe pod POD_NAME に理由が表示されます。一般的な原因は、イメージの pull の失敗(クラスタから overrides.yaml で構成されたイメージ レジストリへの下り(外向き)を確認する)と、CPU またはメモリのヘッドルームの不足(overrides.yaml の mcpServer.resources.requests を増やす)です。

ステップ 3: サイドカー構成の pull ステータスを確認する

各 MCP Pod の apigee-mcp-server-config サイドカー コンテナは、Apigee コントロール プレーンから MCP 構成を定期的に pull します。各 pull では、apigee.googleapis.com から現在の構成参照を取得し、参照された構成バンドルを Cloud Storage から取得します。有効な構成で pull が正常に完了すると、MCP データプレーンは https://mcp.apigee.internal/mcp で MCP ツール トラフィックの処理を開始します。

有効な構成で最初の pull が成功する前(組織に MCP Discovery Proxy がまだデプロイされていないか、サイドカーがコントロール プレーンに到達できないため)、MCP Pod は Ready のままですが、https://mcp.apigee.internal/mcp へのリクエストは「接続が拒否されました」で失敗します。

サイドカーのログを調べて、どちらのケースに該当するかを判断します。

kubectl -n APIGEE_NAMESPACE logs -l app=apigee-mcp-server -c apigee-mcp-server-config --tail=100
  • コントロール プレーンから MCP 構成がないことを示す info 行が繰り返される。
    {"level":"info","ts":1787582542.68,"caller":"mcpsidecar/sidecar.go:397","msg":"mcpsidecar: no MCP config from CP yet; skipping tick, pod stays Ready via seed","org":"YOUR_ORG","mcp_server_id":"default"}
    Apigee コントロール プレーンから成功のレスポンスが返されましたが、組織に MCP Discovery Proxy がまだデプロイされていません。これは、オンボーディング中の想定される定常状態です。MCP データプレーンをサービス提供状態に移行するには、MCP クイックスタートに沿って MCP Discovery Proxy をデプロイします。
  • consecutive_failures カウンタを含む error 行:
    {"level":"error","ts":1787613247.74,"caller":"mcpsidecar/sidecar.go:...","msg":"mcpsidecar: ...","org":"YOUR_ORG","mcp_server_id":"default","consecutive_failures":185,"error":"<wrapped error text>"}
    サイドカー ログは構造化 JSON です。"level":"error" で consecutive_failures カウンタが増加している場合、サイドカーは構成の pull を完了できませんでした。サイドカーはバックオフを使用して自動的に再試行し、次の pull が成功するとカウンタを 0 にリセットします。

    このカウンタが増加し続ける場合に確認すべき一般的な根本原因は次のとおりです。

    • apigee-watcher Google Cloud サービス アカウントが、Apigee 組織の controlPlaneAccess リソースの watcher_identities リストにありません。有効化手順のステップ 2: ウォッチャー ID にコントロール プレーンの MCP 構成へのアクセス権を付与するを参照して、権限を付与し、付与されたことを確認します。
    • クラスタから apigee.googleapis.com または storage.googleapis.com への下り(外向き)がブロックされている。両方のエンドポイントが MCP Pod から到達可能である必要があります(有効化手順のアウトバウンド ネットワーク要件をご覧ください)。
    • apigee-watcher サービス アカウントは、このクラスタから Google Cloud アクセス トークンを取得できません。同じ Namespace 内の既存の Apigee ハイブリッド コンポーネントでも認証エラーが報告されている場合は、まず共有認証の問題に対処します。
    • 一時的な Apigee コントロール プレーンまたは Cloud Storage のエラー。上記の原因を除外してもカウンタが長時間上昇し続ける場合は、Apigee サポートにお問い合わせください。

ステップ 4: Message Processor の hostAliases が挿入されたことを確認する

MCP を有効にすると、Apigee オペレーターは 1 つの hostAliases エントリを Message Processor(MP)Pod 仕様に挿入します。このエントリには、1 つの IP(MCP Service ClusterIP)と 2 つのホスト名(mcp.apigee.internal と小文字の ORG_NAME.mcp.apigee.internal)が含まれます。挿入が成功したことを確認します。

kubectl -n APIGEE_NAMESPACE get pods -l app=apigee-runtime \
  -o jsonpath='{range .items[*]}{.metadata.name}{"\t"}{.spec.hostAliases}{"\n"}{end}'

リストされているすべての MP Pod には、mcp.apigee.internal と、MCP Service ClusterIP を指す小文字の ORG_NAME.mcp.apigee.internal の両方を含む hostAliases エントリが必要です。空のエントリまたは欠落しているエントリを示す MP Pod は、MCP が有効になる前に作成されています。影響を受ける MP Pod を削除して、ApigeeDeployment コントローラが現在の Pod 仕様(挿入された hostAliases エントリを含む)から再作成するようにします。

kubectl -n APIGEE_NAMESPACE delete pod -l app=apigee-runtime

ステップ 5: エンドツーエンドの検証

MCP データプレーンを介して実際の MCP initialize リクエストを送信するエンドツーエンド テストの場合は、MCP Discovery Proxy をデプロイし、MCP クイックスタートの手順に沿って操作します。組織に MCP Discovery Proxy がデプロイされる前に、Message Processor が MCP Pod に到達できるかどうかにかかわらず、https://mcp.apigee.internal/mcp へのリクエストは接続拒否で失敗します(ステップ 3 を参照)。

サポートケース

Apigee ハイブリッド MCP データプレーンに関連するサポートケースを登録する場合は、診断を迅速に行うために、次の出力を添付してください。

  • kubectl -n APIGEE_NAMESPACE logs -l app=apigee-mcp-server --all-containers --tail=1000
  • kubectl -n APIGEE_NAMESPACE describe pod -l app=apigee-mcp-server
  • kubectl -n APIGEE_NAMESPACE get pods -l app=apigee-mcp-server -o wide および kubectl -n APIGEE_NAMESPACE get svc -l app=apigee-mcp-server。
  • 最近の overrides.yaml と helm history APIGEE_ORG_RELEASE_NAME -n APIGEE_NAMESPACE の出力(APIGEE_ORG_RELEASE_NAME は apigee-org チャートの Helm リリース名。helm list -n APIGEE_NAMESPACE | grep apigee-org で確認できます)。overrides.yaml を共有する前に、次のようなシークレット マテリアルを削除します。
    • gcp.workloadIdentity、envs[].serviceAccountPaths、mcpServer.serviceAccountPath のインライン サービス アカウント キー。
    • virtualhosts の TLS 証明書または秘密鍵。
    • cassandra.auth のパスワード フィールド。
    • オーバーライドがオペレーターに渡すインライン認証情報。
  • 問題を再現する curl コマンドとレスポンスの全文。

API Hub で API スタイルを MCP に変更できません

既存の API オペレーションを含む API リソースが Apigee API ハブにすでに存在する場合、そのリソースの API スタイル属性を MCP に変更することはできません。Apigee API ハブに API を MCP API として登録するには、API を最初に登録するときに MCP スタイルを選択するか、スタイルを MCP に変更する前に API リソースにオペレーションが存在しないことを確認する必要があります。

その他の問題が発生した場合は、デバッグの使用で、 Google Cloud コンソールのデバッグツールを使用して MCP Discovery Proxy との間で送受信されるリクエストとレスポンスを分析する方法の詳細をご覧ください。