🚀 モデルコンテキストプロトコル (MCP) サーバ + Microsoft Entra ID OAuth + タスク管理
これは、リモートMCP接続をサポートするモデルコンテキストプロトコル (MCP) サーバで、Microsoft Entra ID (旧称Azure AD) OAuthが組み込まれ、包括的なMicrosoft To Doタスク管理機能を備えています。
このサーバを自分のCloudflareアカウントにデプロイし、独自のAzure AD OAuthアプリケーションを作成すると、機能が完備したリモートMCPサーバを構築できます。ユーザーは、MicrosoftアカウントでサインインしてMCPサーバに接続し、To Doリストを管理したり、タスクを作成したり、リマインダーを設定したりすることができます。
workers-oauth-provider ライブラリを使用して、CloudflareにデプロイされたMCPサーバに他のOAuthプロバイダーを統合する方法の参考例として使用することができます。
MCPサーバ(Cloudflare Workers を使用)は以下のような役割を果たします:
- MCPクライアントに対してOAuth サーバー として機能します。
- 実際の OAuthサーバー(この場合はMicrosoft Entra ID)に対してOAuth クライアント として機能します。
🚀 クイックスタート
リポジトリをクローンし、依存関係をインストールします:npm install
本番環境用
新しいAzure ADアプリケーションを作成します:
-
Azureポータルにアクセスし、Azure Active Directory → アプリの登録に移動します。
-
新規登録をクリックします。
-
以下のように設定します:
- 名前:
MCP Entra OAuth Todo Server
- サポートされるアカウントの種類:
- マルチテナント サポート(推奨):"任意の組織ディレクトリ内のアカウント (任意のAzure ADディレクトリ - マルチテナント) および個人用Microsoftアカウント" を選択します。
- 単一組織のみの場合は、"この組織ディレクトリ内のアカウントのみ" を選択します。
- リダイレクトURI:
- 種類:
Web
- URI:
https://remote-mcp-entra-oauth-todo.<your-subdomain>.workers.dev/callback
- また、
http://localhost:8789/callback(開発用)も追加します。
-
登録後、以下をメモします:
- アプリケーション (クライアント) ID
- ディレクトリ (テナント) ID
-
クライアントシークレットを作成します:
- 証明書とシークレット → 新しいクライアントシークレットに移動します。
- シークレット値をすぐにコピーします(再度表示されません)。
-
APIアクセス許可を構成します:
- APIアクセス許可 → アクセス許可の追加 → Microsoft Graph → 委任されたアクセス許可に移動します。
- 以下を追加します:
User.Read(サインインとユーザープロファイルの読み取り)
Tasks.ReadWrite(ユーザーのタスクの読み取りと書き込み)
- 組織の管理者同意を付与します。
💡 使用アドバイス:コードでは https://graph.microsoft.com/.default スコープが使用されており、事前に同意されたすべてのアクセス許可を動的に要求します。新しいツールやアクセス許可を追加する場合は、Azureポータルで追加して同意を付与するだけで、コードの変更は必要ありません!
-
Wranglerを使用してシークレットを設定します:
wrangler secret put ENTRA_CLIENT_ID
wrangler secret put ENTRA_CLIENT_SECRET
wrangler secret put ENTRA_TENANT_ID
wrangler secret put COOKIE_ENCRYPTION_KEY
wrangler secret put DEFAULT_TIMEZONE
⚠️ 重要な注意事項
マルチテナント構成:ENTRA_TENANT_ID を設定する際には、以下のようにします:
common を使用すると、職場/学校アカウントと個人用Microsoftアカウントの両方がサポートされます(マルチテナントに推奨)。
organizations を使用すると、職場/学校アカウントのみが許可されます(Azure AD)。
consumers を使用すると、個人用Microsoftアカウントのみが許可されます。
- 特定のテナントIDを使用すると、その特定のテナントのユーザーのみがアクセスできます。
マルチテナントアプリの場合、common を使用すると、トークンがユーザーのホームテナントのコンテキストで発行されるため、他のテナントのゲストユーザーであっても、個人のリソース(To Doリストなど)にアクセスできます。
⚠️ 重要な注意事項
最初のシークレットを作成すると、Wranglerが新しいWorkerを作成するかどうかを尋ねます。新しいWorkerを作成してシークレットを保存するには、"Y" を入力します。
タイムゾーンの構成:
DEFAULT_TIMEZONE を好みのタイムゾーンに設定します(例:Asia/Tokyo、America/New_York、Europe/London)。
- このタイムゾーンは、上書きされない限り、すべてのタスクの期限日やリマインダーに使用されます。
- 必要に応じて、ユーザーはタスクごとに異なるタイムゾーンを指定することもできます。
- 設定されていない場合、デフォルトは
Europe/London です。
KV名前空間の設定
- KV名前空間を作成します:
wrangler kv namespace create "OAUTH_KV"
- WranglerファイルをKV IDで更新します。
デプロイとテスト
MCPサーバをデプロイして、workers.devドメインで利用可能にします:
wrangler deploy
Inspector を使用してリモートサーバをテストします:
npx @modelcontextprotocol/inspector@latest
https://remote-mcp-entra-oauth-todo.<your-subdomain>.workers.dev/mcp を入力し、接続をクリックします。認証フローを完了すると、ツールが機能することが確認できます。
これで、リモートMCPサーバがデプロイされました!
Claude DesktopからリモートMCPサーバにアクセスする
Claude Desktopを開き、設定 → 開発者 → 設定の編集に移動します。これにより、ClaudeがアクセスできるMCPサーバを制御する設定ファイルが開きます。
内容を以下の設定に置き換えます。Claude Desktopを再起動すると、OAuthログインページが表示されるブラウザウィンドウが開きます。認証フローを完了して、ClaudeにMCPサーバへのアクセスを許可します。アクセスを許可すると、ツールが使用可能になります。
{
"mcpServers": {
"entra-todo": {
"command": "npx",
"args": [
"mcp-remote",
"https://remote-mcp-entra-oauth-todo.<your-subdomain>.workers.dev/mcp"
]
}
}
}
インターフェイスの🔨の下にツールが表示されたら、Claudeにそれらを使用するように依頼できます。例えば:
- "私のすべてのTo Doリストを表示してください"
- "「買い物」という名前の新しいリストを作成してください"
- "明日の午後5時を期限として、「食料品を買う」というタスクを私の買い物リストに追加してください"
- "私の仕事リストにあるタスクは何ですか?"
- "「クライアントに電話する」タスクを重要度高に変更し、金曜日までに期限を設定してください"
- "「レポートを提出する」タスクを完了済みにマークしてください"
- "「チーム会議の準備」タスクに明日の午前9時のリマインダーを設定してください"
- "私の個人リストからすべての完了済みタスクを削除してください"
ローカル開発用
MCPサーバを反復開発してテストしたい場合は、ローカル開発環境で行うことができます。この場合は、Azure ADで別のOAuthアプリを作成する必要があります:
- ホームページURLには
http://localhost:8789 を指定します。
- 承認コールバックURLには
http://localhost:8789/callback を指定します。
- クライアントIDをメモし、クライアントシークレットを生成します。
- プロジェクトのルートに
.dev.vars ファイルを作成し、以下の内容を追加します:
ENTRA_CLIENT_ID=your_development_azure_ad_client_id
ENTRA_CLIENT_SECRET=your_development_azure_ad_client_secret
ENTRA_TENANT_ID=common # マルチテナントの場合は 'common' を使用するか、特定のテナントIDを使用します
COOKIE_ENCRYPTION_KEY=any_random_string_here
DEFAULT_TIMEZONE=Europe/London
開発とテスト
サーバをローカルで実行して、http://localhost:8789 で利用可能にします:
wrangler dev
ローカルサーバをテストするには、Inspectorに http://localhost:8789/mcp を入力し、接続をクリックします。指示に従うと、「ツールを一覧表示」できます。
Claudeや他のMCPクライアントの使用
Claudeを使用してリモートMCPサーバに接続すると、いくつかのエラーメッセージが表示される場合があります。これは、Claude DesktopがまだリモートMCPサーバをサポートしていないため、混乱することがあるからです。MCPサーバが接続されていることを確認するには、Claudeのインターフェイスの右下隅にある🔨アイコンにマウスをホバーします。そこにツールが表示されているはずです。
Cursorや他のMCPクライアントの使用
CursorをMCPサーバに接続するには、種類 を "コマンド" として選択し、コマンド フィールドにコマンドと引数を1つにまとめます(例:npx mcp-remote https://<your-worker-name>.<your-subdomain>.workers.dev/mcp)。
CursorはHTTP+SSEサーバをサポートしていますが、認証をサポートしていないため、依然として mcp-remote を使用する必要があります(STDIOサーバを使用し、HTTPサーバではなく)。
Windsurfなどの他のMCPクライアントにMCPサーバを接続するには、クライアントの設定ファイルを開き、Claudeの設定に使用したのと同じJSONを追加し、MCPクライアントを再起動します。
仕組み
OAuthプロバイダー
OAuthプロバイダーライブラリは、Cloudflare Workers用の完全なOAuth 2.1サーバー実装として機能します。OAuthフローの複雑さを処理し、トークンの発行、検証、管理を行います。このプロジェクトでは、以下の二重の役割を果たします:
- サーバーに接続するMCPクライアントの認証
- Microsoft Entra IDのOAuthサービスへの接続の管理
- KVストレージにトークンと認証状態を安全に保存
永続的MCP
永続的MCPは、CloudflareのDurable Objectsを使用して基本的なMCP機能を拡張し、以下を提供します:
- MCPサーバの永続的な状態管理
- リクエスト間での認証コンテキストの安全な保存
this.props を介した認証済みユーザー情報へのアクセス
- ユーザーIDに基づく条件付きツールの可用性のサポート
MCPリモート
MCPリモートライブラリは、サーバーがInspectorなどのMCPクライアントによって呼び出されるツールを公開できるようにします。これは以下を行います:
- クライアントとサーバー間の通信プロトコルを定義します。
- ツールを定義する構造化された方法を提供します。
- リクエストとレスポンスのシリアル化と逆シリアル化を処理します。
- クライアント通信には、推奨される Streamable HTTP と Server-Sent Events (SSE) の両方のプロトコルをサポートします。
トランスポートプロトコルの移行
この例は、新しい Streamable HTTP トランスポートプロトコルをサポートするように更新されており、非推奨のServer-Sent Events (SSE) プロトコルを置き換えています。サーバーは現在、両方のエンドポイントを公開しています:
/mcp - 推奨:新しいStreamable HTTPプロトコルを使用します。
/sse - 非推奨:レガシーのSSEプロトコル(下位互換性のために維持されています)。
すべての新しい統合では、/mcp エンドポイントを使用する必要があります。SSEエンドポイントは将来のバージョンで削除されます。
利用可能なツール
ユーザープロファイル
getUserProfile - 認証されたユーザーのMicrosoft Graphプロファイルを取得します。
To Doリスト管理
listTodoLists - 認証されたユーザーのすべてのTo Doタスクリストを取得します。
createTodoList - 指定された名前で新しいタスクリストを作成します。
updateTodoList - 既存のタスクリストの名前を更新します。
deleteTodoList - タスクリストを削除します。
タスク管理
listTasks - 特定のTo Doリストからすべてのタスクを取得します。
getTask - 特定のタスクの詳細情報を取得します。
createTask - オプションのプロパティで新しいタスクを作成します:
- タイトル(必須)
- 本文/説明
- 期限日と時間
- リマインダー日と時間
- 重要度レベル(低、通常、高)
- カテゴリ/タグ
- 日付のタイムゾーン
updateTask - 任意のタスクプロパティを更新します:
- タイトル
- 本文/説明
- ステータス(未開始、進行中、完了、他者を待機中、延期)
- 重要度レベル
- 期限日と時間
- リマインダー設定
- カテゴリ
deleteTask - リストからタスクを削除します。
簡単なテストツール
add - 2つの数値を足す(MCP接続のテスト用)
💻 使用例
Claudeに以下のようなタスクを依頼することができます:
- "私のすべてのTo Doリストを表示してください"
- "「買い物」という名前の新しいリストを作成してください"
- "明日の午後5時を期限として、「食料品を買う」というタスクを私の買い物リストに追加してください"
- "私の仕事リストにあるタスクは何ですか?"
- "「クライアントに電話する」タスクを重要度高に変更し、金曜日までに期限を設定してください"
- "「レポートを提出する」タスクを完了済みにマークしてください"
- "「チーム会議の準備」タスクに明日の午前9時のリマインダーを設定してください"
- "私の個人リストからすべての完了済みタスクを削除してください"
新しいツールの追加
OAuthフローで使用される .default スコープのおかげで、新しいツールの追加は簡単です:
-
Azureポータルでアクセス許可を追加する:
- アプリ → APIアクセス許可に移動します。
- To Do機能のために
Tasks.ReadWrite アクセス許可がすでに含まれています。
- まだ行っていない場合は、管理者同意を付与します。
-
コードでツールを追加する ():
this.server.tool(
"getTodoTaskList",
"特定のTo Doタスクリストの詳細を取得する",
{
listId: z.string().describe("取得するTo DoリストのID"),
},
async ({ listId }) => {
const client = Client.init({
authProvider: (done) => {
done(null, this.props!.accessToken);
},
});
try {
const taskList = await client
.api(`/me/todo/lists/${listId}`)
.get();
return {
content: [{ text: JSON.stringify(taskList, null, 2), type: "text" }],
};
} catch (error) {
return {
content: [{
text: `エラー: ${error instanceof Error ? error.message : String(error)}`,
type: "text"
}],
isError: true,
};
}
}
);
- デプロイする:
npm run deploy
以上で完了です!OAuthフローで .default が事前に同意されたすべてのアクセス許可を動的に要求するため、スコープの変更は必要ありません。
セキュリティに関する注意事項
- ✅ クライアントシークレットはCloudflare Workersのシークレットとして保存され(クライアントには公開されません)、
- ✅ OAuth状態パラメータによりCSRF攻撃を防止し、
- ✅ HMAC署名付きクッキーで承認ダイアログの状態を維持し、
- ✅ アクセストークンはMCPセッショントークン内で暗号化され、
- ✅ すべての通信はHTTPSを介して行われ、
- ✅ 承認ダイアログでは、承認前にクライアント情報が表示されます。
トラブルシューティング
"アクセストークンの取得に失敗しました"
- Azureポータルでクライアントシークレットが正しく、期限切れになっていないことを確認します。
- リダイレクトURIが正確に一致することを確認します(プロトコルとポートを含む)。
ENTRA_TENANT_ID が正しいことを確認します。
"Tasks.ReadWriteアクセス許可が拒否されました"
- Azureポータルで管理者同意が付与されていることを確認します。
- ユーザーがAzure ADで適切なアクセス許可を持っていることを確認します。
- アクセス許可が 委任された アクセス許可として追加されていることを確認し、アプリケーションアクセス許可ではないことを確認します。
"無効なテナント"
- シークレット内の
ENTRA_TENANT_ID が正しいことを確認します。
- マルチテナントアプリの場合は、職場/学校アカウントと個人アカウントの両方をサポートするために
common をテナントIDとして使用します。
- 職場/学校アカウントのみの場合は
organizations を使用し、個人アカウントのみの場合は consumers を使用します。
- 特定のテナントを使用する場合は、テナントIDがAzureポータルに表示されているものと一致することを確認します。
- ゲストユーザーに重要:ユーザーがテナントのゲストであり、個人のリソース(To Doなど)にアクセスしようとしている場合は、トークンがホームテナントのコンテキストで発行されるように
common を使用する必要があります。
Claude Desktopでの接続問題
- 設定を更新した後、Claude Desktopを再起動します。
- WorkerのURLが正しく、アクセス可能であることを確認します。
wrangler secret list を使用してすべてのシークレットが正しく設定されていることを確認します。
タスクのタイムゾーン問題
- シークレット内の
DEFAULT_TIMEZONE が正しく設定されていることを確認します。
- 標準的なタイムゾーン名(例:
America/New_York、EST ではなく)を使用します。
- ツールのパラメータで指定することで、タスクごとにデフォルトのタイムゾーンを上書きすることができます。
アーキテクチャ
この実装は、標準的なOAuth 2.0認可コードフローに従っています:
- ユーザーがMCPクライアントを開く → クライアントがサーバーに接続しようとします。
- サーバーがOAuthを開始する → クライアント情報を含む承認ダイアログが表示されます。
- ユーザーが承認する → Microsoftログインページにリダイレクトされます。
- ユーザーが認証する → Microsoftが承認コードを含めてリダイレクトします。
- サーバーがコードを交換する → クライアントシークレットを使用してMicrosoftからアクセストークンを取得します。
- サーバーがユーザー情報を取得する → Microsoft Graphを呼び出してユーザープロファイルを取得します。
- トークンが発行される → MCPクライアントがユーザーコンテキストを含む暗号化されたトークンを受け取ります。
- ツールが利用可能になる → クライアントは委任されたアクセス許可でMicrosoft Graphツールを呼び出すことができます。
Cloudflare Workerは、機密クライアント(サーバーサイドアプリケーション)として機能し、以下を行います:
- クライアントシークレットを安全に保存します。
- 完全なOAuthフローを処理します。
- 承認コードをアクセストークンに交換します。
- 認証されたユーザーを代表してMicrosoft Graph API呼び出しを行います。
📄 ライセンス
MIT