MCP 認証仕様の詳細レビュー（2025-03-26 版）

> 本記事は元々 [blog.logto.io](https://blog.logto.io/ja/mcp-auth-spec-review-2025-03-26) に掲載されたものです。

MCP ([モデルコンテキストプロトコル](https://modelcontextprotocol.io/)) は、AIモデルが外部ツールやサービスと対話することを可能にするオープンスタンダードです。業界で広く採用されています。MCPがHTTPベースの輸送方法をサポートしているため、リモートMCPサーバーはMCPエコシステムにおいてますます重要な役割を果たすでしょう。

ローカルMCPサーバーとは異なり、各ユーザーが自分のサーバーインスタンスを実行できる場合、リモートMCPサーバーはすべてのユーザーが同じMCPサーバーサービスを共有する必要があります。これは[MCP認証仕様](https://spec.modelcontextprotocol.io/specification/2025-03-26/basic/authorization)が解決を目指す核心的な問題を提起します：**MCPサーバーがユーザーの代わりにユーザーリソースにアクセスするにはどうすればよいか**。

この記事では、[MCP認証仕様](https://spec.modelcontextprotocol.io/specification/2025-03-26/basic/authorization)を詳細に分析します。MCP 認証仕様の設計原則を理解し、MCP 認証の実装のための方向性を提供します。この仕様はまだ進化中であるため、Authenticator の実装において私の個人的な経験に基づいていくつかの考えを共有します。これには以下が含まれます：
- 認証フレームワークとしての OAuth 2.1 の利点と限界
- 認証サーバーとしてのMCPサーバーとリソースサーバーの二重役割の課題
- 完全な認証サーバーを実装する上での実践的な複雑さ
- 第三者委任認証に適したシナリオの分析
- 動的クライアント登録の実践的トレードオフ
- MCPサーバーのリソース境界を明確に定義することの重要性


## MCP 認証仕様の概要

[MCP 認証仕様](https://spec.modelcontextprotocol.io/specification/2025-03-26/basic/authorization)は、MCP サーバー (リモート) と MCP クライアント間の認証プロセスを定義します。

この仕様を OAuth 2.1 に基づかせることは非常に合理的な選択だと思います。OAuthは認証プロトコルフレームワークとして、ユーザーがサードパーティアプリケーションに代わってユーザーリソースにアクセスすることを許可するという問題を解決します。OAuthに慣れていない場合は、[AuthWiki-OAuth](https://auth-wiki.logto.io/oauth-2.0)をチェックしてください。

MCP クライアントと MCP サーバーのシナリオでは、「ユーザーが MCP クライアントに MCP サーバー上のユーザーリソースにアクセスすることを許可する」ということです。現在、「MCP サーバー上のユーザーリソース」とは、主に MCP サーバーが提供するツールまたは MCP サーバーのバックエンドサービスによって提供されるリソースを指します。

OAuth 2.1 認証プロセスを実装するために、このプロトコルでは、MCP サーバーが以下のインターフェースを提供して MCP クライアントと連携し、OAuth 2.1 の認証プロセスを完了することを要求しています：

- `/.well-known/oauth-authorization-server`: OAuth サーバーメタデータ
- `/authorize`: 認可エンドポイント、認可要求に使用されます
- `/token`: トークンエンドポイント、トークン交換とリフレッシュに使用されます
- `/register`: クライアント登録エンドポイント、動的クライアント登録に使用されます

認証プロセスは次のとおりです:
```mermaid
sequenceDiagram
    participant AuthEndpoint as 認可エンドポイント (ブラウザ内)
    participant McpClient as MCP クライアント
    participant McpServer as MCP サーバー
    
    McpClient ->> McpServer: トークンなしでツールのリクエスト
    McpServer ->> McpServer: トークンの検証失敗
    McpServer -->> McpClient: 401 エラー     
    McpClient ->> McpServer: GET `/.well-known/oauth-authorization-server`
    McpServer -->> McpClient: OAuth サーバーメタデータ
    McpClient ->> McpServer: POST `/register`: OAuth クライアントを登録
    McpServer -->> McpClient: クライアント資格
    McpClient ->> McpServer: POST `/authorize`: PKCE を使用してサインインを要求
    McpServer -->> AuthEndpoint: 認可エンドポイントへユーザーをリダイレクト
    McpClient ->> AuthEndpoint: ユーザーサインイン済み
    AuthEndpoint -->> McpClient: `auth_code` でリダイレクト
    McpClient ->> McpServer: POST `/token`: トークンをリクエスト
    McpServer -->> McpClient: アクセストークン
    McpClient ->> McpServer: アクセストークンでMCPサーバーツールをリクエスト
    McpServer ->> McpServer: トークンの検証成功
    McpServer ->> McpServer: ツールの呼び出し
    McpServer -->> McpClient: モデルコンテキストに結果を追加
```

この仕様は、第三者の認可サーバーを通じてMCPサーバーが委任認証をサポートする方法も指定しています。

仕様における例のフローは次のとおりです（仕様内容から）：

```mermaid
sequenceDiagram
    participant B as ユーザーエージェント (ブラウザ)
    participant C as MCP クライアント
    participant M as MCP サーバー
    participant T as 第三者認可サーバー

    C->>M: 初期 OAuth 要求
    M->>B: 第三者 /authorize へのリダイレクト
    B->>T: 認可要求
    Note over T: ユーザーが許可
    T->>B: MCP サーバーコールバックへのリダイレクト
    B->>M: 認証コード
    M->>T: トークン交換のためのコード
    T->>M: 第三者アクセス トークン
    Note over M: バインドされた MCP トークンを生成
    M->>B: MCP クライアントコールバックへのリダイレクト
    B->>C: MCP 認証コード
    C->>M: トークンの交換要求
    M->>C: MCP アクセストークン
```

このシナリオでは、MCP サーバーは第三者認可サーバーに認可を委任していますが、それでもMCPサーバーはMCPクライアント用の認可サーバーとして機能しています。これは、MCPサーバーが独自のアクセス トークンをMCP クライアントに発行する必要があるからです。

私の見解では、このシナリオは、MCPサーバーがMCPクライアント（ユーザー）のプロキシとして第三者リソース（例えばGithubのリポジトリ）にアクセスする場合に適しているように見えます。

要するに、プロトコルによれば、MCP サーバーはOAuthで認可サーバーとリソースサーバーの二重の役割を担っています。

次に、認可サーバーおよびリソースサーバーとしてのMCPサーバーの責任について議論します。

## 認可サービスとしての MCP サーバー

MCPサーバーが認可サーバーとして機能する場合、これはMCP クライアントのエンドユーザーがMCP サーバー上で独自のアイデンティティを持っていることを意味します。MCPサーバーはこのエンドユーザーの認証を担当し、このエンドユーザーがMCPサーバーリソースにアクセスするためのアクセストークンを発行します。

上記のMCP 認証仕様によって要求される認可関連のインターフェースは、MCP サーバーが認可サーバーの実装を提供する必要があることを意味します。

しかし、MCP サーバー上で認可サーバー機能を実装することは、開発者にとって大きな課題です。一方で、多くの開発者はOAuth関連の概念に精通していないかもしれません。他方で、認可サーバーを実装する際には考慮すべき多くの詳細があります。関連分野出身の開発者でない場合、実装時にセキュリティ問題などの問題を引き起こす可能性があります。

しかし、プロトコル自体はMCPサーバーが認可サーバー機能を自分で実装することを制限していません。開発者は、これらの認可関連のエンドポイントを他の認可サーバーに完全にリダイレクトまたはプロキシできます。MCPクライアントにとって、これはMCPサーバーが独自に認可サーバー機能を実装することと違いがありません。

```mermaid
sequenceDiagram
    participant Client as MCPクライアント
    participant Server as MCPサーバー
    participant Auth as 認可サーバー
    
    Client->>Server: GET `/.well-known/oauth-authorization-server`
    Server->>Auth: 認可サーバーの `/.well-known/oauth-authorization-server` への転送
    Auth-->>Server: OAuth サーバーメタデータ
    Server-->>Client: OAuth サーバーメタデータを返す
    
    Client->>Server: POST `/authorize`
    Server->>Auth: 認可サーバーの `/authorize` への転送
    Auth-->>Server: 認可応答
    Server-->>Client: 認可応答を返す
    
    Client->>Server: POST `/token`
    Server->>Auth: 認可サーバーの `/token` への転送
    Auth-->>Server: トークン応答
    Server-->>Client: トークン応答を返す
    
    Client->>Server: POST `/register`
    Server->>Auth: 認可サーバーの `/register` への転送
    Auth-->>Server: 登録応答
    Server-->>Client: 登録応答を返す
```

このアプローチは上記で述べた第三者委任認可の方法を使用すべきか疑問に思うかもしれません。

私の見解では、これは主にあなたが依存している第三者認可サービスのユーザーがMCPサーバーのエンドユーザーと同じかどうかに依存します。これは、第三者認可サービスによってあなたに発行されたアクセストークンがあなたのMCPサーバーによって直接消費されることを意味します。

- もしそうであれば、あなたのMCPサーバーでAuth関連のインターフェースを完全に第三者認可サービスに転送できます。

- もしそうでないなら、あなたは仕様に記載された第三者委任認可アプローチを使用すべきです。MCPサーバー内でMCPサーバーが独自に発行するアクセストークンと第三者認可サービスが発行するアクセストークンとの間のマッピング関係を維持する必要があります。

プロトコルで定義されている第三者委任認可アプローチは実際のアプリケーションシナリオにおいていくぶん曖昧であると考えています。プロトコルは、第三者がMCPサーバーの認可プロセスを手助けすることを意図していますが、それでもなおMCPサーバーが自ら独自のアクセストークンを発行する必要があります。これは開発者にとって必ずしも便利ではありません。プロトコルの著者が、第三者のアクセストークンを直接MCPクライアントに返すことが何かしらのセキュリティ上の問題（例えば漏洩/悪用など）を引き起こすと考えたからかもしれません。

経験から言えば、プロトコルで指定された第三者委任認可が最も適したシナリオは「ユーザーがMCPサーバーに第三者リソースにアクセスすることを許可する」シナリオであるべきだと思います。例えば、あるMCPサーバーがユーザーのGithubリポジトリにアクセスし、そのリポジトリのコードをコード配布プラットフォームに展開する必要がある場合。この場合、ユーザーは自分のGithubリポジトリとコード配布プラットフォームにアクセスする権限をMCPサーバーに付与する必要があります。

このシナリオでは、MCPクライアントの認可サーバーとしてMCPサーバーが機能します。これはエンドユーザーがMCPサーバーで独自のアイデンティティを持っているからです。また、MCPサーバーは第三者リソース（この場合はGithub）の第三者クライアントとして機能し、ユーザーリソースにアクセスするためにユーザーの認可を得る必要があります。MCPクライアントとMCPサーバーの間、およびMCPサーバーと第三者リソースの間でユーザーアイデンティティが分離されています。これにより、MCPサーバー内で独自に発行するアクセストークンと第三者認可サービスが発行するアクセストークンの間にマッピング関係を維持することが意味を持ちます。

したがって、プロトコルにおける第三者委任認可プロトコルは、「ユーザーが第三者リソースサーバーでユーザーリソースにアクセスするためにMCPサーバーを認可する方法」の問題を解決するべきです。

## リソースサーバーとしての MCP サーバー

MCPサーバーがリソースサーバーとして機能する場合、MCPサーバーはMCPクライアントのリクエストが有効なアクセストークンを持っているかどうかを確認する必要があります。MCPサーバーはアクセストークンのスコープに基づいて、MCPクライアントに特定のリソースへのアクセスを許可するかどうかを決定します。

MCPの定義から、MCPサーバーによって提供されるリソースはMCPクライアントが使用できるツールであるべきです。このシナリオでは、MCPサーバーはユーザーに特定のツールへのアクセスを提供するかどうかだけを決定する必要があります。

しかし、実世界のシナリオでは、MCPサーバーが提供するこれらのツールも、MCPサーバーサービスプロバイダー自体のリソースサーバーと対話する必要があります。このとき、クライアント要求から取得したアクセストークンが自分のリソースサーバーにアクセスするために必要です。ほとんどの場合、MCPサーバーとツールの背後のリソースサーバーは同じ開発者です。MCPサーバーは自分のバックエンドリソースによってMCPクライアントに提供されるインターフェースに過ぎません。このとき、MCPサーバーは1つの認可サーバーから発行された同じアクセストークンをバックエンドリソースと共有できる可能性があります。

この場合、MCPサーバーがリソースサーバーとして提供したツールと自分のサービスのリソースを述べるより、既存のリソースサーバーがMCPサーバーとなり、MCPクライアントが呼び出すツールを提供するだけであると言った方が正確です。

リソースサーバーに自分のリソースサーバーが提供するリソースを含めるのは現実のシナリオを考慮した結果です。しかし、私は個人的には、MCP サーバーが提供するリソースが MCP クライアントが使用するツールだけであり、ツールが必要とするリソースはMCPサーバーが他のリソースサーバー（自社および第三者を含む）から取得するべきだと考えています。これにより、すべての現実世界のシナリオがカバーできます。

## MCP 認証の仕組み

MCP サーバーが認可サーバーおよびリソースサーバーとしての役割を把握した後、MCP 認証が具体的にどのように機能するかがわかります：

```mermaid
sequenceDiagram
    participant AE as 認可エンドポイント (ブラウザ内)
    participant C as MCP クライアント
    box MCP サーバー
        participant A as 認可サーバー
        participant R as リソースサーバー
    end

    C ->> R: トークンなしでツールのリクエスト
    R ->> R: トークンの検証失敗
    R -->> C: 401 エラー     
    C ->> A: GET `/.well-known/oauth-authorization-server`
    A -->> C: OAuth サーバーメタデータ
    C ->> A: POST `/register`: OAuth クライアントを登録
    A -->> C: クライアント資格
    C ->> A: POST `/authorize`: PKCE を使用してサインインを要求
    A -->> AE: 認可エンドポイントへユーザーをリダイレクト
    C ->> AE: ユーザーサインイン済み
    AE -->> C: `auth_code` でリダイレクト
    C ->> A: POST `/token`: トークンをリクエスト
    A -->> C: アクセストークン
    C ->> R: アクセストークンでMCPサーバーツールをリクエスト
    R ->> R: トークンの検証成功
    R ->> R: ツールの呼び出し
    R -->> C: モデルコンテキストに結果を追加
```

## 動的クライアント登録

仕様は、認可サーバーがクライアントを識別する方法も定義しています。OAuth 2.1 は動的クライアント登録プロトコルを提供し、MCP クライアントが手動で介入することなく OAuth クライアントIDを自動的に取得できるようにします。

仕様によると、MCP サーバーは OAuth 2.0 の動的クライアント登録プロトコルをサポートするべきで、これにより、MCP クライアントは新しいサーバーに自動的に登録して OAuth クライアント IDを取得できます。この方法がMCPシナリオで推奨される理由は主に以下のとおりです：
- MCP クライアントは事前に可能なすべてのサーバーを知ることができません
- 手動登録はユーザーにとって面倒です
- 新しいサーバーとの接続がシームレスになります
- サーバーは独自の登録ポリシーを実装できます

ただし、実践的観点から見ると、MCPシナリオでの動的クライアント登録の適用について以下の意見があります：

- 実際のOAuthサービスの実践において、クライアントは通常、特定のビジネスアプリに一対一で対応しています。クライアントを動的に作成することは、OAuthサービスで関連リソース（ユーザー、アプリなど）の効果的な管理を妨げる可能性があります。多くのOAuthサービスプロバイダーは、クライアントが自在にクライアントとして登録することを希望するのではなく、接続されたクライアントを明確にコントロールしたいと考えています。
- 多くのOAuthサービスは、ユーザーがクライアントを動的に作成することを推奨または許可していません。これによりサービスの悪用が発生する可能性があるからです。多くの成熟したOAuthサービスプロバイダー（例えばGitHub、Googleなど）は、開発者が開発者コンソールを通じてクライアントを手動で登録する必要があるとし、アプリケーション、コールバックURLなどの詳細も提供する必要があります。
- OAuthクライアントの手動登録は、実際には開発中の一度の作業であり、すべてのエンドユーザーが行う必要があるものではありません。それは開発者にとって大きな負担をもたらすものではありません。
- パブリッククライアント（例えばネイティブアプリケーション、シングルページアプリケーションなど）においては、動的登録を行わずにOAuthフローを実装するより安全な方法があります。クライアントIDとPKCE（Proof Key for Code Exchange）の組み合わせは、パブリッククライアントのための十分に安全なOAuthフローを提供できます。
- プロトコルは、動的クライアント登録を利用することが、クライアントが事前にクライアントIDを知る必要がないことを避けると指摘していますが、実際にはMCPクライアントは常に事前にリモートMCPサーバーのアドレスを知る必要があります。そうであれば、リモートMCPサーバーのアドレスを渡す際にクライアントIDを指定することはそれほど大きなトラブルを引き起こしません。また、MCPクライアントがMCPサーバーにクライアントIDを尋ねるための慣習を作成することも可能で、これは難しい作業ではありません。

動的クライアント登録は理論上MCPエコシステムに柔軟性を提供しますが、現実の実装においては本当にこの動的登録メカニズムが必要かどうかを考慮する必要があります。ほとんどのサービスプロバイダーにとって、手動でOAuthクライアントを作成および管理することは、よりコントロールしやすく、安全な方法かもしれません。

## まとめ

この記事では、MCP 認証仕様の設計理念および実装の課題を詳細に分析しました。OAuth 2.1 に基づく認証フレームワークとして、この仕様はリモートMCPサーバーがユーザーのためにユーザーリソースにアクセスする方法という重要な問題を解決することを目指しています。

MCP サーバーが認可サーバーおよびリソースサーバーとしての二重役割を果たすことや、動的クライアント登録と第三者委任認可の利点と欠点についての詳細な議論を通じて、Authenticatorを実装する際の個人的な経験からの考えや提案を提供しました。

MCP認証仕様は進化し続けていることに注意する価値があります。Logtoチームの一員として、私たちはこの仕様の最新の開発状況に注意を払い、実際の実装において私たちのソリューションを継続的に最適化し、AIモデルと外部サービスとの安全な相互作用に貢献していきます。