office365-mcp
日本語訳
office365-mcp
Microsoft 365 向けのマルチユーザー・リモート MCP サーバー — Outlook メール、Teams、SharePoint/OneDrive を Microsoft Graph 経由で操作します。各ユーザーは通常のブラウザーサインインで自分の Microsoft アカウントを接続します。サーバーはその認可を暗号化したまま保持し、以降のツール呼び出しではすべてそのユーザーとして動作します。したがって、一度接続すれば、クライアントが Microsoft トークンを保持することなく動作し続けます。共有メールボックスとサービスメールボックス(support@、billing@、info@)は、一部のツールに付属のパラメーターではなく、第一級の ID として扱われます。
1 つの TypeScript コードベース、3 つのデプロイ先:
プラットフォーム | エントリーポイント | ビルド / デプロイ |
AWS Lambda (Function URL) |
|
|
まず AWS に触れずに設定を検証できる:
DRY_RUN=1 npm run deploy:lambda| Azure Functions (v4 Node) | src/entries/azure.ts | npm run build:azure && func azure functionapp publish … |
| Plain Node(開発 / 自己ホスト) | src/entries/node.ts | npm run dev |
他の Microsoft 365 MCP サーバーとの違い
オープンソースの分野は大きく、優れたプロジェクトもいくつかあります。このサーバーは、Graph のカバレッジを増やすのではなく、別のデプロイモデルと ID モデルを中心に構築されています。
サーバーは更新トークンを仲介し、クライアントは Microsoft トークンを一切見ません。 既存のサーバーの多くは、1 台のマシン上の 1 ユーザーにつき 1 つのトークンを
~/.outlook-mcp-tokens.json、~/.microsoft_mcp_token_cache.json、~/.office-mcp-tokens.jsonなどの平文で保存しています。ある古いリモートサーバー (Softeria のms-365-mcp-serverの HTTP モード) は、トークン更新はクライアントの責任であると明言しているため、Graph アクセストークンが約 1 時間で期限切れになるとセッションが終了します。一方、このサーバーでは、ユーザーごとの Entra 更新トークンを AES-256-GCM でシールし、サーバー側で保持します。サーバーはユーザーの代わりにアクセストークンを静かに更新します。ステートレスでサーバーレス指向です。 SSE セッションの固定化も長時間実行プロセスもなく、すべてのセッション状態と認証情報は DynamoDB に保持されます。HTTP 対応の選択肢は、常時稼働のコンテナ (Express、Azure Container Apps、ローカル stdio シャドウの背後にある App Service バックエンド) を前提としています。
共有メールボックスは、アプリ専用パスを含めてモデル化されています。 呼び出し元が Exchange 権限を保持している場合、サーバーは
/users/{mailbox}に対してユーザー自身の委任されたトークンを使用します。誰もそのメールボックスにサインインしない場合、スコープ付きのアプリケーション認証情報を使用できます。この 2 つ目の管理されたパスを提供するオープンソースサーバーは他にありません。Exchange 側のスコープ設定は、ここではスクリプト (deploy/entra/scope-app-only.ps1) として提供されており、演習として残されているわけではありません。ユーザーは、アシスタントが利用できるメールボックスを選択します。 委任された
Mail.*.Sharedにはメールボックスごとの同意がありません。そのスコープを付与すると、そのユーザーが Exchange で開けるすべてのメールボックスを開けるトークンが生成され、Microsoft ではそれを絞り込む方法はありません。そのため、サインインの後にはこのサーバーが提供する承認ページが続きます。ユーザーがそこでチェックした内容は、すべてのリクエストでサーバー側に厳密に設定されます。メールボックス承認 を参照してください。ガバナンスの仕組みがあります。 ユーザーごとのツール許可リスト(新しいツールはデフォルトで拒否)、ユーザーごとのレート制限、ユーザー自身の承認に加えた管理者のメールボックス上限、不可逆削除に対するデプロイレベルのロック、そして呼び出しごとにユーザー、ツール、引数、実際に操作したメールボックスを記録する構造化監査ログがあります。
ツールの数は意図的に絞っています。 エンドポイント形状の 300 ではなく、タスク形状の 27 ツールです。カバレッジの広さで競うのではありません。Soferia は Excel range と OneNote ページをカバーし、上のMicrosoft の Work IQ サーバーはセマンティック検索と Defender 業級のトレシブレースを備えています。しかし、どちらも Microsoft 365 Copilot ライセンスなしで自分のリージョンで自己ホストできるサーバーは提供していません。
Microsoft 公式の選択肢も 2 2つあり、知っておいてください。Microsoft の MCP Server for Enterprise は無料ですが読み取り専用で、設定は Entra ディレクトリデータに限定されています。補完であって、競合ではありません。Agent 365 また "Work IQ" はメール、カレンダー、Teams、SharePoint をカバーしていますが、プレビュー限定、Microsoft ホストのみ、Microsoft 365 Copilot ライセンスが必要です。
クイックスタート
1. この手順は最も失敗しやすいステップです。 deploy/entra/SETUP.md を参考にしてください。特に、プラットフォームは SPA ではなく Web として登録してください。SPA のリダイレクト URI は更新トークンを 24 時間にサイレントでを制限し、そのトークンから派生するすべてのトークンにその有効期限が受け継がれるため、一度接続すればという前提が崩れます。
2. 2 つのキーを生成します。 これらは役割が異なる別のキーであり、互いに代替にはなりません。
npm install
npm run gen:oauth-key # RS256 keypair — signs the tokens Claude presents to US
npm run gen:enc-key # AES-256-GCM key — seals the tokens WE present to Microsoftgen:enc-key の出力は、デプロイとは別にシークレットマネージャーにバックアップしてください。これを失うと、すべての保存済み送信が解除ができなくなり、ユーザー全員が同時に再度サインインする必要があります。
3. デプロイします。
npm run build:lambda
npm run deploy:lambda # wraps `sam deploy` against deploy/aws/template.yamlスタックは EntraRedirectUri を出力します。アプリ登録にその URI を正確に登録してください。これは自動化できない唯一の手順です。
4. クライアントを接続する。 カスタムコネクターとして https://<your-deployment>/mcp を追加します。クライアントは /.well-known/oauth-protected-resource から OAuth 認証エンドポイントを検出し、自分を登録して、ユーザーを Microsoft のサインインに送ります。Microsoft の同意画面の後、この サーバー独自のメールバック承認ページが表示され、ユーザーはアシスタントが使用できる共有メールボックスを選択します。その後で初めてクライアントはトークンを受け取ります。`o365_whoamiを呼び出し、接続状態、付与された権限、ユーザーが承認した送信箱、呼び出し元が現在保持しているツールを確認します。
ローカル開発では、Configuration の変数を .env ファイルに配置します。Entra の登録、2 つのキー、そして MCP_USERS_FILE + MCP_GRAPH_FILE + MCP_OAUTH_FILE をファイル保存ストア用に入力します。これにより、npm run dev が AWS なしで実際のブラウザサインインとメールバック承認フローを実行できます。.env.example は注釈付きのバージョンです。次に:
npm run dev # http://localhost:3000/mcpアプリ登録に http://localhost:3000/oauth/callback を 2 番目のリダイレクト URI として追加します。
アーキテクチャ
Claude / MCP client
│ 1. POST /mcp (Bearer: our RS256 JWT)
▼
┌──────────────────────────────────────────────────────────┐
│ office365-mcp (Lambda Function URL / Azure Fn / Node) │
│ │
│ Hono ── /mcp ── JSON-RPC 2.0 ── tool registry │
│ │ │
│ ├─ OAuth 2.1 authorization server (for the MCP client) │
│ │ /.well-known/* /oauth/register /authorize │
│ │ /callback /consent /token /jwks.json │
│ │ │
│ └─ Graph token broker ── actor resolution ── client │
└───────┬──────────────────────────┬────────────────────────┘
│ │
│ 2. browser sign-in │ 5. Bearer: Graph access token
▼ ▼
Microsoft Entra ID Microsoft Graph
login.microsoftonline graph.microsoft.com/v1.0
│
│ 3. refresh token ──► AES-256-GCM ──► DynamoDB (MCP_GRAPH_TABLE)
│ (row-bound AAD)
▼
4. the browser lands back here, on /oauth/consent — the user ticks
which mailboxes the assistant may use, and only then is the
authorization code handed to the MCP clientトランスポート。 Streamable HTTP、ステートレスモード。JSON-RPC 2.0 を POST /mcp に送ります。1 リクエストに 1 メッセージを送ります。JSON-RPC リクエストは -32600 で拒否されます。これは、MCP の 2025-06-18 版からバッチ処理が削除されたため、また、呼び出しの配列が 1 回のレート制限の消費量に乗せられてしまうためです。未認証のリクエストには、RFC 9728 の WWW-Authenticate: Bearer realm="mcp", resource_metadata=… チャレンジを含む 401 を返し、クライアントにコネクタの QAuth フローを開始させます。保護されたリソースドキュメントは /.well-known/oauth-protected-resource と /.well-known/oauth-protected-resource/mcp の両方で提供され、resource は {origin}/mcp として報告されます。これはユーザーが実際に入力したエンドポイントであり、クライアントが比較する対象です。
アイデンティティモデル
2 つの OAuth 関係性が別々にあります。この区別を保つことが全体の設計です。
MCP クライアント ↔ このサーバー。 私たちが認可サーバーです。クライアントは動的に登録(RFC 7591)し、
/oauth/authorizeと/oauth/tokenに対して認可コード + PKCE フローを実行し、だけ、当サーバーが署名した RS256 JWT を受け取ります。Entra は動的クライアント登録をサポートしておらず、この交換を一切行いません。このサーバー ↔ Entra。 私たちは 1 1 つの静的な Web リダイレクト URI を持つ機密クライアントです。ユーザーのサインイン中に、私たちは独自の独立した PKCE チェーンレベルを設定し、クライアントシークレットまたは証明書でコードを引き換え、
id_token、Graph アクセストークン、そしてoffline_access要求により 更新トークン を受け取ります。
この更新トークンこそが本製品です。これは、行に結び付けられた追加の認証データ ({tid}:{oid}:refresh) の下で AES-256-GCM によってシールされ、あるユーザーのレコードから抽出されたブロブが別のユーザーのレコードに送り返されないようにし、認証情報専用のテーブルに書き込みます。その後の呼び出しは、JWT → (tid, oid) → キャッシュされたアクセストークン、または更新トークン引き換え → Graph となります。ユーザーのキーは、卸しこんだidThis pairs (tid, oid)/codeInToken.これは、変更可能で管理者が制作を確認できる email、preferred_username、upn ではありません。
この間、/oauth/callback は一時停止します。Microsoft の認可が保存されると、MCP クライアントに認可コードを渡しません。代わりに、Useに 1 回限りのテケト付きで /oauth/consent にの不相続き、ユーザーはこのアシスタントがアクセスできるメールバックの数を選択します。ユーザーがそのページを送信したとき onlyに、コードが発行され、クライアントはリダイレクトでた戻ります。メールバック承認 を参照してください。
テナント可能リスト (O365_ALLOWED_TENANTS) は、サインイン時だけでなく、すべての リクエストで再度チェックされるため、テナントを削除すると、次のログインではなく、ただちに効果があります。
委任 (delegated) とアプリ専用 (app-only)
すべての Graph 呼び出しは、設定から決定的な方法でまず actor を解決します。モデルがサーバーを説得して権限昇格などできないのです。
Actor | Address | オーダー | Units |
|
|
| サインインしたユーザーが確認できることと完全に同じ |
|
| 別のメールバドであり、接続時にユーザーが承認し、管理者ポリシーが許可し、呼び出し元が Exchange 権限を保持している場合 | Exchange がそのメールバド上のそのユーザーに与えているもの |
|
| メールバが | Exchange RBAC がアプリケーションに対してスコープしているもの |
delegated-self 以外のアクターは、Graph に何かを問い合わせる前に、まず policyAllowsMailbox を通過します。これは、ユーザー自身の承認、その次に管理者が設定した上げ革先で、 Graph が制される前に決定します。委任がデフォルトであり、通常のパスです。この 2 つのゲートを通過すると、既存のExchange 権限が実際のゲートとなり、拒否された場合は「そのメールバックへの Full Access」を管理者に依頼してください。という実際的な 403 をサーバーが返し、それをユーザーに通知します。App-only は、誰もサインインしないメールバックにのみ、デフォルトで無効です。/me が共有メールバックを意味するこもりはなく、共有メールバックへ /me で到達するパスはありません。アプリ専用トークンは、ユーザーがサインインしていないため、/me を一切使用できません。
Teams は設計上、委託された専用です。永続的にです。制限と既知の問題 を参照してください。
モデルに公開されるツール
27 のツール。W はテナントのスタートを変更するツールを生成します。これらは、新規にプロビジョニングされたユーザーについてはデフォルトで無効であり、そのポリシーに allowWrites が必要です。Outlook のツールはすべて任意のメールバック引数 (UPN または SMTP アドレス) を受け取り、メールバック選択できます。自分のメールバックにはそれを省略。すべての戻り値 ID は Microsoft Graph のプロジェクト ID です。そのまま渡し、作成しないでください。
Outlook — 読み取る
Let's check. Wait I have not included the heading "### Outlook — reading"? At end yes. But I notice I wrote ### モデルに公開されるツール? The original heading is "## Tools exposed to the model". I translated as "## モデルに公開されるツール". Good. But I missed content under "### Outlook — reading" because it ends. So just heading.
Need check if I accidentally added extra text "この2つの間、/oauth/callback は一時停止します..." actually original "Between the two" meaning between the two OAuth relationships. That is translated okay: "それら2つの OAuth 関係の狭間で、/oauth/callback は一時停止します" Wait I omitted "それら". But fine.
However I need ensure "the two separate OAuth relationships" section: "There are two separate OAuth relationships, and keeping them distinct is the whole design:" I translated more or less. Good.
Need maybe "source" content exact etc.
But I notice I've written:"AWS に触れずに設定を検証できる:" — This line should be "まず AWS に触れずに設定を検証できます:"? Original "Validate the configuration without touching AWS first:" Maybe "AWS before validation" But "first" means before deploy. Our "AWS に触れずに設定を検証できる:" okay.
"GXP1" appears. In output I "GXP1" on line, but then "| Azure Functions ..." with backslash. Good.
Now check "office365-mcp nickname" title should be literal # office365-mcp (not "#365-mcp"?). Yes.
Potential error: first paragraph "マルチユーザー・リモート MCP server" We need "multi-user remote" in Japanese. Good.
Potential issue "every user connects with... the server keeps the authorization, encrypted, and acts as that user on every user's each tool call." I wrote "acting as that user" but use bold? Actually original has no bold, only em. I used **. Hmm.
Original: "acts as that user on every subsequent tool call" with emphasis around "as that user". In my translation I used "そのユーザーとして" because I wrote *そのユーザーとして*? Actually above I typed *そのユーザーとして*? In final I erroneously maybe used **. Need be accurate. Let's check final draft: In first paragraph, I wrote "以後のツール呼び出しではすべてそのユーザーとして動作します" maybe. But in final output above, I think "以後のツール呼び出しごとにそのユーザーとして動作します" with one * = italic. That is fine.
Actually in final: "サーバーはその認証を暗号化したまま保持し、** のツールコールごとに" I wrote maybe "** の"? I read final: "サーバーはその認可を暗号化したまま保持し、**そのユーザー(すこし)*として動作します。"? The text above in final says "そのユーザー"? I need check: The final I've typed in chat at "Let's craft final" includes:
"サーバーはその認可を暗号化したまま保持し、そのユーザーとして動作します。" Actually in the first paragraph I wrote "サーバーはその認可を暗号化したまま保持し、そのユーザー(そのユーザー) として動作します" with no emphasis? Looking at the actual output above: "サーバーはその認可を暗号化したまま保持し、そのユーザーとして動作します" I see two asterisks at start and end - likely a Strong "そのユーザー" because I read. That's not strong. Need use single asterisks. I should modify final to *そのユーザーとして*.
Also in "サーバーはそのユーザーのサインイン中に... the user's own independent PKCE chain": I used *? Need check.
Need finalize with careful emphasis markers. Since the goal preserve emphasis markers exactly. Where original has *as that user* use *そのユーザーとして*. Where bullet starts bold with **The server...** use **サーバーは...**. Good.
Let's adjust.
Should we include "GXP1" after paragraph with colon. In markdown, line "GXP1" is a text paragraph, not code. Good. But maybe need blank line after. In final I have GXP1 then blank line. Good.
Potential issue: "あの 2つのキー" sentence: "These are different keys with different jobs..." Good.
Need check "All ids returned are opaque Microsoft Graph ids" "IDs" is written "id" in original? original "ids". We can keep "id".
One more: "MCP Server for Linux" vs "MCP server for Microsoft". "Microsoft's MCP Server for Enterprise" is product? Fine.
Potential issue "Word IQ" didn't include "Microsoft's own Work IQ servers" -> "Microsoft 自身の Work IQ サーバーは" etc.
Let's now produce final directly, doing cleanup. Use ** for "mailbox approval page**" etc.
Need make sure the table \| lines after GXP1: In final, I will include them as in original but maybe translate only "Azure Functions (v4 Node)" no, and "Plain Node(開発 / 自己ホスト)" row. Good.
Let's craft final answer now.# office365-mcp
Microsoft 365 向けのマルチユーザー・リモート MCP サーバー — Outlook メール、Teams、SharePoint/OneDrive を Microsoft Graph 経由で操作します。各ユーザーは通常のブラウザーサインインで自分の Microsoft アカウントを接続します。サーバーはその認可を暗号化したまま保持し、以降の各ツール呼び出しではそのユーザーとして動作します。したがって、一度接続すれば、クライアントが Microsoft トークンを一切保持せずに動作し続けます。共有メールボックスとサービスメールボックス(support@、billing@、info@)は、一部のツールに付随するパラメーターではなく、第一級のアイデンティティとして扱われます。
1 つの TypeScript コードベース、3 つのデプロイ先:
プラットフォーム | エントリーポイント | ビルド / デプロイ |
AWS Lambda (Function URL) |
|
|
まず AWS に触れずに設定を検証できます:
DRY_RUN=1 npm run deploy:lambda| Azure Functions (v4 Node) | src/entries/azure.ts | npm run build:azure && func azure functionapp publish … |
| Plain Node(開発 / 自己ホスト) | src/entries/node.ts | npm run dev |
他の Microsoft 365 MCP サーバーとの違い
オープンソースの分野は広く、優れたプロジェクトもいくつかあります。このサーバーは、Graph のカバレッジを増やすことではなく、異なるデプロイモデルとアイデンティティモデルを中心に構築されています。
サーバーが更新トークンを仲介し、クライアントは Microsoft トークンを一切見ることがありません。 既存のサーバーの多くは、1 台のマシン上の 1 ユーザーにつき 1 つのトークンを、
~/.outlook-mcp-tokens.json、~/.microsoft_mcp_token_cache.json、~/.office-mcp-tokens.jsonなどの平文で保持しています。ある成熟したリモートサーバー(Softeria のms-365-mcp-serverの HTTP モード)は、トークンの更新はクライアントの責任であると明言しており、Graph アクセストークンが約 1 時間で期限切れになるとセッションが終了します。ここでは、ユーザーごとの Entra 更新トークンは AES-256-GCM でシールされてサーバー側に保存され、サーバーはユーザーの代わりに静かにアクセストークンを更新します。ステートレスかつサーバーレス向けの設計です。 SSE セッションアフィニティも、長時間実行プロセスもなく、すべてのセッション状態と資格情報状態は DynamoDB にあります。HTTP 対応の代替案は、常時稼働のコンテナ(Express、Azure Container Apps、ローカル stdio リレーの背後にある App Service バックエンド)を前提としています。
共有メールボックスは、アプリ専用パスを含めてモデル化されています。 呼び出し元が Exchange 権限を持つ場合、サーバーは自分の委任トークンを
/users/{mailbox}に対して使用します。誰もそのメールボックスにサインインしない場合は、スコープ付きのアプリケーション資格情報を使用できます。この 2 番目の管理されたパスを提供するオープンソースサーバーは他にありません。Exchange 側のスコープ指定は、deploy/entra/scope-app-only.ps1というスクリプトとしてここに含まれており、利用者に委ねるのではありません。ユーザーは、アシスタントが利用できるメールボックスを選択できます。 Entra には、委任された
Mail.*.Sharedのメールボックス単位の同意がありません。そのスコープを付与すると、Exchange がそのユーザーに開かせてくれるすべてのメールボックスを開けるトークンが生成され、Microsoft にはそれを制限する手段がありません。そこで、サインインの後にはこのサーバーが提供するメールボックス承認ページが表示され、ユーザーがそこでチェックした内容がサーバー側で常に強制されます。メールボックス承認 を参照してください。ガバナンスの仕組みがあります。 新しいツールをデフォルトで拒否するユーザー単位のツール許可リスト、ユーザー単位のレート制限、ユーザー自身の同意に加えた管理者のメールボックス上限、元に戻せない削除に対するデプロイレベル上のロック、そして呼び出しごとにユーザー、ツール、引数、実際に操作したメールボックスを記録する構造化監査ログを備えています。
ツールの表面は意図的に小さくなっています。 エンドポイント型の 300 ツールではなく、タスク型の 27 ツールです。幅広さで競うのではありません。Softeria は Excel の範囲や OneNote ページをカバーし、Microsoft 自身の Work IQ サーバーはセマンティック検索と Defender レベルのトレー信号を持っています。しかし、どちらもMicrosoft 365 Copilot ライセンスなしで、自分でリージョンを決めてホストできるサーバーを提供していません。
ファーストパーティの選択肢は 2 つあり、知っておく価値があります。Microsoft の MCP Server for Enterprise は無料ですが読み取り専用で、Entra ディレクトリデータに限定されています。補完であり、競合ではありません。Agent 365 / Work IQ はメール、カレンダー、Teams、SharePoint を対象とした実現にしていますが、プレビュー段階で Microsoft ホスト専用があり、Microsoft 365 Copilot ライセンスが必要です。
クイックスタート
1. Entra アプリを登録します。 このステップが最も間違いやすい作業です。deploy/entra/SETUP.md に従ってください。特に、プラットフォームは SPA ではなく Web として登録してください。SPA のリダイレクト URI は、更新トークンを 24 時間に静かに制限し、それから派生するすべてのトークンにその期限が引き継がれるため、「一度接続すればよい」という前提が壊れます。
2. 2 つのキーを生成します。 これらは異なる役割を持つ別のキーであり、互いに代用することはできません。
npm install
npm run gen:oauth-key # RS256 keypair — signs the tokens Claude presents to US
npm run gen:enc-key # AES-256-GCM key — seals the tokens WE present to Microsoftgen:enc-key の出力は、デプロイとは別にシークレットマネージャーにバックアップしてください。これを失うと、保存されているすべての接続が暗号解除できなくなり、すべてのユーザーが一斉に再度サインインすることを余儀なくされます。
3. デプロイします。
npm run build:lambda
npm run deploy:lambda # wraps `sam deploy` against deploy/aws/template.yamlスタックは EntraRedirectUri を出力します。その URI をアプリ登録にそのまま登録してください。多分自動化できない唯一の手順です。
4. クライアントを接続します。 https://<your-deployment>/mcp をカスタムコネクターとして追加します。クライアントは /.well-known/oauth-protected-resource から OAuth エンドポイントを検出し、自分を登録し、ユーザーを Microsoft のサインインに送ります。Microsoft の同意画面の後、このサーバー自身のメールボックス承認ページが表示され、ユーザーはアシスタントが使用できる共有メールボックスを選択します。クライアントがトークンを受け取るのはその後です。その後、o365_whoami を呼び出すと、接続状態、付与されたアクセス許可、ユーザーが承認したメールボックス、呼び出し元が現在保持している Pass ツールが報告されます。
ローカル開発では、Configuration の変数を .env ファイルに置きます。最低限、Entra の登録情報、2 つのキー、そしてファイルバックのストア用に MCP_USERS_FILE + MCP_GRAPH_FILE + MCP_OAUTH_FILE が必要です。これで npm run dev は AWS なしで実ブラウザのサインインとメールボックス承認フローを実行できるようになります。.env.example に注釈付きのサンプルがあります。あとは:
npm run dev # http://localhost:3000/mcpアプリ登録に http://localhost:3000/oauth/callback を 2 番目のリダイレクト URI として追加してください。
アーキテクチャ
Claude / MCP client
│ 1. POST /mcp (Bearer: our RS256 JWT)
▼
┌──────────────────────────────────────────────────────────┐
│ office365-mcp (Lambda Function URL / Azure Fn / Node) │
│ │
│ Hono ── /mcp ── JSON-RPC 2.0 ── tool registry │
│ │ │
│ ├─ OAuth 2.1 authorization server (for the MCP client) │
│ │ /.well-known/* /oauth/register /authorize │
│ │ /callback /consent /token /jwks.json │
│ │ │
│ └─ Graph token broker ── actor resolution ── client │
└───────┬──────────────────────────┬────────────────────────┘
│ │
│ 2. browser sign-in │ 5. Bearer: Graph access token
▼ ▼
Microsoft Entra ID Microsoft Graph
login.microsoftonline graph.microsoft.com/v1.0
│
│ 3. refresh token ──► AES-256-GCM ──► DynamoDB (MCP_GRAPH_TABLE)
│ (row-bound AAD)
▼
4. the browser lands back here, on /oauth/consent — the user ticks
which mailboxes the assistant may use, and only then is the
authorization code handed to the MCP clientトランスポート。 Streamable HTTP、ステートレスモード。JSON-RPC 2.0 を POST /mcp へ送ります。配列された JSON-RPC リクエストは 1 リクエストに 1 メッセージというポリシーで -32600 として拒否されます。これは、バッチ処理が MCP 2025-06-18 版で削除されたことと、呼び出しの配列が 1 回のレート制限の対象として扱われるためです。未認証のリクエストには、RFC 9728 の WWW-Authenticate: Bearer realm="mcp", resource_metadata=… チャレンジ付き 401 を返し、クライアントにコネクタの OAuth フローを開始させます。保護されたリソース文書は /.well-known/oauth-protected-resource と /.well-known/oauth-protected-resource/mcp の両方で提供され、resource には {origin}/mcp が報告されます。これはユーザーが実際に指定した URL であり、クライアントが比較対象です。
アイデンティティモデル
2 つの独立した OAuth の関係があり、この区別を維持を保つことが全体の設計です。
MCP クライアント ↔ このサーバー。 ここでは私たちが認可サーバーを務めます。クライアントは動的に登録 (RFC 7591) し、
/oauth/authorizeと/oauth/tokenに対して認可コード + PKCE フローを実行し、私たちが署名した RS256 JWT を受け取ります。Entra は動的クライアント登録をサポートしておらず、このやり取りには一切関与しません。このサーバー ↔ Entra。 私たちは 1 つの静的な Web リダイレクト URI を持つ機密クライアントです。ユーザーのサインイン中に、私たちは Entra に対し独立したPKCE チェーンを実行し、クライアントシークレットまたは証明書でコードを交換し、
id_token、Graph アクセストークン、追加のrefresh_token(offline_accessを要求するため)を受け取ります。
この更新トークンこそ成果物です。それは、{tid}:{oid}:refresh という追加認証データに紐付けられて AES-256-GCM でシールされ、他のユーザーのレコードに転送できない値として、資格情報専用のテーブルに格納ワードです。それ以降の呼び出しはすべて、JWT → (tid, oid) → キャッシュされたアクセストークン、または更新トークン引き換え → Graph、という順路になります。ユーザーのキーは id_token からの不変な組み合わせ (tid, oid) のみで、email、preferred_username、upn は変更可能で管理者が制御できるため、使用しません。
この 2 つの関係範囲の間で、/oauth/callback は座標します。Microsoft の認可が保存されると、MCP クライアントに認証コードを渡しません。代わりに、ワンタイムチケット付きの /oauth/consent へブラウザを をリダイレクトし、ユーザーはこのにするアシスタントが操作できるメールボックスを選択します。そのページでユーザーが送信した場合に限ってコードが発行され、クライアントは元のアプリ名へ戻されます。メールボックス承認 を参照。
テナント許可リスト (O365_ALLOWED_TENANTS) は、サインイン時だけでなくすべてのリクエストで再検証されるため、テナントを削除すると次のログイン待たずにすぐに効果があります。
委任(delegated)とアプリ専用(app-only)
すべての Graph 呼び出しは、設定から決定的にアクターを解決します。モデルがサーバーを騙して特権を引き上げることはできません。
Actor | Address | When | Sees |
|
|
| サインイン済みユーザーがそのまま見える内容 |
|
| 別のメールボックスを指し、接続時にユーザーが承認し、管理者ポリシーが許し、呼び出し元が Exchange 権限を持っている場合 | Exchange がそのメールボックス上のそのユーザーに権限を付与している範囲 |
|
| メールボックスが | Exchange RBAC がアプリに対してスコープしている範囲 |
delegated-self 以外のすべてのアクターは、Graph に何かを問い合わせる前に、まず policyAllowsMailbox を通過します。つまり、ユーザー自身の承認済みメールボックスの一覧と、管理者が設定した上限を照合します。デリゲート(委任)がデフォルトで、通常の経路です。この 2 つのゲートを通過すると、既存の Exchange 権限が本当のゲートとして機能します。I multiply of拒否されたリクエストにはサーバーが誠実な 403 を返し、その 403 は「原因 Exclusion Work Exchange のこそのメールボックスに対する Full Access を管理者に依頼してください」と解釈され出します。app-only は誰もサインインしないメールボックス専用で、デフォルトでオフです。/me が shared メールボックスを意味することはなく、/me を経る共有メールボックスへのパランスはありません。サインイン済みユーザーがいないため、アプリ専用トークンは /me を一切使用できません。
Teams は設計上、恒久的に委任のみ(delegated-only・)です。制限と既知の問題 を参照してください。
モデルに公開されているツール
27 ツールです。W と書かれているツールはテナントの状態を変更するツールです。これらは新規に Provision したユーザーの既定では無効で、さらにそのポリシーが AllowWrites を持っていることが必要です。また、すべての Outlook ツールはオプションの mailbox 引数(UPN または SMTP アドレス)を受け付けます。自分用メールボックスかどうかいを指定せずに省略してください。返された ID はすべて不透明な Microsoft Graph ID です — そのままの値で渡し、ご自身で構造化しないでください。
Outlook — 読み取り
ツール | 説明 |
| Outlook の検索構文( |
| フォルダー内のメッセージを構造フィルターと並べ替えで一覧表示します — 未読のみ、送信者、日付範囲、順序など。 |
| メッセージ 1 通分を完全に取得し、本文はプレーン テキストで返します。オプションでインターネット メッセージ ヘッダーと添付ファイルのメタデータを含みます。長い本文は切り捨てられ、元の長さが報告されます。 |
| メール フォルダーをトップ レベルまたはツリー全体で一覧表示し、メッセージ数と未読数を示します。メッセージを移動する前にフォルダー ID を解決するために使用します。 |
| 1 つのメッセージの添付ファイル名、種類、サイズ、インライン フラグを一覧表示します。メタデータのみで、ファイルの内容は返しません。 |
| 添付ファイルをダウンロードし、有効期間の短いプリサイン URL を返します。インラインでバイト列を返すことはありません。 |
Outlook — 書き込み
ツール | 説明 |
W | インラインで作成した内容または下書きから、メッセージをすぐに送信します。Graph が配信を受領して ID を返さないため、このツールは「配信済み」ではなく「受理済み」として報告します。 |
W | 既存のメッセージに返信、全員返信、または転送を 1 つのステップで行います。自分のテキストは、引用された元のメッセージより上に配置されます。 |
W | 最初から作成するか、元のメッセージを引用済みの返信・転送として、未送信の下書きを作成します。下書き ID を返します。 |
W | 未送信の下書きの件名、本文、受信者を編集します。下書きに対してのみ動作します。 |
W | メッセージを別のフォルダーへ移動またはコピーします。移動するとメッセージ ID が変わります — 新しい ID が返され、古い ID は使えなくなります。 |
W | メッセージを削除します。 |
W | 既読・未読の設定、フラグ付け、カテゴリ分類、重要度の設定を最大 20 件のメッセージに一度に行います。 |
W | メール フォルダーの作成、名前変更、移動、削除を行います。 |
W | 下書きにファイルを添付します。3 MB 未満はインライン、最大 150 MB までは分割アップロード セッションで対応します。 |
Teams
ツール | 説明 |
| 自分の所属するチーム、または 1 つのチーム内のチャンネルを一覧表示します。他の Teams ツールが必要とする ID に、チーム名やチャンネル名を解決するための手段です。 |
| 自分のチャット(1 対 1、グループ、会議)を直近のアクティブ順に表示します。1 対 1 チャットには独自の名前がないため、参加者から名前が作成されます。 |
| チャンネルまたはチャットからメッセージを読み取ります。チャットは日付範囲に対応していますが、Graph のチャンネル API が日付フィルターを受け付けないため、チャンネルには対応していません。 |
W | 自分自身として、チャンネル、チャンネル スレッド、またはチャットに投稿します — メールで人に投稿し、1 対 1 チャットを見つけるか作成することも含みます。 |
| 閲覧可能なすべてのチャットとチャンネルを対象にしたキーワード検索です。Teams を検索する唯一の方法であり、一覧表示 API には検索機能が一切ありません。こちらも** |
SharePoint と OneDrive
ツール | 説明 |
| SharePoint と OneDrive 全体、または 1 つのサイトやライブラリ内でファイルを検索します。KQL 用語( |
| サイトを検索するか、サイトのドキュメント ライブラリとドライブ ID を一覧表示します。SharePoint 作業の出発点です。 |
| OneDrive またはドキュメント ライブラリ内のフォルダを、ドライブ + アイテム ID、パス、または自分の OneDrive のルートで一覧表示します。 |
| 1 つのファイルまたはフォルダの詳細を取得します — 貼り付けた共有 URL からも取得し、それを解決します。オプションでアクセス権を持つユーザーを報告します。 |
| ファイルをプリサイン URL としてダウンロードします。オプションで出力時に PDF に変換されます。インラインでは返しません。 |
W | リンク共有またはユーザーを招待して共有します。テナントの共有ポリシーが依頼内容を静かに縮小することがあるため、実際に付与されたアクセス権を報告します。 |
コア
ツールと | 説明 |
| 自分が誰としてサインインしているか、Microsoft 接続が有効かどうかと最終更新時刻、付与された権限、利用可能なツール、承認したうえで現在実際に使える共有メールボックス、そしてそれぞれについて、呼び出しが自分として実行されるかサービスアカウントとして実行されるかを表示します。何か失敗したときは、まずこれを呼び出してください。 |
設定
すべて環境変数のみです。公式の一覧は src/config.ts 内の Env 型です。
Entra アプリの登録
変数 | 必須 | 説明 |
| 必須 | テナントのGUIDまたは検証済みドメイン。すべての呼び出しはこの特定のテナントを対象とします。 |
| 必須 | アプリケーション(クライアント)ID。登録時に使用するプラットフォームの種類は Web である必要があります。 |
| いずれか1つ | クライアントシークレット。最も簡単な方法ですが、Entraでは有効期間が最大24か月に制限されます。 |
| いずれか1つ | 証明書クライアント認証用のPKCS#8 PEM秘密鍵(リテラルまたはbase64)。本番環境ではこちらが推奨されます。 |
| 証明書利用時 | ポータルに表示されるとおりの16進数SHA-1サムプリント。Entraはサムプリントによってアサーションと証明書を照合するため、両方の指定が必須です。 |
| マルチテナントで実行する際に受け入れるテナントIDのカンマ区切りリスト。サインイン時と、毎回のリクエスト時にもチェックされます。ここからテナントを削除すると、既存の接続は次のログイン時ではなく、直ちにロックアウトされます。 |
キー
変数 | 必須 | 説明 |
| 必須 | 当サーバーのMCPアクセストークンに署名するRS256キーのBase64 PKCS#8 PEM。 |
| 必須 | 対応する公開キーのBase64 SPKI PEM。 |
| JWKSとトークンヘッダー内のキーID。キーペアのローテーションに合わせて変更してください。デフォルトは | |
| 必須 |
|
| 同じ形式の使用を終えたキーをカンマ区切りで指定し、復号化のみに受け付けます。これにより、キーのローテーションは特定の日にすべてを切り替えるのではなく、段階的に行う運用になります。 |
ストレージ
変数 | 必須 | 説明 |
| 必須¹ | 暗号化したリフレッシュトークン(TTLなし)とキャッシュ済みアクセストークン(TTLあり)のためのDynamoDBテーブル。資格情報に対して専用のIAM境界とバックアップポリシーを適用できるよう、OAuthテーブルとは意図的に分離しています。 |
| セルフホスティングや開発用のJSONファイルフォールバック。 | |
| 必須¹ | 自サーバーのOAuth状態 — 登録済みクライアント、ログイン状態、認証コード、リフレッシュトークン用のDynamoDBテーブル。 |
| 同じ状態のJSONファイルフォールバックです。 | |
| ユーザーとポリシー用のDynamoDBテーブルで、 | |
| セルフホスティングと開発用のJSONユーザーストアです。 | |
| ユーザーストアを回避する旧式の単一管理者ベアラ。スモークテスト用に便利です。これには自身のGraph接続が存在しないため、サインイン済みのユーザーに対応付かない限り、Graphツールは再接続エラーを返します。 |
¹ または、ローカル開発では対応する _FILE のバリアント(MCP_GRAPH_FILE / MCP_OAUTH_FILE)を使用します。
Graph の動作
変数 | デフォルト | 説明 |
|
|
|
| derived | スペース区切りの空です。読み込み時に検証されます: |
|
| 必要な機能が存在しないソブリン クラウドだけを変更してください。 |
|
| Outlook 呼び出しに |
|
|
|
|
| リクエストごとのタイムアウト。クライアントのツールタイムアウト(300秒)より十分短く、応答が遅れないようにします。 |
|
| (アプリケーション、メールボックス)あたりの実行中リクエストの上限。Exchangeは正確に4つまでしか許可せず、この値はそこへ固定されます。ここで増やしても単に輻輳を429に変換することになるだけでです。 |
|
| Microsoftは、特定情報のないトラフィックの優先度を低くします。ドキュメント通りの形を維持し、真ん中のフィールドに自社名を入れましょう。 |
| auto |
|
ゲートと出力
変数 | 既定値 | 説明 |
|
| アプリ専用モードのマスタースイッチ。 |
| 空 | アプリの資格情報でアクセスできるメールボックスアドレスのカンマ区切りリスト。ワイルドカードは即座に拒否されます。 |
| 空 | アプリ専用アクターで行われるすべてのサイトまたはドライブ指定の呼び出しに対して強制される、アプリ資格情報でアクセス可能なサイトIDまたはURLのカンマ区切りリスト。空のリストは、アプリ専用がSharePointに一切アクセスできないことを意味し、サイトを指定しないアプリ専用の呼び出しは、許可ではなく拒否されます。照合は大文字と小文字を区別せず、完全一致または |
|
| ユーザーごとのポリシーに加えて、 |
|
|
|
| ダウンロード用のS3バケット。すべてのダウンロードツールで必須です。設計上、base64にフォールバックはありません。 | |
|
| 署名付きURLの有効期間。署名なしでファイルを取得できるため、短くしてください。 |
|
| アーティファクトバケットのリージョンを上書きします。 |
| 標準エラー出力に加えて、監査・セキュリティ記録用のJSONLシンク。セルフホストデプロイメント向け。Lambdaでは、標準エラー出力はすでにCloudWatchに到達します。 | |
| 未設定 |
|
|
| 通常のNodeエントリポイント用のリッスンポート。LambdaとAzure Functionsでは使用されません。 |
共有メールボックスへのアクセス
この設計はこの機能を中心に構築されており、その機能自体はこのサーバーではなくテナントに依存します。
Graphがそもそもこの操作を実行するには、2つの条件が両方とも真である必要があります。 接続には、委任された .Shared Graph スコープ(Mail.Read.Shared、Mail.ReadWrite.Shared、Mail.Send.Shared — すべて work スコーププロファイルに含まれます)が必要です。また、Exchange Onlineは、サインイン中のユーザーにターゲットメールボックスへの権限を付与している必要があります。スコープはあくまで機能を可能にするだけで、実際のゲートはExchangeです。 Exchange側で権限がなければ、同意がどうであってもGraphは403を返します。
管理者は、Exchange管理センター(受信者 → メールボックス → 共有メールボックス → 委任)で、次の1つ以上を付与します。
権利 | 有効になること | 効果 |
完全アクセス(Full Access) | メールボックス内の読み取り、一覧表示、移動、削除、下書き | そのメールボックスに対するすべての読み取り・書き込みツールで必要です。送信時に共有メールボックスの送信済みアイテムにコピーを残す場合にも必要です。 |
別名で送信(Send As) | 共有メールボックスを送信者として送信 | 受信者には共有メールボックスのみが表示されます。 |
代理送信(Send on Behalf) | メールボックスの代理として送信 | 受信者には「ユーザー が 共有メールボックス に代わって」と表示されます。ユーザーはOutlookでこれを自分自身に付与できます。 |
権限を付与してから反映されるまで、最大1時間かかることがあります。 付与直後の403は通常そのためであり、サーバーはエラーにその旨を明記します。
それら2つの条件に加えて、このサーバーにはさらに2つの条件があります。ユーザーが接続時にメールボックスを承認していること、および管理者の allowedMailboxes 上限がそれを許可していることです。両方とも、Graphに何か問い合わせる前にチェックされます — 詳しくは メールボックスの承認 を参照してください。
そして、アドレスを渡すだけです: o365_mail_list({ mailbox: "support@contoso.com", unreadOnly: true })。サーバーはアクターを解決し、/users/support@contoso.com/… を呼び出します。/me による呼び出しはありません — 共有メールボックスへの /me パスは存在しないためです。また、接続時にアドレスがユーザーによって承認されている必要があります。下記の メールボックスの承認 を参照してください。
最初に知っておくべき制約が2つあります。ユーザーが権限を持つメールボックスを列挙するGraph APIは存在しません — そのため、このAPIでは入力されたアドレスを一覧表示するのではなく、承認ページで検証しています。また、呼び出し元はツール呼び出しでメールボックスを指定する必要があります。また、署名されたユーザーは通常、自分のライセンス済みメールボックスが必要です。ただし、共有メールボックス自体にはライセンスは不要です。
さらに絞り込む。 policy.allowedMailboxes は、ユーザー自身が承認した内容の上に設定される管理者の上限です。有効なアクセス権利は、その両方の積集合です。デフォルトは "*" で、残りの判断は本来あるべきExchangeに委ねます。これを明示的なリストに設定すると、ユーザーをExchange上位料権限以下に絞り込めます。あるいは null に設定すると、mailbox 引数を即座に拒否します。
npm run user -- add alice --mailboxes=support@contoso.com,billing@contoso.comメールボックスの承認
アドレスは手入力されます。Microsoft には、特定の人が開くことのできるメールボックスを一覧にするAPIはありません。また、相手先から推測すると、リストがほとんど間違えてしまうため、このページは推測を行いません。このページが実際に行うのは検証です。入力されたすべてのアドレスは、承認される前にExchangeでチェックされ、理由とともに可能なもの「利用できる/利用できない」として表示されます。
サインインしているユーザー自身のメールボックスもそのリストの通常の候補として表示され、削除できます。共有サポートメールボックスを中心に構築されたアシスタントは、オペレーターの個人用受信トレイのを読む必要はないため、その明示的に可能にする必要があります。これに基準では、別の承認済みメールボックスを指定しない呼び出しは、すべてのOutlookルのツールで拒否されます。Teams と SharePoint は影響を受けません。両方誰も、メールボックスゲートを通過しないためです。
Microsoft はこの付与(grant)を制限できないため、このサーバーが制限します。 Entra は、委譲された Mail.*.Shared に対してメールボックスごとの同意(consent)を提供していません。ユーザーがそれらのスコープを付与した瞬間、結果として得られるトークンは、Exchange がその人に開くことを許可したすべてのメールボックスを開くことができ、Microsoft 側でそれを絞り込む手段はありません。SharePoint は 2024 年に委譲された Sites.Selected を獲得しましたが、Exchange には同等のものもロードマップ上の項目もありません。したがって、下記の承認ページは単なる儀礼ではなく、この付与を制限する唯一のものです。そしてそれは サーバー側で、すべてのリクエストで、Graph 呼び出しの前に 強制されます(src/users.ts の policyAllowsMailbox。resolveActor から到達します)。
フロー。 /oauth/authorize → Entra サインイン → /oauth/callback はシールされた更新トークンを保存し、MCP クライアントに認可コードを渡す代わりに、一度きりのチケット(15分)付きで /oauth/consent へリダイレクトします。ページには、ユーザー自身のメールボックス(常に含まれ、削除不可)に加え、候補となる共有メールボックスが表示されます。各メールボックスは事前にプローブされているため、開けないアドレスは、後で受け付けられて失敗するのではなく、理由付きでグレーアウト表示されます。ユーザーはこのアシスタントが使用してよいものにチェックを入れ、そのとき初めて認可コードが発行されて MCP クライアントはホームへリダイレクトされます。再接続すると、前回の選択があらかじめチェックされた状態でページが再実行されますが、これはユーザーが後でメールボックスを 削除する 方法でもあります。
プローブが見えないもの。 Inbox の 403 には理由が3つあり、そのうち「まったくアクセスがない」はその一つだけです。Send As のみを持つユーザー、またはメールボックス全体ではなく 単一のフォルダー へのアクセスを与えられたユーザーは、その狭いアクセスで目的の操作には十分であっても、Inbox のプローブには失敗します。ページはメールボックスが表示される場所でその旨を説明しています。正直な要約としては、プローブは大げさに報告するのではなく少なめに報告するということです。偽陽性(false positive)を出すことは決してありません。プローブが 200 を返すアドレスは、サーバーが実際にアクセスできるアドレスです。
2つのゲート、両方ともサーバー側。
APIキーのサービス アカウントはページを表示しません。 ブラウザも人間もないため、consent ゲートは適用されません。これは意図的な仕様です。キーを作成した管理者が同意当事者であり、allowedMailboxes が単独でその同意を制御します。この区別は、ID に Entra の oid があるか、つまりブラウザーでのサインインを経由したことがあるかどうかで決まります。
App-only mode
アプリ専用モードが存在するのは、1つの状況のためだけです。それは、誰もサインインせず、誰にも委任されていないものの、エージェントがトリアージすべきメールボックスです。このモードはユーザーの資格情報ではなく、アプリケーション自身の資格情報を使用するため、サインイン中のユーザーは存在せず、/me は無効です。
これはデフォルトではオフで、必要でない限りはオフ状態を維持してください。 管理者が同意したアプリケーションの Mail.ReadWrite は、組織内のすべてのメールボックスにアクセスを許可するからです。オンにするには、O365_APP_ONLY_ENABLED=true であること、メールボックスが O365_APP_ONLY_MAILBOXES にリストされていること、呼び出し元ユーザーのポリシーに allowAppOnly があることという、3つの独立した条件が必要です。許可リストに含まれているが、呼び出し元に allowAppOnly がないメールボックスは、単に delegate にフォールバックして Exchange の応答に委ねられます。アプリ専用に昇格(escalation)しても、2つのメールボックスゲートはスキップされません。resolveActor はアプリ専用のブランチを考慮する前に、それらのゲートを適用するので、サインイン中のユーザーも、そのアドレスを承認ページ上で承認している必要があります。典型的なアプリ専用の呼び出し元は APIキーのサービス アカウントであり、そのアカウントがプロンプトで要求されることは決してないため、allowedMailboxes が全体の制御になります。
O365_APP_ONLY_MAILBOXES は制御の半分にすぎず、しかも弱い半分です。 これは このコードが何を要求するかを制約するだけです。資格情報そのものには何も影響しません。それを入手した人は、テナント内のすべてのメールボックスにアクセスできます。実際の制御は、テナント側で適用される Exchange RBAC for Applications です。deploy/entra/scope-app-only.ps1 がそれをスクリプト化しています: Exchange でサービス プリンシパルを登録し、メールが有効なセキュリティ グループに対して管理スコープを生成し、そのスコープに制限された Application Mail.* ロールを割り当て、Test-ServicePrincipalAuthorization で検証します。
この罠が全体を無効化します: RBAC の付与は Entra の付与と 加算的(additive) です。スコープのないアプリの永続化された、アプリ登録に同意されたまま残っている場合、その和集合が適用され、スコープ設定は何の意味もありません。Entra 側のアプリの許可コンセントは削除する必要があります。また、権限キャッシュにも考慮してください: 変更には 30分~2時間 かかります(Test-ServicePrincipalAuthorization はキャッシュをとばすため、スクリプトはそれで終わるのです)。
Teams には、ここにはアプリ専用モードのパス(path)は存在しません。以下の通り。
ユーザーごとのツール権限
ユーザーストアが設定されると (MCP_USERS_TABLE、また開発時は MCP_USERS_FILE)、すべてのユーザーレコードは policy を持ちます:
フィールド | 意味 |
|
|
| ツールごとの |
| その上に、変更(mutating)とマークされたすべてのツールで必要です。管理、CLIではありません。 |
| コネクト時にメールボックスの同意ページでユーザーが承認したもの。欠落している場合は、承認が実施されず、そのユーザー自身のメールボックスのみにアクセスできます。 |
| その上に設ける管理者の上限: |
| アプリ専用に対してのユーザーごとのゲート。デフォルトは |
| ユーザーごとの呼び出しレート制限。デフォルトは 60。 |
| アイデンティティを削除せずに無効にします。 |
ユーザーに呼び出せないツールは、tools/list からも非表示にするため、モデルはそのツールを認識しません。OAuth ログインごとに、マップがライブなレジストリと照合されます: 新しいツールは false として追加され、破壊的な可能性のある新しいツールが黙って付与されことはありません。削除されたツールは取り除かれます。ストアは変更があったときのみ書き込まれます。
新しくプロビジョニングされたユーザーには、読み取り専用ツールはすべて有効、変更ツールはすべて無効という状態で付与されます。
管理者CLI
npm run user -- list
npm run user -- add alice --writes --mailboxes=support@contoso.com --app-only
npm run user -- tools alice # the effective 27-tool map
npm run user -- tools alice --enable=o365_mail_send
npm run user -- rotate alice # new API key, old one dead
npm run user -- disable alicenpm run connection -- list # who is connected, scopes, last refresh — never token material
npm run connection -- test alice@contoso.com # one live Graph call, proves the stored credential still redeems
npm run connection -- revoke alice@contoso.com # server-side kill switch: delete the row, purge cached tokens
npm run connection -- rewrap # re-seal every stored secret under the current encryption keyconnection revoke は、このサーバーが権限を持つことを承認します。テナント側の決定権のある kill switch は、Entra のユーザーオブジェクトの Revoke Sessions です。パスワードを変更するだけでは、デフォルトの登録済みクライアントの更新トークンを終了できません(SECURITY.md の失効マトリックスを参照してください)。
制限と既知の課題
AWS では、
WWW-Authenticateチャレンジヘッダーが再名称されます。 Lambda の Function URL が それをx-amzn-Remapped-WWW-Authenticateに書き換え、この関数内ではそれを防止することはできません。 あなたのディスカバリーには影響しません: MCP 仕様は、クライアントが/.well-known/oauth-protected-resource/mcpを直接取得するように (later, the root variant) 、そしてそれからルートバリアントを直接を取得するフォールバックを要求し、 そして参照SDKは 401 で、条件に関係なくそれを行います。そのため、このサーバーは 上記の両方のドキュメントを提供し、MCP エンドポイント URL とバイト同一のresource値を返します。本当にこのヘッダーを必要とするクライアントに遭遇した場合、CloudFront を前に置き、 その方の上にある Lambda@Edge の origin-response 関数で、リマップされた名前を元にコピーします。 viewer-response 関数では動作しません。なぜなら、オリジンが400以上を返したときに、 CloudFront はそれらを呼び出さないためです。
平たく言うと、これらは、他の状態とバグとして扱われてしまうことのために存在します。(Note: These were written to be explicitly plain.)
更新トークンは、ランダムな方法で消えていきます。 90日のウィンドウはスライドする非アクティビティであり、固定された有効期限ではありません。毎週接続するユーザーは事実上失効しない一方、91日間クセスしないユーザーは戻った時には切れています(AADSTS 70008 / 700082)。また、条件付きアクセス(Conditional Access)のサインイン頻 rate は独自の要求で再認証を強制し、サーバー側のコードでそれを防ぐことはできません。管理者が Entra の管理画面でパスワードを続行すると、すぐにトークンが失効します。ユーザーが自分のパスワードをリセットした場合は失効しません。これらはどれも、再接続先 URL を付けた、1 つの明快なツールエラーとして現れます。
クライアントシークレットの有効期限が切れるのは、崖のようなものです。 Entra はシークレットの寿命を24か月に制限しており、期限が切れた場合、そのデプロイの全ユーザーが、一度にAADSTS7000222 で失敗します。徐々にではならありません。証明書資格情報はこのフェ・イルモードを避けます。それらのいずれかに、かなり早めに期限日をローテーションしてください。
暗号化キーを失うと復元は不可能です。 キーがなければ、保存された接続はないため、全員が同時に再サインインする必要があります。別途バックアップし、O365_TOKEN_ENC_KEYS_PREVIOUS と connection 回ります でローテーションしてください。決して置き換えないでください。
Teams への送信は委任のみです。 すべての Graph の送信端ポイントは、Teamwork.Migrate.All を唯一のアプリケーション許可として提供しており、Microsoft はそれを「移行」のスコープにだけ許可を。関数で、Teams のメッセージを botFramework ボットまたは Teams アプリパッケージを使用する必要があり、どちらも、独立したリモート MCP サーバーに適合しません。Teams の メーター は対象外ではありません: model A / model B の課金制は、大部分の既存ドキュメントがまだ書いているものの、2025 年 8 月 25 日に終了しました。
Teams チャネルスコープには、テナント管理者の同意が必要です。 特に ChannelMessage.Read.All は自己同意できません。管理者権限のないセルフホスターが O365_SCOPE_PROFILE=personal で実行すると、メール・ファイル・チャットは正常に動作し、チャネルツールは不可解な 403 ではなく説明付きのエラーとして失敗します。personal は User.ReadBasic.All も省くため、会話の外の人物への @ メンションは解決できません。メッセージ自体は送信されますが、名前は本文にプレーンテキストとして残り、ツールは「相手に通知されませんでした」という警告を返します。
メールボックスの承認ページは、実際より少なく表示することはあっても、実際より多く表示することはありません。 このページは、Inbox を開いてみることでメールボックスを使用できるかどうかを判断します。403 が返る原因は3つあります。Send As のみを持つ場合、またはメールボックス全体ではなく1つのフォルダへのアクセスしか持たない場合、そのアドレスは利用不可と表示され、チェックを付けることができません。たとえ、その限定された権限でもやりたい操作が実行できたとしてもです。逆の誤りは起こりません。チェックを付けられるアドレスは、サーバーが実際に開けるものです。
検索には、データ損失のように見える厳しい上限があります。 Outlook の $search は最大1,000件の結果を返し、フィルターやカスタム並べ替えとは組み合わせられません。Teams の検索は合計件数ではなくページ数を返すため、一致件数として提示することは絶対にできません。SharePoint のディープページングは1,000件を超える結果では停止します。また、アプリ専用の検索は、既定では個人用 OneDrive のコンテンツを除外します。これを有効にすると新しい索引がプロビジョニングされ、数日から1週間かかることがありますが、その間、エラーは一切出ず、結果は静かに不完全なままです。
テナントの共有ポリシーは o365_files_share が生成したものを黙って書き換えます。 組織レベルとサイトごとの設定により、匿名リンクが組織内のみにダウングレードされたり、有効期限が強制されたり、リンクが閲覧専用に変えられたりすることがあります。さらに悪いことに、createLink は (アプリ、リンクタイプ) ごとに冪等です。そのため、新しい7日間のリンクをリクエストしても、数年も前に作成され、期限切れなく、スコープの違うリンクが返ることがあります。このツールは常に実際の許可を読み戻して報告します。これが唯一の防御手段です。
メッセージ ID は、メッセージが移動すると変わり、Teams の ID はグローバルに一意ではありません。 まさにこの理由で O365_IMMUTABLE_IDS は既定でオンになっています。ただし、これは実質的に一方通行のドアです。ある形式で発行された ID は別の形式では使えず、稼働中の環境で切り替えると ErrorInvalidIdMalformed が発生します。一方、Teams のメッセージ ID は、そのチャットまたはチャネル内でのみ一意です。そのため、メッセージ ID は常に会話の座標と一緒に返されます。
日々の運用で最も発生しやすいのはスロットリングです。 Outlook は (アプリ、メールボックス) ごとに同時4リクエストと10分間で10,000リクエストを許可し、Teams は、各チャネル、各チャット、ごとにユーザーそれぞれ1秒間にだいたい1リクエストを許可します。SharePoint は権限呼び出しごとに5つのリソースユニットを消費し、Graph のほかの分野よりも検索をずっと厳しく制限します。バッチ処理でも回避できません。Graph がバッチ内から Outlook へ並行転送るのは最大4つのサブリクエストだからです。このサーバーは自身のファンアウトを制限して Retry-After を厳守しますが、それでも積極的なエージェントであれば、いずれ 429 に遭遇することになります。
3 MB を超える添付ファイルは、共有メールボックスでは動作しません。 Microsoft の公式ドキュメントによると、委任された呼び出し元が共有または委任メールボックス内のメッセージに大きいファイルを添付した場合、403 が返るされます。3 MB 未満なら問題ありません。このツールは、むき出しの 403 を出すだけでなく、その旨を説明します。
ソブリンクラウドには、機能ません。機能が欠落上がる。 完全削除、チャットのデルタ クエリ、Teams のエクスポート API は、米国政府向け L4/L5 および中国の 21Vianet では利用できず、地域またりのサイトへのアクセスは、アプリの権限とは無関係の理由で失敗することがあります。マルチジオテナントでは1つのリージョンごとに検索リクエストが必要です。それなドがないと、他リージョンの完全な状態は静かに欠落します。
更新トークンのローテーションの競合は、無害ですが実際に発生します。 Entra は、認可引き換えのたびに新しい更新トークンを発行し、古い更新トーンを失効させません。つまり、同一ユーザーに対する同時実行の2つの呼び出しが、それぞれ有効な後継トークンを受信します。条件付き書き込みがあるため、一方が勝者となり、他方は自身のコピーを破棄します。競合に負けても、ツール呼び出しが失敗することはありません。この程度の競合は「アクセストークンのキャッシュ」があるからこそ稀にあります。
ロードマップ
これは見落としではなく、意図された v1 のスコープ削減です:
カレンダー。 メールの次に誰もが期待する機能です。
Calendars.ReadWriteはユーザーの自己同意が可能で、共有メールボックス用に既存のアクターモデルは、共有カレンダーにもそのまま適用できます。次に着手します。ファイルのアップロード OneDrive と SharePoint への対応。v1 は検索、一覧、取得、ダウンロード、共有までをカバーしています。
連絡先 / 人名の参照 で、名前をメールアドレスに解決します。現在の送信ツールは、呼び出し元が既にメールアドレスを持っていることを前提としています。
受信トレイルール ( の
messageRules。 **MailboxSettings.ReadWriteが必要) を使ったサーバーサイドの自動仕分け基盤。
また、現在検証中のもの: stderr だけに依存するプラグ可能な監査シンク (Firehose → S3 → Athena)、長期間有効なシークレットを一切不要にする AWS 上での3種類目のクライアント資格情報としてのワークロードIDフェデレーション、そして生の @odata.nextLink 文字列の代わりにサーバー側発行の不透明なページハンドルなどです。
ディレクトリ管理は意図的に対象外です。Microsoft の無料の MCP Server for Enterprise が、読み取り専用の Entra クエリをすでにカバーしているからです。
コントリビューション
CONTRIBUTING.md をご参照ください。セキュリティの問題は SECURITY.md をご覧ください — 公開の issue を開かないでください。
ライセンス
MIT。 LICENSE を参照してください。
This server cannot be installed
Maintenance
Resources
Unclaimed servers have limited discoverability.
Looking for Admin?
If you are the server author, to access and configure the admin panel.
Related MCP Connectors
Copilot connector permission audits with owner signoff receipts.
*Updated June 17th 2025** Manage your Microsoft 365 services effortlessly. Create and manage distr…
Governed email for AI agents (Mailbuttons / mbag.ai): sandbox inboxes, policy gate, audit log.
Latest Blog Posts
- Who's Calling? MCP Hosts Are an Identity Blind Spot (And the Spec Knows It)By Om-Shree-0709 on .mcpAgent IdentityOAuth 2.1
- Your AI Chatbot Just Exposed Your CEO's Salary to an InternBy Om-Shree-0709 on .Agent IdentityMCP SecurityOAuth Delegation
- Why MCP Servers Need Execution Sandboxing (And Why Your Current Stack Isn't Enough)By Om-Shree-0709 on .Agentic AiPrompt InjectionWebAssembly
MCP directory API
We provide all the information about MCP servers via our MCP API.
curl -X GET 'https://glama.ai/api/mcp/v1/servers/LotzerDigital/aws-office365mcp'
If you have feedback or need assistance with the MCP directory API, please join our Discord server