Claude Media
MCPのOAuth Client Credentials拡張 — 人間不在の認証

MCPのOAuth Client Credentials拡張 — 人間不在の認証

ブラウザリダイレクトなしでMCPサーバーに接続するOAuth Client Credentials拡張の仕組みと、JWT Bearer Assertionとクライアントシークレットの使い分け。

CI/CDパイプラインやバックグラウンドサービスのように、ブラウザで人間がログインできない場面でMCPサーバーに接続するための拡張がOAuth Client Credentialsです。拡張識別子はio.modelcontextprotocol/oauth-client-credentials。OAuth 2.0のclient credentialsフロー(RFC 6749セクション4.4)をMCPに追加し、アプリケーション自身の資格情報だけでアクセストークンを取得できるようにします。

標準のMCP認可フローが機械間通信に向かない理由

標準のMCP認可フローは、ブラウザが開いてユーザーがログインし、mix-up攻撃対策などのセキュリティ要件を満たしながら権限を承認するという手順を前提にしています。人間が操作する対話的な利用には向いていますが、そこにユーザーが存在しない場面では成立しません。

OAuth Client Credentialsは、委任されたユーザー資格情報の代わりに、アプリケーション自身の資格情報(クライアントIDとシークレット、または署名付きJWTアサーション)で認証する方式に置き換えます。クライアントは認可サーバーに対して自分の身元を直接証明し、ブラウザのリダイレクトもユーザー操作も介さずにアクセストークンを受け取ります。

どんな場面で使うべきか

公式ドキュメントは、次の4つのユースケースを想定しています。

  • バックグラウンドサービスが、ユーザー不在のままスケジュールやイベントに応じてMCPツールを呼び出す
  • CI/CDパイプラインが、自動化されたビルド・テスト・デプロイの一部としてMCPサーバーを呼び出す
  • サーバー間連携で、エンドユーザーが介在しない2つのバックエンドシステムを接続する
  • デーモンプロセスや常駐ワーカーが、MCPリソースへの永続的なアクセスを必要とする

逆に、明示的にユーザー本人が承認すべき統合であれば、標準のMCP認可フローを使うべきだと明記されています。人間が関与するかどうかが、拡張を使うかどうかの判断基準です。

2つの資格情報形式 — JWTアサーションとクライアントシークレット

拡張は2つの資格情報形式をサポートします。

JWT Bearer Assertion(推奨)

RFC 7523で定義される方式で、クライアントが自身の秘密鍵でトークンに署名し、身元証明として提示します。認可サーバーは、クライアントが事前に登録した公開鍵でその署名を検証します。

フローは次の順で進みます。

  1. クライアントがgrant_type=urn:ietf:params:oauth:grant-type:jwt-bearerと署名済みJWT(assertion)を認可サーバーの/tokenエンドポイントへPOSTする
  2. 認可サーバーがアクセストークンを返す
  3. クライアントがそのBearerトークンを付けてMCPサーバーへリクエストする

JWTアサーションに含まれる主なクレームは次のとおりです。

クレーム内容
iss内容クライアントID(発行者)
sub内容クライアントID(認証対象)
aud内容認可サーバーのトークンエンドポイントURL
exp内容有効期限
iat内容発行時刻

クライアントシークレット

よりシンプルな構成向けに、標準的なclient_idclient_secretによるclient credentialsフローもサポートしています。クライアントがこの2つを認可サーバーのトークンエンドポイントへ直接送り、アクセストークンを受け取ります。

クライアントシークレットは、ユーザー操作なしにアクセスを許可する長期間有効な資格情報です。漏洩すると、シークレットがローテーションされるまで攻撃者がアプリケーションになりすまして静かに認証を通せてしまいます。公式ドキュメントは次の対策を挙げています。

  • シークレットはシークレットマネージャーに保管し、ソースコードやバージョン管理下の環境ファイルに書かない
  • 定期的にローテーションし、漏洩が疑われた場合は即座にローテーションする
  • 資格情報のスコープを必要最小限に絞る
  • 可能な場合はJWTアサーションを優先する(短命で、署名鍵自体を送信しない)

クライアント・サーバーが実装すべきこと

MCPクライアント側は、まずリクエストの_meta内で拡張への対応を宣言します。

{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "...",
  "params": {
    "_meta": {
      "io.modelcontextprotocol/clientCapabilities": {
        "extensions": {
          "io.modelcontextprotocol/oauth-client-credentials": {}
        }
      }
    }
  }
}

そのうえで、MCPサーバーへ接続する前にclient credentialsグラントでトークンを取得し、HTTPリクエストのAuthorizationヘッダーにBearer <access_token>として付与します。client credentialsトークンは通常、ユーザー委任トークンより有効期限が短いため、期限切れ前にトークンを再取得するリフレッシュ処理も実装が必要です。

MCPサーバー側は、リクエストごとにJWKSエンドポイント経由でJWT署名とクレームを検証し、要求される操作に必要なスコープをトークンが含んでいるか確認します。server/discoverレスポンスのcapabilities.extensionsにこの拡張を含めて対応を明示することも、発見可能性の観点から推奨されています。

