目次

  1. なぜ今、実装品質が問われるのか
  2. Step 1: 認証 — OAuth 2.1 + PKCE + Resource Indicators
  3. Step 2: レート制限 — per-client / per-tool の2層設計
  4. Step 3: エラーハンドリング — エージェントが自律回復できる設計
  5. Step 4: サーキットブレーカー — 外部依存障害の伝播を防ぐ
  6. AEOスコアを上げる実装チェックリスト
データ出典

本ガイドの推奨事項は、MCP公式仕様(modelcontextprotocol.io)、KanseiLink MCPサーバーの実運用データ(225サービス、総呼び出し数1,000回以上)、およびAstrix Security・cdata.com・octopus.comの2026年調査レポートに基づきます。

なぜ今、実装品質が問われるのか

2024〜2025年の「MCPサーバーを動かす」フェーズは終わった。2026年のエージェント産業が問うのは、「本番環境で、長時間、大量のリクエストに耐えて動き続けるか」だ。

KanseiLinkが収集する実運用データは、この現実を数字で示している。上位AAAグレードのサービスは90〜94%の成功率を記録する一方、API onlyの下位サービスでは有意な呼び出しデータが蓄積されない。エージェントは動かないサービスを学習し、使わなくなる。これがAEOスコアの本質だ。

本ガイドでは、KanseiLinkの実運用で観察された失敗パターンを出発点に、日本SaaS開発者が今日から実装できる具体的なパターンを提供する。

Step 1: 認証 — OAuth 2.1 + PKCE + Resource Indicators

2026年4月に更新されたMCP仕様により、公開リモートMCPサーバーへのOAuth 2.1 + RFC 8707(Resource Indicators)の適用が必須となった。以下の3つの変更が実装に影響する。

廃止

Implicit Flow(暗黙的フロー)

OAuth 2.0の暗黙的フローはMCP仕様から除外。フラグメントでアクセストークンを返す実装は非準拠となる。

必須

PKCE(Proof Key for Code Exchange)

認可コードフローにはPKCEが必須。code_verifier / code_challengeの生成・検証を実装すること。

新規必須

Resource Indicators(RFC 8707)

アクセストークンに対象リソース(MCPサーバーのURI)を埋め込む。トークン漏洩時の横展開を技術的に防止。

M2M対応

client_credentials フロー

人間ユーザー不在の自律エージェント向け。バッチ処理・夜間自動実行に必要な機械間認証を正式サポート。

トークン有効期限の設計:日本SaaSの最大の落とし穴

KanseiLinkのAgent Voiceデータで最も多く報告されるフラストレーションは「トークンが24時間で期限切れになる」問題だ。freeeの Claude Agentは「24時間有効期限が最大の障壁。夜間バッチ処理でトークンが静かに期限切れになり、部分完了の復旧が困難」と報告する。

1

短命アクセストークン + ローテーション付きリフレッシュトークン

アクセストークンの有効期限は15〜60分に設定する。24時間は長すぎる。リフレッシュトークンは使用のたびにローテーション(古いトークンを無効化)し、漏洩時の被害窓口を最小化する。

複数のエージェントインスタンスが同一認証情報を共有する場合、リフレッシュリクエストの競合状態(race condition)を防ぐためにRedisなどのアトミックロックを使用する。

2

per-toolスコープの設計

エージェントにブランケットアクセスを与えない。invoices:readinvoices:createreports:exportのようにツール単位のスコープを定義し、各リクエストでスコープを検証する。スコープの粒度が細かいほど、エージェントが意図せず実行できる操作の範囲が限定される。

実装チェック

認証サーバーがResource Indicatorsをサポートしているか確認する。Keycloak(バージョン18以降)、Auth0(Enterprise)はRFC 8707に対応。自前実装の場合はresourceパラメーターをトークンエンドポイントに追加し、発行トークンのaudienceクレームに対象MCPサーバーのURIを含める。

Step 2: レート制限 — per-client / per-tool の2層設計

レート制限のないMCPサーバーは、暴走したエージェントがAPIクォータを瞬時に消費し、外部サービスのコストを爆発させるリスクがある。KanseiLinkが追跡するサービスでも、適切なレート制限の欠如はエラー率の上昇と直結している。

2層レート制限の設計原則

Layer 1(per-client):クライアントIDまたはAPIキー単位での総リクエスト数制限。1分間に100リクエスト、1時間に2,000リクエストなど、サービスのAPIクォータに基づいて設定する。

Layer 2(per-tool):個別ツール単位の制限。削除系の危険な操作(delete_invoiceなど)は1分間に5リクエストに制限し、読み取り専用ツールには緩い制限を適用するといった差別化が可能になる。

-- PostgreSQLによるアトミックなレート制限(バースト防止) -- 単一UPDATEでチェックと更新を同時実行 → 競合状態を排除 UPDATE rate_limits SET request_count = request_count + 1, window_start = CASE WHEN NOW() - window_start > INTERVAL '1 minute' THEN NOW() ELSE window_start END, request_count = CASE WHEN NOW() - window_start > INTERVAL '1 minute' THEN 1 ELSE request_count + 1 END WHERE client_id = $1 AND tool_name = $2 RETURNING request_count, window_start, (request_count <= $3) AS allowed;

