MCP 認証の仕組み:MCP における OAuth 2.1 の動作(2026 年版ガイド)
MCP 認証を徹底解説。OAuth 2.1、保護リソースメタデータ、PKCE、Client ID メタデータドキュメント、リソースインジケーター、stdio の環境変数認証情報と API キーまで網羅します。
MCP 認証の仕組み
リモート(HTTP)MCP サーバーの認証には OAuth 2.1 が使われます。MCP サーバーは OAuth リソースサーバー、MCP クライアントは OAuth クライアントであり、別の認可サーバーがアクセストークンを発行し、クライアントはそれをすべてのリクエストで Authorization: Bearer として送信します。ローカルの stdio サーバーはこのフローを使いません。仕様上、認証情報は環境から取得します。通常は環境変数で渡す API キーです。本記事は現行の仕様リビジョン 2026-07-28 に基づき、何が変わったのか、各要素が何をするのか、公式 SDK でどう実装するのかを解説します。
stdio と HTTP:どちらの認証モデルが適用されるか?
認可仕様 は認可を任意(OPTIONAL)としたうえで、トランスポートごとに分けています。
- HTTP ベースのトランスポートは MCP 認可仕様(OAuth 2.1)に準拠すべきです(SHOULD)。
- stdio トランスポートはこれに従うべきではなく(SHOULD NOT)、環境から認証情報を取得します。
- その他のトランスポートは、そのプロトコルで確立されたセキュリティのベストプラクティスに従わなければなりません(MUST)。
| ローカル stdio サーバー | リモート HTTP サーバー | |
|---|---|---|
| 実行場所 | 自分のマシン上、クライアントの子プロセスとして | ホスト型サービス |
| 典型的な認証情報 | 環境変数の API キーやトークン | OAuth 2.1 アクセストークン |
| ログインする人 | キーを作成するときの自分 | OAuth フロー中にブラウザでログインする自分 |
| 失効方法 | プロバイダー側でキーをローテーション | 許可を取り消すか、短命トークンの期限切れを待つ |
| 仕様上のフロー | なし、環境から読み取る | 保護リソースメタデータ、ディスカバリー、PKCE |
OAuth 2.1 フローをステップごとに
1. サーバーが 401 で案内を返す
クライアントはトークンなしで MCP エンドポイントを呼び出します。サーバーは 401 を返し、WWW-Authenticate ヘッダーで保護リソースメタデータ文書(RFC 9728)の場所と、必要に応じてこのリクエストに必要なスコープを示します。
HTTP/1.1 401 Unauthorized
WWW-Authenticate: Bearer resource_metadata="https://mcp.example.com/.well-known/oauth-protected-resource",
scope="files:read"
2. クライアントが保護リソースメタデータを読む
MCP サーバーは RFC 9728 を実装しなければならず(MUST)、その文書は authorization_servers に少なくとも 1 つのエントリーを含まなければなりません(MUST)。
{
"resource": "https://mcp.example.com/mcp",
"authorization_servers": ["https://auth.example.com"],
"scopes_supported": ["mcp:tools", "mcp:resources"]
}
ヘッダーに resource_metadata がない場合、クライアントは次の順で well-known URI にフォールバックしなければなりません(MUST)。まずパス付きの URI(エンドポイントが /public/mcp なら https://example.com/.well-known/oauth-protected-resource/public/mcp)、次にルートの /.well-known/oauth-protected-resource です。
3. クライアントが認可サーバーを発見する
認可サーバーは RFC 8414 のメタデータか OpenID Connect Discovery を提供しなければならず(MUST)、クライアントは両方に対応しなければなりません(MUST)。パスのない issuer の場合、クライアントは /.well-known/oauth-authorization-server、次に /.well-known/openid-configuration を試します。https://auth.example.com/tenant1 のようにパスを含む issuer の場合は、OAuth のパス挿入 URL、OIDC のパス挿入 URL、OIDC のパス追記 URL の順に試します。返された文書の issuer は URL の構築に使った issuer と一致しなければならず、一致しない場合クライアントはそれを破棄しなければなりません(MUST)。
4. クライアントがクライアント ID を取得する
フローを始める前に、クライアントには client_id が必要です。クライアント登録のページ は 3 つの仕組みと優先順位を定めています。
| 優先度 | 仕組み | 適用される場面 |
|---|---|---|
| 1 | 事前登録 | クライアントがこの認可サーバー用のクライアント ID をすでに持っている |
| 2 | Client ID Metadata Documents(CIMD) | 認可サーバーが client_id_metadata_document_supported を公開している |
| 3 | 動的クライアント登録(RFC 7591) | サーバーが registration_endpoint を公開している場合のフォールバック |
| 4 | ユーザーに入力してもらう | 他に手段がない |
最近のリビジョンでの大きな変更点は、動的クライアント登録(DCR)が非推奨になったことです。新しい実装では CIMD を使うべきです。CIMD では client_id 自体が HTTPS の URL で、認可サーバーが取得する JSON 文書を指します。
{
"client_id": "https://app.example.com/oauth/client-metadata.json",
"client_name": "Example MCP Client",
"redirect_uris": ["http://127.0.0.1:3000/callback"],
"grant_types": ["authorization_code"],
"response_types": ["code"],
"token_endpoint_auth_method": "none"
}
URL は https でパスを含まなければならず、文書は client_id、client_name、redirect_uris を含まなければならず、client_id は URL と完全に一致しなければなりません(いずれも MUST)。CIMD のクライアント ID は認可サーバーをまたいで使えます。一方、事前登録や DCR で得た認証情報は、それを発行した issuer に紐づけなければなりません(MUST)。
5. PKCE とリソースインジケーターを伴う認可コードフロー
クライアントはブラウザで認可エンドポイントを開きます。特に重要な要件は 3 つです。
- PKCE は必須。 クライアントは技術的に可能な場合
S256を使わなければならず、サーバーメタデータのcode_challenge_methods_supportedを確認しなければなりません。これがない場合、クライアントは処理を中止しなければなりません(MUST、セキュリティ上の考慮事項)。 resourceパラメーターは必須。 クライアントは認可リクエストとトークンリクエストの両方で RFC 8707 のresourceを送信しなければなりません(MUST)。値は MCP サーバーの正規 URI(例:https://mcp.example.com/mcp、フラグメントなし、末尾スラッシュなしが望ましい)で、認可サーバーが無視する場合でも送ります。- issuer を検証する。 リダイレクト前にクライアントは期待される
issuerを記録し、コードが返ってきたらissパラメーター(RFC 9207)と比較し、一致しなければ拒否します。これにより認可サーバー間のミックスアップ攻撃を防げます。
スコープの選び方:401 チャレンジに scope があればそれを使い、なければ scopes_supported に列挙されたものを要求します。
6. トークンを付けてサーバーを呼び出す
POST /mcp HTTP/1.1
Host: mcp.example.com
Authorization: Bearer eyJhbGciOiJIUzI1NiIs...
トークンはすべてのリクエストで Authorization ヘッダーに含めなければならず(MUST)、クエリ文字列に含めてはなりません(MUST NOT)。サーバーはトークンを検証し、自サーバーをオーディエンスとして発行されたものか確認しなければなりません(MUST)。無効または期限切れのトークンには 401 を返します。
7. ツールがより多くの権限を必要とするときのステップアップ
トークンにスコープが足りない場合、サーバーは error="insufficient_scope" 付きの 403 と、その操作に必要なスコープを返すべきです(SHOULD)。クライアントは「以前に要求したスコープ」と「新たにチャレンジされたスコープ」の和集合で再認可し、限られた回数だけリトライします。
HTTP/1.1 403 Forbidden
WWW-Authenticate: Bearer error="insufficient_scope",
scope="files:write",
resource_metadata="https://mcp.example.com/.well-known/oauth-protected-resource"
サーバー作者向けのルール
- トークンを素通しさせない。 MCP サーバーは自分宛てに発行されたトークンだけを受け入れなければならず(MUST)、クライアントのトークンを上流 API に転送してはなりません(MUST NOT)。GitHub や Slack を呼ぶ場合は、別の上流トークンを取得してください。理由は MCP セキュリティのベストプラクティス を参照してください。
- 本物の認可サーバーを使う。 公式の 認可チュートリアル は、トークン検証を自前で書かないよう勧めています。チュートリアルでは Keycloak を使い、イントロスペクションでオーディエンスを検証しています。
- トークンは短命に。 認可サーバーは短命のアクセストークンを発行すべきであり(SHOULD)、パブリッククライアントに対してはリフレッシュトークンをローテーションしなければなりません(MUST)。
- すべて HTTPS で提供する。 認可エンドポイントは HTTPS でなければならず、リダイレクト URI は
localhostか HTTPS でなければなりません(MUST)。 - 認証情報をログに残さない。
Authorizationヘッダー、認可コード、トークンはログから除去してください。
公式 SDK での実装ポイント
TypeScript。 公式チュートリアルのサーバーは @modelcontextprotocol/sdk のヘルパーを使っています。mcpAuthMetadataRouter が保護リソースメタデータを公開し、requireBearerAuth があなたの検証器でトークンを強制し、getOAuthProtectedResourceMetadataUrl が WWW-Authenticate チャレンジ用の URL を生成します。
import { requireBearerAuth } from '@modelcontextprotocol/sdk/server/auth/middleware/bearerAuth.js';
import {
getOAuthProtectedResourceMetadataUrl,
mcpAuthMetadataRouter,
} from '@modelcontextprotocol/sdk/server/auth/router.js';
app.use(
mcpAuthMetadataRouter({
oauthMetadata,
resourceServerUrl: mcpServerUrl,
scopesSupported: ['mcp:tools'],
resourceName: 'MCP Demo Server',
})
);
const authMiddleware = requireBearerAuth({
verifier: tokenVerifier,
requiredScopes: [],
resourceMetadataUrl: getOAuthProtectedResourceMetadataUrl(mcpServerUrl),
});
Python。 Python SDK では、MCPServer クラスが token_verifier(TokenVerifier のサブクラス)と AuthSettings を受け取ります。メタデータ文書を公開し、未認証のリクエストには適切な 401 を返します。
from pydantic import AnyHttpUrl
from mcp.server import MCPServer
from mcp.server.auth.settings import AuthSettings
app = MCPServer(
name="MCP Resource Server",
token_verifier=token_verifier,
auth=AuthSettings(
issuer_url=AnyHttpUrl("https://auth.example.com/realms/master"),
required_scopes=["mcp:tools"],
resource_server_url=AnyHttpUrl("https://mcp.example.com/mcp"),
),
)
どちらの SDK にも、トークンの resource とサーバー URL を比較する checkResourceAllowed / check_resource_allowed ヘルパーがあります。ハンドシェイク全体を観察するには、MCP Inspector で接続してみてください。
API キーと環境変数の認証情報
MCP ディレクトリ のサーバーの多くは、今も API キーで認証します。それで問題ありませんし、まさに仕様が stdio について述べているやり方です。
環境変数を使う stdio サーバー(公式ドキュメント にある Claude Code の例):
claude mcp add --env AIRTABLE_API_KEY=YOUR_KEY --transport stdio airtable \
-- npx -y airtable-mcp-server
Bearer キーを受け付けるリモート HTTP サーバー:
claude mcp add --transport http secure-api https://api.example.com/mcp \
--header "Authorization: Bearer your-token"
OAuth を使うリモート HTTP サーバー: 認証情報なしで追加し、クライアントからログインします。たとえば claude mcp add --transport http sentry https://mcp.sentry.dev/mcp を実行し、Claude Code 内で /mcp を実行してブラウザでログインを完了します。Sentry MCP Server、Notion MCP Server、GitHub MCP Server はいずれもホスト型エンドポイントを提供しています。クライアント別の詳しい手順は Claude Code MCP ガイド を参照してください。
コミットされる設定ファイルにキーを書かないよう、.mcp.json では環境変数を参照します。
{
"mcpServers": {
"api-server": {
"type": "http",
"url": "https://api.example.com/mcp",
"headers": {
"Authorization": "Bearer ${API_KEY}"
}
}
}
}
静的なキーでも動きますが、通常は長期間有効で権限も広くなりがちです。OAuth ならユーザーごとの同意、スコープ付きで期限のあるトークン、共有シークレットをローテーションせずに済む失効手段が手に入ります。
MCP 認証のトラブルシューティング
| 症状 | 考えられる原因 | 対処法 |
|---|---|---|
| クライアントがブラウザを開かない | サーバーの 401 に resource_metadata がなく、well-known 文書も存在しない | /.well-known/oauth-protected-resource を提供するか、ヘッダーを追加する |
| PKCE 非対応と表示される、またはクライアントが先に進まない | 認可サーバーメタデータに code_challenge_methods_supported がない | PKCE S256 を有効にしてフィールドを公開する |
| クライアント登録に失敗する | CIMD 非対応、registration_endpoint がない、または OIDC がリダイレクト URI を拒否 | CIMD を有効にする、クライアントを事前登録する、またはローカルクライアントでは application_type を native にする |
| 認可サーバーはトークンを受け入れたが MCP サーバーが 401 を返す | オーディエンスの不一致 | トークンのオーディエンスを resource パラメーターのサーバー正規 URI に合わせる |
| 403 がループする | サーバーが不足スコープを 1 つずつチャレンジしている | その操作に必要なすべてのスコープを 1 回のチャレンジで返す |
次のステップ
MCP サーバーディレクトリ から OAuth に対応したホスト型サーバーを選び、お使いの クライアント で接続してフローを確認してみてください。自分でサーバーを運用するなら、公開前に MCP セキュリティのベストプラクティス を読んでおきましょう。
よくある質問
MCP は OAuth を使いますか?
- はい。HTTP トランスポートを使うリモートサーバーについては、MCP 認可仕様は OAuth 2.1 をベースに、保護リソースメタデータ、PKCE、リソースインジケーターを組み合わせています。認可は任意であり、ローカルの stdio サーバーは代わりに環境から認証情報を読み取ることが想定されています。
ローカルの MCP サーバーはどう認証しますか?
- ローカルの stdio サーバーは通常、クライアントがサーバー起動時に渡す環境変数から API キーやトークンを読み取ります。仕様では、stdio の実装は OAuth フローを使わず、環境から認証情報を取得すべきとされています。
MCP では動的クライアント登録が必須ですか?
- いいえ。2026-07-28 リビジョンでは動的クライアント登録は非推奨となり、後方互換性のためだけに残されています。クライアントは事前登録のクライアント ID、次に Client ID Metadata Documents を優先し、認可サーバーが他に対応していない場合にのみ動的登録にフォールバックすべきです。
リモート MCP サーバーで API キーは使えますか?
- 多くのホスト型サーバーは Authorization ヘッダーで送られた API キーを受け付け、Claude Code などのクライアントではサーバー設定でそのヘッダーを指定できます。これでも動きますが、OAuth ならユーザーごとの同意、スコープ付きで期限のあるトークン、より簡単な失効が得られます。