本記事は 2025年4月7日 に更新された「An Introduction to MCP and Authorization」を翻訳した記事です。
Model Context Protocol (MCP) は、 GPT、 Gemini、 Claude のような大規模言語モデル (LLM) を、標準化され、安全で、再利用可能な方法で外部ツールや API に接続するための方法として注目を集めています。
MCP とは
MCP は、ツール呼び出しや関数呼び出しという形で LLM に API を提供する汎用的な変換レイヤーだと考えてください。 LLM は言語処理に優れていますが、 GitHub、データベース、ローカルファイルシステムのようなサービスと対話するために必要な特定の API をネイティブには理解できません。 MCP は、個々のツールの API に精通していなくても、LLM がこれらのサービスにアクセスするための標準プロトコルです。
MCP がどのように機能するかをより良く理解するために、各コンポーネントとそれらが相互にどのようにやり取りするかを理解しましょう。:
- MCP ホスト: Claude Desktop、Cursor IDE またはその他の AI ツールのような MCP を介してデータへのアクセスを必要とするプログラムです。
- MCP クライアント: MCP ホスト は 1 つ以上の MCP クライアントを実行します。各クライアントは MCP サーバーと 1 対 1 の関係になります。例えば、Cursor が起動すると、提供された各 MCP サーバーに接続します。
- MCP サーバー: MCP ホストが呼び出したいツールを実行するサーバーです。サーバーは、ローカルで実行することも、リモートサーバーでホストすることもできます。
- ローカルのデータソース: MCP サーバーがローカルで実行されている場合、ホストコンピューター内のファイル、データベース、ローカルで実行されているサービスやアプリケーションといったローカルリソースにアクセスできます。
- リモートサービス: インターネット経由で利用可能な外部システムです。例: Auth0 API、Google Calendar API、GitHub API など。
MCP はどのように動作するのか
MCP の基本的な考え方とコンポーネントを理解したところで、 MCP がホストからサービスへ、そして再びホストへ戻る一連の流れでどのように動作するのかを見ていきましょう。
MCP ホスト と クライアント
MCP ホストが全ての初期化を行います。まず MCP ホストで、MCP サーバー定義のインポートから始めます。この定義は JSON フォーマットで、サーバーごとに異なります。
設定例を以下の通りです。:
{
"mcpServers": {
"filesystem": {
"command": "npx",
"args": [
"-y",
"@modelcontextprotocol/server-filesystem",
"/Users/username/Desktop",
"/Users/username/Downloads"
]
}
}
}
この設定により、MCP ホストは server-filesystem
MCP サーバーを介して、ユーザーの Desktop および Downloads フォルダーへのアクセスを提供する MCP クライアントを初期化します。 MCP サーバーは一般的にセットアップや複数の設定に関する詳細をドキュメントで提供します。
クライアントは、次のセクションで説明するトランスポートを介して MCP サーバーと通信します。
トランスポート
MCP のトランスポートは、MCP クライアントと MCP サーバー間の通信手段を提供します。トランスポートは、すべての通信メカニズムを処理し、両者でメッセージを送受信する役割を担います。
MCP サーバーを開発する際、開発者は MCP の要件に応じて特定のトランスポートタイプを選択します。
現在の仕様では、3 種類の異なるトランスポートタイプを利用できます。:
1. Stdio (標準入出力): このトランスポートでは、入力および出力ストリームを使って通信を行います。ローカル統合やコマンドラインツールで役立ちます。
const server = new Server({ name: "my-mcp-server", version: "1.0.0" }); const transport = new StdioServerTransport(); await server.connect(transport);
2. SSE (Server Sent Event, サーバー送信イベント): このトランスポートでは、 HTTP POST ストリーミングリクエストを行なって通信を行います。インタラクティブな UI などで役立ちます。
const app = express(); const server = new Server({ name: "my-mcp-server", version: "1.0.0" }); let transport: SSEServerTransport | null = null; app.get("/sse", (req, res) => { transport = new SSEServerTransport("/messages", res); server.connect(transport); }); app.post("/messages", (req, res) => { if (transport) { transport.handlePostMessage(req, res); } }); app.listen(3000);
3. カスタム トランスポート: MCP では、開発者が固有の要件に合わせて新しいカスタムトランスポートを簡単に定義できます。特定のネットワークプロトコルをサポートする必要がある場合や、既存のシステムと統合する必要がある場合に、シンプルなインターフェースを実装することでカスタムトランスポートを作成できます。
interface Transport { // Start processing messages start(): Promise<void>; // Send a JSON-RPC message send(message: JSONRPCMessage): Promise<void>; // Close the connection close(): Promise<void>; // Callbacks onclose?: () => void; onerror?: (error: Error) => void; onmessage?: (message: JSONRPCMessage) => void; }
MCP サーバー
最後に MCP サーバーを定義します。 MCP サーバーは、LLM のリクエストに基づいてクライアントが送信したメッセージを受信して処理し、結果をストリーム配信または出力します。
MCP サーバーは、ホストに 3 つの機能を提供できます。:
- リソース: MCP サーバーはクライアントに読み取り可能なデータやコンテンツを公開し、LLM との対話のコンテキストとして使用できるようにします。リソースは、ファイルコンテンツ、データベースレコード、 API レスポンスなど、あらゆる種類のデータです。
- プロンプト: MCP サーバーはクライアントがユーザーや LLM をガイドするために使用できる再利用可能なプロンプトテンプレートやワークフローを定義します。
- ツール: おそらく MCP が最も知られている理由でしょうが、 ツールはクライアントにサーバーが実行可能な機能を提供します。ツールのおかげで、LLM は外部システムと直接対話し、計算を実行し、実世界にアクセスできます。
最初の MCP サーバー
MCP サーバーとは何か、そしてどのように機能するかが分かったので、最初のサーバーを構築しましょう。
まず考える必要があるのは、どのタイプのトランスポートを使用するかということです。SSE サーバーから始めます。この MCP サーバーは、ランダムな犬の画像を取得できます。適当な名前を付けましょう。
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp"; import { SSEServerTransport } from "@modelcontextprotocol/sdk/server/sse"; import express from "express"; const server = new McpServer({ name: "MCP for Dog Lovers", version: "1.0.0", }); const app = express(); let transport: SSEServerTransport | null = null; app.get("/sse", async (_req, res) => { transport = new SSEServerTransport("/messages", res); server.connect(transport); }); app.post("/messages", async (req, res) => { if (transport) { transport.handlePostMessage(req, res); } }); app.listen(3000);
次に、サードパーティサービスから犬の画像を取得し、クライアントに返すツールを定義します。:
server.tool("getRandomDogImage", {}, async () => { const response = await fetch(`https://dog.ceo/api/breeds/image/random`); const data = await response.json(); return { content: [ { type: "text", text: `Your dog image is here: ${data.message}` }, ], }; });
このツールは非常にシンプルで、引数を取りません。そこで、好きな犬種の名前を指定できるように改良しましょう。:
server.tool("getRandomDogImage", { breed: z.string() }, async ({ breed }) => { const response = await fetch( `https://dog.ceo/api/breed/${breed}/images/random`, ); const data = await response.json(); return { content: [ { type: "text", text: `Your dog image is here: ${data.message}` }, ], }; });
同様に、 LLM のツール呼び出しで引数を指定できるように、 MCP ツールを引数を指定できるようにします。これにより柔軟性が向上します。完全なコードサンプルと、このサーバーが実際に動作する様子を確認しましょう。:
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp"; import { SSEServerTransport } from "@modelcontextprotocol/sdk/server/sse"; import express from "express"; import { z } from "zod"; const server = new McpServer({ name: "My Super Cool Thursday MCP Demo Server", version: "1.0.0", }); server.tool("getRandomDogImage", { breed: z.string() }, async ({ breed }) => { const response = await fetch( `https://dog.ceo/api/breed/${breed}/images/random`, ); const data = await response.json(); return { content: [ { type: "text", text: `Your dog image is here: ${data.message}` }, ], }; }); const app = express(); let transport: SSEServerTransport | null = null; app.get("/sse", (req, res) => { transport = new SSEServerTransport("/messages", res); server.connect(transport); }); app.post("/messages", (req, res) => { if (transport) { transport.handlePostMessage(req, res); } }); app.listen(3000); console.log("Server is running on http://localhost:3000/sse");
このサーバーを実行する手順は次の通りです。:
- プロジェクトを保存するための新しいディレクトリを作成し、
npm init
を実行します。 - すべてのコードを
main.ts
ファイルに保存します。 - プロジェクトの依存関係をインストールします。:
npm i @modelcontextprotocol/sdk express zod
npx tsx main.ts
でサーバーを実行します。- 最後に、好きなクライアントで MCP をセットアップします。サーバーには次の URL を指定します。:
http://localhost:3000/sse
実際の動作を確認しましょう。:
MCP のライフサイクル
MCP がどのように機能するかを理解し、 MCP サーバーも自作しましたが、処理の裏にはプロンプトからレスポンスまでの一連のステップがあります。
ライフサイクルは 2 つの異なるフローに分かれています。
接続
- MCP ホスト (画像の例では Cursor) は MCP 設定ファイルを読み取り、接続する必要のある MCP サーバーを決定し、プロトコルバージョンと機能情報を含む
initialize
リクエストを MCP サーバーに送信します。 - MCP サーバーは、自身のプロトコルバージョンと機能情報 (例: 利用可能なツール) を応答します。
- クライアントは確認応答として
initialized
通知を送信します。 - これで接続が使用可能になります。
メッセージング
- ユーザーが MCP ホストを介してプロンプトを送信すると、ホストは LLM モデルを実行し、LLM モデルはツール呼び出しをリクエストする必要があるかどうかを判断します。このケースでは、「ラブラドールのランダムな画像を生成して」とプロンプトを出すと、LLM は犬について話していること、そのタスクに利用可能なツールがあること (これは MCP サーバーを介して行えます)、そしてそのツールを呼び出すことを選択します。
- このプロセスでは、MCP クライアントは MCP サーバーにツールの実行とパラメーターを示すメッセージを送信します。
- MCP サーバーはリクエストを受信し、Dog API を呼び出し、画像 URL をクライアントに返します。
- MCP ホストは、ツールからのレスポンスを LLM コンテキストとマージし、新しいレスポンスを処理して、ユーザーに表示します。
MCP における認可
これまでのところ、MCP サーバーは誰が情報をリクエストしているのか、あるいはツールを呼び出しているのかわかりません。この状況は、MCP サーバーが認可を必要としないパブリックサービスにアクセスする場合や、ローカルリソースへアクセスする場合には機能するかもしれません。
しかし、認可が必要なリモートサービスにアクセスする必要がある場合はどうでしょうか。確認しましょう。
API キーやシークレットによる認可
開発者が外部サービスにアクセスするためによく使用する方法の 1 つは、API キー を使用することです。API キーは API やサービスへのリクエストを認証および認可するシンプルな文字列トークンです。
API キーを環境変数やローカルリソースに保存し、MCP サーバーの実行中に取得できます。これは通常 MCP クライアントが起動する際に行われます。例は以下です。:
{ "mcpServers": { "github": { "command": "npx", "args": ["-y", "@modelcontextprotocol/server-github"], "env": { "GITHUB_PERSONAL_ACCESS_TOKEN": "<YOUR_TOKEN>" } } } }
この方法は、 stdio トランスポートを使用する MCP サーバーにのみ推奨されます。
あるいは、MCP サーバーは CLI アプリケーションのようにローカルで実行されるサーバーであるため、サーバーはユーザーを認証するデバイスフロー を利用してアクセストークンを取得できます。デバイスフローに必要なクライアントのクレデンシャルは、前述のとおり環境変数またはシークレットとして保存されます。
OAuth 2.1
2025 年 3 月に、 MCP 仕様は OAuth 2.1 を使用した認可 を標準化する新しい仕様のリリースで大きく進歩しました。
新しい仕様で MCP クライアントやサーバーは OAuth 2.1 を使用して、認可の委任をできるようになりました。新しい仕様を実装した MCP サーバーは Auth0、Google API、GitHub など OAuth を使った既存のサービスのように、特定のタスクやアクションの実行をユーザーが認可できるようになります。
新しい仕様の一部は以下です。:
必須のセキュリティ基準 (PKCE): すべてのクライアントに PKCE を義務付けることで、セキュリティの基準が大幅に向上し、導入後すぐに一般的な攻撃から保護します。
設定を簡素化 (メタデータディスカバリー): 特定の MCP サーバーにログインするために、ツールはどのようにして誘導先を知るのでしょうか。仕様では、サーバーが OAuth エンドポイントを自動的に公開できる メタデータディスカバリー が推奨されています。これにより手動設定が減り、エラーも少なくなります。
円滑な利用開始 (動的クライアント登録, Dynamic Client Registration - DCR): おそらくユーザーの利便性にとって最も影響力のある機能の 1 つです。 DCR により、 MCP クライアント (汎用的な AI ツールなど) はこれまでアクセスしたことのない新しい MCP サーバーに プログラム自身で登録 できます。これにより、新しい接続ごとに面倒な手動セットアップ操作をユーザーに強いることを回避できます。これはモデルサーバーが膨大な数になる可能性のある状況において非常に重要です。
既存の ID インフラの活用 (サードパーティ認証): 仕様 では、MCP サーバーが実際のユーザーのログインに Auth0 のような信頼できる サードパーティの ID プロバイダー に委任するフローを明示的にサポートしています。
認可フローは次のようになります。:
- MCP クライアントは MCP サーバーと標準の OAuth フローを開始します。
- MCP サーバーはユーザーをサードパーティの認可サーバーにリダイレクトします。
- ユーザーはサードパーティのサーバーで認可します。
- サードパーティのサーバーは、ユーザーを MCP サーバーに認可コードとともにリダイレクトして戻します。
- MCP サーバーは認可コードをサードパーティのアクセストークンと交換します。
- MCP サーバーはサードパーティのセッションに紐付けた独自のアクセストークンを生成します。
- MCP サーバーは MCP クライアントとの元の OAuth フローの続きを行い完了させます。
今後の展開
新しい仕様では MCP 標準に OAuth 2.1 サポートが導入され、MCP サーバーを リソースサーバであり、かつ OAuth 認可サーバーでもある と定めています。(訳注: MCP サーバーは、リソースサーバとして MCP クライアントに API を提供し、OAuth 認可サーバとして MCP クライアントにトークンを提供します。さらに、サードパーティの ID プロバイダも利用する場合は、MCP サーバーは OAuth クライアントとしても動作すると定められています。)この設計は一歩前進ではあるものの、既存の ID プロバイダーを十分に活用する方法にはなっておらず、MCP サーバーの開発者がディスカバリー、登録、トークンエンドポイントを個別に実装する必要があります。これは開発者にとって課題になっています。不適切な実装はセキュリティの脆弱性やスケーリングの課題につながる可能性があるため、課題は単にコード行数が増えるだけにとどまりません。
もちろん、MCP における認可を改善するための議論 はすでに始まっており、アイデンティティの専門家がこれらの制限の一部に対する解決策を見つけるために意見や知識を共有しています。
しかし、改善される前に、現在の状況を確認しましょう。
現在の課題
仕様 で定義されているように、OAuth 2.1 をサポートする MCP サーバーの要件は以下となります。:
- MCP 認証実装は、機密クライアントとパブリッククライアントの両方に対して適切なセキュリティ対策を講じた OAuth 2.1 を実装しなければなりません (MUST)。
- MCP 認証実装は、OAuth 2.0 動的クライアント登録プロトコル (RFC7591) をサポートすべきです (SHOULD)。
- OAuth 2.0 Authorization Server Metadata (RFC8414) を MCP サーバーは実装すべきであり (SHOULD)、MCP クライアントは実装しなければなりません (MUST)。 Authorization Server Metadata をサポートしないサーバーは、デフォルトの URI スキーマに従わなければなりません (MUST)。
MCP サーバーがサポートすべきとされている OAuth 2 Authorization Server Metadata により、クライアントには特に以下のものが公開されます。:
- issuer
- authorization_endpoint
- token_endpoint
- registration_endpoint
- userinfo_endpoint
例えば、次の URL を使用して Auth0 テナントのメタデータを確認できます。:
https://<your-tenant>.auth0.com/.well-known/openid-configuration
内容は以下のようになります。:
{ "issuer": "https://<your-tenant>.auth0.com/", "authorization_endpoint": "https://<your-tenant>.auth0.com/authorize", "token_endpoint": "https://<your-tenant>.auth0.com/oauth/token", "device_authorization_endpoint": "https://<your-tenant>.auth0.com/oauth/device/code", "userinfo_endpoint": "https://<your-tenant>.auth0.com/userinfo", "mfa_challenge_endpoint": "https://<your-tenant>.auth0.com/mfa/challenge", "jwks_uri": "https://<your-tenant>.auth0.com/.well-known/jwks.json", "registration_endpoint": "https://<your-tenant>.auth0.com/oidc/register", "revocation_endpoint": "https://<your-tenant>.auth0.com/oauth/revoke", ... }
このようにメタデータには Auth0 テナント (または ID プロバイダー) に関する情報が含まれています。では、MCP サーバーを実装するには、これらのエンドポイントを独自に実装する必要があるのでしょうか?
独自に実装することはあまり良い方法ではなく、 MCP サーバーに多くの負担と制限をもたらします。例えば、次のとおりです。:
- MCP サーバーはトークン処理のためのストレージが必要になります。
- MCP サーバーはクリティカルなインフラストラクチャの一部となり、必要なすべてのセキュリティ要件、監査、ロギング、認証に対応する必要があります。
- MCP サーバーはサードパーティのトークンを検証する責任を負うことになりますが、これは必ずしも必要ではなく、推奨される方法ではありません。
では独自に実装する代わりに、メタデータのエンドポイントを ID プロバイダーのメタデータのエンドポイントに「プロキシ」することで対応することができるでしょうか?この場合、 MCP サーバーはある意味で認可サーバーに認可を委任することになります。
しかし、MCP 仕様によると、プロキシする方法には問題があります。セクション 2.9.2 で、MCP サーバーがサードパーティの認可サーバーで認可し、その後サードパーティのセッションに紐付けられた独自のアクセストークンを生成すると記載されています。メタデータのエンドポイントをプロキシして、認可を委任すると、この部分の仕様に対応できません。
では、代替案は何でしょうか?(訳注: 現時点で利用可能な良い代替案はありません)
アイデンティティの専門家が意見を寄せているため、上記の問題が対処され、MCP 標準が OAuth2 に関する素晴らしいアプローチを提供する新しい仕様をリリースする可能性を考慮する必要があります。
まとめ
MCP が成長を続ける中で、業界のリーダーとして、また意識の高い開発者として、開発者がより安全で信頼性の高い製品を構築できるようにする、より強力で安全な標準の確立を主導することが重要です。これは MCP の旅のほんの始まりにすぎません。本記事が気に入ったり、セキュリティ、アイデンティティ、 MCP に関するコメントやアイデアがありましたら、以下のコメントセクションで会話に参加してください。皆で MCP をより良くしていきましょう。
Auth0 では、セキュアな AI に深くコミットしており、そのコミットメントの一環として、セキュアな AI アプリケーションやサービスを構築するのに役立つ新しいツールと SDK である Auth for GenAI に取り組んでいます。
お読みいただきありがとうございました。
About the author

Juan Cruz Martinez
Staff Developer Advocate