Retry-After ヘッダーの正確な返却

レート制限超過時(HTTP 429)は、Retry-Afterヘッダーで次回リクエスト可能な時刻を秒数または日時で返す。これによりエージェントが適切な待機時間を計算でき、無駄なリトライでさらにレートを消費するループを防止できる。

AEO改善効果

適切なレート制限とRetry-Afterヘッダーの実装は、KanseiLinkのAEOスコアの「エラー回復性」評価項目に直接影響する。エージェントが自律的にリトライできるサービスはAEOスコアが平均12ポイント高い傾向がある(KanseiLink内部データ)。

Step 3: エラーハンドリング — エージェントが自律回復できる設計

MCPのツールエラーはLLMのコンテキストウィンドウに注入される。つまり、エラーメッセージはエージェントへの指示書でもある。エラーメッセージの品質がエージェントの自律回復能力を決定する。

エラーの3分類

C

CLIENT_ERROR(4xx)— クライアントの問題

リトライしても意味がない。エラーメッセージには「何が間違っているか」と「どう修正するか」を具体的に示す。例:"error": "CLIENT_ERROR", "message": "invoice_date は ISO 8601 形式(YYYY-MM-DD)で指定してください。受け取った値: '2026年4月14日'", "retry_recommended": false

S

SERVER_ERROR(5xx)— サーバーの問題

指数バックオフ付きリトライを推奨する。エラーメッセージにリトライ推奨と待機時間の目安を含める。例:"error": "SERVER_ERROR", "message": "内部エラーが発生しました。30秒後にリトライしてください。", "retry_recommended": true, "retry_after_seconds": 30

E

EXTERNAL_ERROR(502/503)— 外部依存の問題

上流サービス(外部API)の障害。エラーメッセージに依存サービス名と代替アクションを示す。例:"error": "EXTERNAL_ERROR", "dependency": "freee_api", "message": "freee APIが一時的に利用不能です。5分後に再試行するか、別の会計ツールの使用を検討してください。", "retry_recommended": true

指数バックオフ + ジッターの実装

リトライ間隔は毎回同じにしてはならない。複数のエージェントが同時にリトライすると、障害中のサービスにトラフィックが集中して回復を妨げる。指数バックオフ(待機時間を倍増)にジッター(ランダムな揺れ)を加えることで、リトライのスパイクを分散させる。

// 指数バックオフ + ジッターの実装例(TypeScript) async function retryWithBackoff( fn: () => Promise<any>, maxRetries: number = 3, baseDelayMs: number = 1000 ): Promise<any> { for (let attempt = 0; attempt <= maxRetries; attempt++) { try { return await fn(); } catch (error) { if (attempt === maxRetries) throw error; // 指数バックオフ: 1s → 2s → 4s + ジッター(0〜1s) const delay = baseDelayMs * Math.pow(2, attempt) + Math.random() * 1000; await new Promise(resolve => setTimeout(resolve, delay)); } } }

Step 4: サーキットブレーカー — 外部依存障害の伝播を防ぐ

外部APIが障害中でも、MCPサーバーがリクエストを受け続けると、タイムアウトが積み重なりサーバー全体のレスポンスが低下する。サーキットブレーカーは、エラー率が閾値を超えたら新規リクエストを即座に拒否し(回路を「開く」)、回復後に自動的に通常動作に戻るパターンだ。

CB

サーキットブレーカーの3状態

Closed(正常): 全リクエストを通過させる。エラー率が閾値(例: 50%)を超えたらOpenに遷移。

Open(遮断): 上流への呼び出しをせずに即座に失敗を返す。クールダウン期間(例: 30秒)後にHalf-Openに遷移。

Half-Open(回復確認): 1リクエストを試験的に通過させる。成功したらClosedへ、失敗したらOpenに戻る。

日本SaaSでの観察パターン

KanseiLinkのデータでは、freeeのOAuth token期限切れ(auth_expired)がバースト的に発生するケースが観察されている。サーキットブレーカーをauth_expiredエラーと連動させ、連続した認証失敗を検知した場合はトークンリフレッシュを試みてからリクエストを再開するパターンが有効だ。

AEOスコアを上げる実装チェックリスト

以下のチェックリストは、KanseiLinkのAEO評価基準に直接対応している。全項目をクリアすることでAA〜AAAグレードを目指せる。

KanseiLink評価基準との対応

上記チェックリストの全項目をクリアし、実運用データで80%以上の成功率を達成したサービスがAAグレードの対象となる。90%以上かつMCP公式サーバー対応でAAAグレードを検討する。KanseiLinkへのスコア更新申請はcontact@synapse-arrows.comまで。

あなたのサービスのAEOスコアを確認する

KanseiLink MCPサーバーに接続して、競合サービスとのAEOスコア比較と実装改善ポイントを確認できます。

MCPサーバーを見る