SDKでの実装 — TypeScriptとPythonの例

公式MCP SDKはclient credentials認証を組み込みでサポートしており、トークンの取得と更新を自動化します。TypeScript SDKはnpm install @modelcontextprotocol/client、Python SDKはpip install mcpで導入します。

npm install @modelcontextprotocol/client

クライアントシークレットを使う場合、TypeScriptではClientCredentialsProviderにクライアントIDとシークレットを渡し、StreamableHTTPClientTransportauthProviderとして設定するだけでトークンの取得と付与が自動化されます。JWT秘密鍵を使う場合はPrivateKeyJwtProviderに切り替え、署名アルゴリズムと秘密鍵を渡す構成に変わります。Python側も同様に、ClientCredentialsOAuthProviderPrivateKeyJWTOAuthProviderがそれぞれの資格情報形式に対応します。

いずれの言語でも、トークンの永続化はアプリケーション側のストレージ実装に委ねられ、SDKはトークンの取得・付与・失効判定のロジックだけを担う設計です。公式サンプルはメモリ上に保持するInMemoryTokenStorageを使っていますが、これはあくまでサンプル実装です。

デーモンプロセスのように長時間稼働するワーカーでは、プロセス再起動のたびに毎回トークンを取り直す非効率を避けるため、Redisやシークレットマネージャーのような永続ストアにトークンを保存する実装へ差し替えるのが実運用での定石です。scopesパラメータで要求するスコープを絞り込めるため、そのワーカーが実際に呼び出すMCPツールに必要な最小限のスコープだけを指定しておくと、トークン漏洩時の被害範囲も限定できます。

そもそもMCPの「拡張」とは何を指すか

OAuth Client Credentialsは、MCPのコア仕様ではなく「拡張(extension)」という別枠の仕組みで提供されています。拡張は{vendor-prefix}/{extension-name}という形式の識別子を持ち、公式拡張はio.modelcontextprotocolというベンダープレフィックスを使います。

拡張が公式として扱われるには、SEPプロセス(Extensions Track)を通す必要があります。SEPを提出し、公式SDKのいずれかで最低1つのリファレンス実装を作り、Core Maintainerがレビューして承認して初めて拡張リポジトリに取り込まれます。拡張には必ず対応するWorking GroupかInterest Groupが紐付いていなければならないという要件もあり、認可系の拡張はまとめてAuthorization Interest Groupが窓口になっています。

拡張は既定で無効(disabled by default)です。SDKが拡張への対応を実装するかどうかもSDKメンテナーの裁量に委ねられており、MCPプロトコルへの準拠自体には拡張実装は必須ではありません。また拡張はコア仕様とは独立して進化し、更新のたびにCore Maintainerのレビューを経る必要はありません。変更が必要なときは、新しい識別子を作るより、capability flagか拡張設定オブジェクト内のバージョニングで対応するのが推奨されています。後方互換性のない変更が避けられない場合に限り、新しい識別子(io.modelcontextprotocol/oauth-client-credentials-v2のような形)を割り当てます。

対応クライアントはまだ限定的

拡張はオプトインで、クライアントとサーバーの双方が対応を宣言しない限り有効になりません。公式のExtension Support Matrixを見ると、Claude(web)・Claude Desktop・VS Code GitHub Copilot・Cursor・ChatGPT・Archestra.AIを含む主要クライアントのいずれにも、OAuth Client Credentialsへの対応チェックは付いていません。仕様は固まっていますが、クライアント実装が追いついていない段階です。

自前のバックグラウンドサービスやCI/CDパイプラインからMCPサーバーを呼び出す構成では、多くの場合クライアント側を自作することになります。その場合は前節のSDK(ClientCredentialsProviderClientCredentialsOAuthProvider)を使えば、汎用クライアントの対応を待たずに導入できます。

企業のIdPで社員のアクセス自体を一元管理したい場合は、この拡張とは別物のEnterprise-Managed Authorization拡張が対象になります。人間がログインする前提のIdP連携について詳しくはMCPセキュリティガイド、リモートMCPの標準的なOAuth認可フローとの違いはリモートMCPのOAuth認証で扱っています。

まとめ

OAuth Client Credentials拡張は、人間のログインを前提とする標準のMCP認可フローに対して、アプリケーション自身の資格情報でアクセストークンを取得する経路を追加します。CI/CDパイプラインやバックグラウンドサービスのように、ユーザーが介在しない機械間通信がユースケースの中心です。

資格情報の形式はJWT Bearer Assertionとクライアントシークレットの2つがあり、公式は短命で秘密鍵を送信しないJWTアサーションを推奨しています。クライアントシークレットを使う場合は、長期間有効な資格情報である以上、シークレットマネージャーでの保管とローテーションが欠かせません。対応クライアントはまだ乏しく、独自実装で先行して導入するチームがSDKのClientCredentialsProvider系のクラスを使う構成が当面の現実的な選択肢になります。

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