Skip to main content
Glama

日本語訳

office365-mcp

Microsoft 365 向けのマルチユーザー・リモート MCP サーバー — Outlook メール、Teams、SharePoint/OneDrive を Microsoft Graph 経由で操作します。各ユーザーは通常のブラウザーサインインで自分の Microsoft アカウントを接続します。サーバーはその認可を暗号化したまま保持し、以降のツール呼び出しではすべてそのユーザーとして動作します。したがって、一度接続すれば、クライアントが Microsoft トークンを保持することなく動作し続けます。共有メールボックスとサービスメールボックス(support@billing@info@)は、一部のツールに付属のパラメーターではなく、第一級の ID として扱われます。

1 つの TypeScript コードベース、3 つのデプロイ先:

プラットフォーム

エントリーポイント

ビルド / デプロイ

AWS Lambda (Function URL)

src/entries/lambda.ts

npm run build:lambda && npm run deploy:lambda

まず 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 Microsoft

gen: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 関係性が別々にあります。この区別を保つことが全体の設計です。

  1. MCP クライアント ↔ このサーバー。 私たちが認可サーバーです。クライアントは動的に登録(RFC 7591)し、/oauth/authorize/oauth/token に対して認可コード + PKCE フローを実行し、だけ、当サーバーが署名した RS256 JWT を受け取ります。Entra は動的クライアント登録をサポートしておらず、この交換を一切行いません。

  2. このサーバー ↔ 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.これは、変更可能で管理者が制作を確認できる emailpreferred_usernameupn ではありません。

この間、/oauth/callback一時停止します。Microsoft の認可が保存されると、MCP クライアントに認可コードを渡しません。代わりに、Useに 1 回限りのテケト付きで /oauth/consent にの不相続き、ユーザーはこのアシスタントがアクセスできるメールバックの数を選択します。ユーザーがそのページを送信したとき onlyに、コードが発行され、クライアントはリダイレクトでた戻ります。メールバック承認 を参照してください。

テナント可能リスト (O365_ALLOWED_TENANTS) は、サインイン時だけでなく、すべての リクエストで再度チェックされるため、テナントを削除すると、次のログインではなく、ただちに効果があります。

委任 (delegated) とアプリ専用 (app-only)

すべての Graph 呼び出しは、設定から決定的な方法でまず actor を解決します。モデルがサーバーを説得して権限昇格などできないのです。

Actor

Address

オーダー

Units

delegated-self

/my/yks

mailbox 引数がない場合、または呼び出し元の自分のアドレスを渡した場合

サインインしたユーザーが確認できることと完全に同じ

delegated-shared

/users/{upn}

別のメールバドであり、接続時にユーザーが承認し、管理者ポリシーが許可し、呼び出し元が Exchange 権限を保持している場合

Exchange がそのメールバド上のそのユーザーに与えているもの

app-only

/users/{upn}

メールバが O365_APP_ONLY_MAILBOXES に含まれ、アプリ専用が有効で、呼び出し元のポリシーが allowAppOnly を持つ場合

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)

src/entries/lambda.ts

npm run build:lambda && npm run deploy:lambda

まず 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 Microsoft

gen: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 の関係があり、この区別を維持を保つことが全体の設計です。

  1. MCP クライアント ↔ このサーバー。 ここでは私たちが認可サーバーを務めます。クライアントは動的に登録 (RFC 7591) し、/oauth/authorize/oauth/token に対して認可コード + PKCE フローを実行し、私たちが署名した RS256 JWT を受け取ります。Entra は動的クライアント登録をサポートしておらず、このやり取りには一切関与しません。

  2. このサーバー ↔ Entra。 私たちは 1 つの静的な Web リダイレクト URI を持つ機密クライアントです。ユーザーのサインイン中に、私たちは Entra に対し独立したPKCE チェーンを実行し、クライアントシークレットまたは証明書でコードを交換し、id_token、Graph アクセストークン、追加の refresh_tokenoffline_access を要求するため)を受け取ります。

この更新トークンこそ成果物です。それは、{tid}:{oid}:refresh という追加認証データに紐付けられて AES-256-GCM でシールされ、他のユーザーのレコードに転送できない値として、資格情報専用のテーブルに格納ワードです。それ以降の呼び出しはすべて、JWT → (tid, oid) → キャッシュされたアクセストークン、または更新トークン引き換え → Graph、という順路になります。ユーザーのキーは id_token からの不変な組み合わせ (tid, oid) のみで、emailpreferred_usernameupn は変更可能で管理者が制御できるため、使用しません。

この 2 つの関係範囲の間で、/oauth/callback座標します。Microsoft の認可が保存されると、MCP クライアントに認証コードを渡しません。代わりに、ワンタイムチケット付きの /oauth/consent へブラウザを をリダイレクトし、ユーザーはこのにするアシスタントが操作できるメールボックスを選択します。そのページでユーザーが送信した場合に限ってコードが発行され、クライアントは元のアプリ名へ戻されます。メールボックス承認 を参照。

テナント許可リスト (O365_ALLOWED_TENANTS) は、サインイン時だけでなくすべてのリクエストで再検証されるため、テナントを削除すると次のログイン待たずにすぐに効果があります。

委任(delegated)とアプリ専用(app-only)

すべての Graph 呼び出しは、設定から決定的にアクターを解決します。モデルがサーバーを騙して特権を引き上げることはできません。

Actor

Address

When

Sees

delegated-self

/me

mailbox 引数がない場合、または呼び出し元の自分のアドレスの場合

サインイン済みユーザーがそのまま見える内容

delegated-shared

/users/{upn}

別のメールボックスを指し、接続時にユーザーが承認し、管理者ポリシーが許し、呼び出し元が Exchange 権限を持っている場合

Exchange がそのメールボックス上のそのユーザーに権限を付与している範囲

app-only

/users/{upn}

メールボックスが O365_APP_ONLY_MAILBOXES に含まれ、アプリ専用が有効であり、呼び出し元のポリシーが allowAppOnly を持っている場合

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 — 読み取り

ツール

説明

o365_mail_search

Outlook の検索構文(from:subject:attachment:hasAttachments:true など)を使用してメールボックスを全文検索します。常に日付順に並べ替えられ、Microsoft により最大 1,000 件に制限され、フィルターと組み合わせることはできません。

o365_mail_list

フォルダー内のメッセージを構造フィルターと並べ替えで一覧表示します — 未読のみ、送信者、日付範囲、順序など。o365_mail_search の対になるもので、キーワード一致ではなく正確なフィルタリングを行います。

o365_mail_get

メッセージ 1 通分を完全に取得し、本文はプレーン テキストで返します。オプションでインターネット メッセージ ヘッダーと添付ファイルのメタデータを含みます。長い本文は切り捨てられ、元の長さが報告されます。

o365_mail_folders

メール フォルダーをトップ レベルまたはツリー全体で一覧表示し、メッセージ数と未読数を示します。メッセージを移動する前にフォルダー ID を解決するために使用します。

o365_mail_attachments_list

1 つのメッセージの添付ファイル名、種類、サイズ、インライン フラグを一覧表示します。メタデータのみで、ファイルの内容は返しません。

o365_mail_attachment_download

添付ファイルをダウンロードし、有効期間の短いプリサイン URL を返します。インラインでバイト列を返すことはありません。

Outlook — 書き込み

ツール

説明

W o365_mail_send

インラインで作成した内容または下書きから、メッセージをすぐに送信します。Graph が配信を受領して ID を返さないため、このツールは「配信済み」ではなく「受理済み」として報告します。

W o365_mail_reply

既存のメッセージに返信、全員返信、または転送を 1 つのステップで行います。自分のテキストは、引用された元のメッセージより上に配置されます。

W o365_mail_draft_create

最初から作成するか、元のメッセージを引用済みの返信・転送として、未送信の下書きを作成します。下書き ID を返します。

W o365_mail_draft_update

未送信の下書きの件名、本文、受信者を編集します。下書きに対してのみ動作します。

W o365_mail_move

メッセージを別のフォルダーへ移動またはコピーします。移動するとメッセージ ID が変わります — 新しい ID が返され、古い ID は使えなくなります。

W o365_mail_delete

メッセージを削除します。trash(既定、復元可能)、soft、または permanent — 元に戻せず、加えて O365_ALLOW_PERMANENT_DELETE による制御が必要。

W o365_mail_flags

既読・未読の設定、フラグ付け、カテゴリ分類、重要度の設定を最大 20 件のメッセージに一度に行います。

W o365_mail_folder_manage

メール フォルダーの作成、名前変更、移動、削除を行います。

W o365_mail_attachment_add

下書きにファイルを添付します。3 MB 未満はインライン、最大 150 MB までは分割アップロード セッションで対応します。

Teams

ツール

説明

o365_teams_list

自分の所属するチーム、または 1 つのチーム内のチャンネルを一覧表示します。他の Teams ツールが必要とする ID に、チーム名やチャンネル名を解決するための手段です。

o365_teams_chats_list

自分のチャット(1 対 1、グループ、会議)を直近のアクティブ順に表示します。1 対 1 チャットには独自の名前がないため、参加者から名前が作成されます。

o365_teams_messages_list

チャンネルまたはチャットからメッセージを読み取ります。チャットは日付範囲に対応していますが、Graph のチャンネル API が日付フィルターを受け付けないため、チャンネルには対応していません。

W o365_teams_message_send

自分自身として、チャンネル、チャンネル スレッド、またはチャットに投稿します — メールで人に投稿し、1 対 1 チャットを見つけるか作成することも含みます。user 引数はありません。Teams はすべてのメッセージをサインイン中のユーザーに帰属させるため、user 引数を提供することはあり得ないなりすましを示すことになります。

o365_teams_search

閲覧可能なすべてのチャットとチャンネルを対象にしたキーワード検索です。Teams を検索する唯一の方法であり、一覧表示 API には検索機能が一切ありません。こちらも**user 引数はありません**/search/query はトークンの所有者にスコープされ、「別ユーザーとして検索」パラメータはありません。

SharePoint と OneDrive

ツール

説明

o365_files_search

SharePoint と OneDrive 全体、または 1 つのサイトやライブラリ内でファイルを検索します。KQL 用語(filetype:author:path:)に対応しています。

o365_files_sites

サイトを検索するか、サイトのドキュメント ライブラリとドライブ ID を一覧表示します。SharePoint 作業の出発点です。

o365_files_list

OneDrive またはドキュメント ライブラリ内のフォルダを、ドライブ + アイテム ID、パス、または自分の OneDrive のルートで一覧表示します。

o365_files_get

1 つのファイルまたはフォルダの詳細を取得します — 貼り付けた共有 URL からも取得し、それを解決します。オプションでアクセス権を持つユーザーを報告します。

o365_files_download

ファイルをプリサイン URL としてダウンロードします。オプションで出力時に PDF に変換されます。インラインでは返しません。

W o365_files_share

リンク共有またはユーザーを招待して共有します。テナントの共有ポリシーが依頼内容を静かに縮小することがあるため、実際に付与されたアクセス権を報告します。

コア

ツールと

説明

o365_whoami

自分が誰としてサインインしているか、Microsoft 接続が有効かどうかと最終更新時刻、付与された権限、利用可能なツール、承認したうえで現在実際に使える共有メールボックス、そしてそれぞれについて、呼び出しが自分として実行されるかサービスアカウントとして実行されるかを表示します。何か失敗したときは、まずこれを呼び出してください。

設定

すべて環境変数のみです。公式の一覧は src/config.ts 内の Env 型です。

Entra アプリの登録

変数

必須

説明

OAUTH_ENTRA_TENANT_ID

必須

テナントのGUIDまたは検証済みドメイン。すべての呼び出しはこの特定のテナントを対象とします。/commonはトークンキャッシュのミスを生み不要な再認証を引き起こし、クライアント資格情報では無効です。common / organizations にすると、デプロイがマルチテナントになります。

OAUTH_ENTRA_CLIENT_ID

必須

アプリケーション(クライアント)ID。登録時に使用するプラットフォームの種類は Web である必要があります。

OAUTH_ENTRA_CLIENT_SECRET

いずれか1つ

クライアントシークレット。最も簡単な方法ですが、Entraでは有効期間が最大24か月に制限されます。

OAUTH_ENTRA_CLIENT_CERT_PEM

いずれか1つ

証明書クライアント認証用のPKCS#8 PEM秘密鍵(リテラルまたはbase64)。本番環境ではこちらが推奨されます。

OAUTH_ENTRA_CLIENT_CERT_THUMBPRINT

証明書利用時

ポータルに表示されるとおりの16進数SHA-1サムプリント。Entraはサムプリントによってアサーションと証明書を照合するため、両方の指定が必須です。

O365_ALLOWED_TENANTS

マルチテナントで実行する際に受け入れるテナントIDのカンマ区切りリスト。サインイン時と、毎回のリクエスト時にもチェックされます。ここからテナントを削除すると、既存の接続は次のログイン時ではなく、直ちにロックアウトされます。common との組み合わせで空にすると、同意した任意のテナントが接続できます。つまり、サーバーはセキュリティ上の警告を起動時に大きな警告を発します。

キー

変数

必須

説明

OAUTH_SIGNING_KEY_PRIVATE

必須

当サーバーのMCPアクセストークンに署名するRS256キーのBase64 PKCS#8 PEM。npm run gen:oauth-key で生成します。

OAUTH_SIGNING_KEY_PUBLIC

必須

対応する公開キーのBase64 SPKI PEM。/.well-known/jwks.json で公開されます。

OAUTH_SIGNING_KEY_KID

JWKSとトークンヘッダー内のキーID。キーペアのローテーションに合わせて変更してください。デフォルトは primary です。

O365_TOKEN_ENC_KEY

必須

<kid>:<base64 32 bytes> — 保存されているすべてのEntraリフレッシュトークンとキャッシュされたアクセストークンを暗号化します。npm run gen:enc-key で生成します。失うと、保存済みの全接続が完全に壊れます。

O365_TOKEN_ENC_KEYS_PREVIOUS

同じ形式の使用を終えたキーをカンマ区切りで指定し、復号化のみに受け付けます。これにより、キーのローテーションは特定の日にすべてを切り替えるのではなく、段階的に行う運用になります。

ストレージ

変数

必須

説明

MCP_GRAPH_TABLE

必須¹

暗号化したリフレッシュトークン(TTLなし)とキャッシュ済みアクセストークン(TTLあり)のためのDynamoDBテーブル。資格情報に対して専用のIAM境界とバックアップポリシーを適用できるよう、OAuthテーブルとは意図的に分離しています。

MCP_GRAPH_FILE

セルフホスティングや開発用のJSONファイルフォールバック。MCP_GRAPH_TABLEが設定されている場合は無視されます。Lambdaでは利用できません。

MCP_OAUTH_TABLE

必須¹

自サーバーのOAuth状態 — 登録済みクライアント、ログイン状態、認証コード、リフレッシュトークン用のDynamoDBテーブル。expiresAt 上にTTLを入れています。

MCP_OAUTH_FILE

同じ状態のJSONファイルフォールバックです。npm run dev でAWSに頼らずに実際のブラウザー・サインインフローを実行できるようにします。MCP_OAUTH_TABLEが設定されている場合は無視されます。Lambdaでは、コンテナごとに状態が異なるコンテナを使ってしまうため利用できません。

MCP_USERS_TABLE

ユーザーとポリシー用のDynamoDBテーブルで、keyPrefix-indexoid-index というGSIを持ちます。

MCP_USERS_FILE

セルフホスティングと開発用のJSONユーザーストアです。MCP_USERS_TABLEが設定されている場合は無視されます。

MCP_SHARED_SECRET

ユーザーストアを回避する旧式の単一管理者ベアラ。スモークテスト用に便利です。これには自身のGraph接続が存在しないため、サインイン済みのユーザーに対応付かない限り、Graphツールは再接続エラーを返します。

¹ または、ローカル開発では対応する _FILE のバリアント(MCP_GRAPH_FILE / MCP_OAUTH_FILE)を使用します。

Graph の動作

変数

デフォルト

説明

O365_SCOPE_PROFILE

work

work には、共有メールボックス、SharePoint サイト、Teams チャネルのスコープが含まれ、そのうちいくつかは一度のテナント管理者同意が必要です。personal は、ユーザーが同意できるスコープのみ要求します。

O365_SCOPES

derived

スペース区切りの空です。読み込み時に検証されます: /.default は指定スコープとの混在はなく、(AADSTS70011) となり不可です。またoffline_access が必須です。

O365_GRAPH_BASE

https://graph.microsoft.com/v1.0

必要な機能が存在しないソブリン クラウドだけを変更してください。

O365_IMMUTABLE_IDS

true

Outlook 呼び出しに Prefer: IdType="ImmutableId" を送り、IDが移動をしても保持されるようにします。最初のデプロイ時に一度決定し、以後のスタックでは変更しないこと

O365_BODY_FORMAT

text

text はプレーンテキストの本文を要求します。これはモデル向けサーバーに合った挙動です。HTML 本文は、トラッキング関連のマークアップが大半を占めます。

O365_GRAPH_TIMEOUT_MS

30000

リクエストごとのタイムアウト。クライアントのツールタイムアウト(300秒)より十分短く、応答が遅れないようにします。

O365_MAX_CONCURRENCY_PER_MAILBOX

4

(アプリケーション、メールボックス)あたりの実行中リクエストの上限。Exchangeは正確に4つまでしか許可せず、この値はそこへ固定されます。ここで増やしても単に輻輳を429に変換することになるだけでです。

O365_USER_AGENT

NONISV|SelfHosted|office365-mcp/0.1.0

Microsoftは、特定情報のないトラフィックの優先度を低くします。ドキュメント通りの形を維持し、真ん中のフィールドに自社名を入れましょう。

O365_SEARCH_REGION

auto

POST /search/query で使う SharePoint の地理的地域(NAMEURAPC)。アプリ専用検索に必須です。複数地域テナントでは明示的に設定してください。

ゲートと出力

変数

既定値

説明

O365_APP_ONLY_ENABLED

false

アプリ専用モードのマスタースイッチ。false の間は、許可リストの内容に関係なく、このコードパスに到達できません。

O365_APP_ONLY_MAILBOXES

アプリの資格情報でアクセスできるメールボックスアドレスのカンマ区切りリスト。ワイルドカードは即座に拒否されます。

O365_APP_ONLY_SITES

アプリ専用アクターで行われるすべてのサイトまたはドライブ指定の呼び出しに対して強制される、アプリ資格情報でアクセス可能なサイトIDまたはURLのカンマ区切りリスト。空のリストは、アプリ専用がSharePointに一切アクセスできないことを意味し、サイトを指定しないアプリ専用の呼び出しは、許可ではなく拒否されます。照合は大文字と小文字を区別せず、完全一致または/パス境界で終わるプレフィックスです。そのため、1つのエントリでサイトとその配下のすべてをカバーでき、短い参照がアクセスを広げることはありません。Sites.Selected の登録とペアになります。

O365_ALLOW_PERMANENT_DELETE

false

ユーザーごとのポリシーに加えて、o365_mail_deletepermanent モードに対するデプロイメントレベルのロック。

MCP_OUTPUT_FORMAT

toon

toon は、一覧表示でのトークン使用量を大幅に削減するコンパクトな表形式出力を生成します。json は、プログラムによる利用者向けのきれいなJSONを出力します。

MCP_ARTIFACT_BUCKET

ダウンロード用のS3バケット。すべてのダウンロードツールで必須です。設計上、base64にフォールバックはありません。

MCP_ARTIFACT_URL_TTL_SECONDS

3600

署名付きURLの有効期間。署名なしでファイルを取得できるため、短くしてください。

MCP_ARTIFACT_REGION

AWS_REGION

アーティファクトバケットのリージョンを上書きします。

MCP_AUDIT_FILE

標準エラー出力に加えて、監査・セキュリティ記録用のJSONLシンク。セルフホストデプロイメント向け。Lambdaでは、標準エラー出力はすでにCloudWatchに到達します。

MCP_AUDIT_READS

未設定

1 は読み取りツールも書き込みと同様に監査します。読み取りが量を占めるため、デフォルトではオフです。この設定とは無関係に、すべての変更呼び出し、すべての失敗、別のメールボックスまたはユーザーを指定するすべての呼び出しは常に記録されます。他人のメールボックスに対する操作は、コンプライアンスレビューが特に問題にする点です。

PORT

3000

通常のNodeエントリポイント用のリッスンポート。LambdaとAzure Functionsでは使用されません。

共有メールボックスへのアクセス

この設計はこの機能を中心に構築されており、その機能自体はこのサーバーではなくテナントに依存します。

Graphがそもそもこの操作を実行するには、2つの条件が両方とも真である必要があります。 接続には、委任された .Shared Graph スコープ(Mail.Read.SharedMail.ReadWrite.SharedMail.Send.Shared — すべて work スコーププロファイルに含まれます)が必要です。また、Exchange Onlineは、サインイン中のユーザーにターゲットメールボックスへの権限を付与している必要があります。スコープはあくまで機能を可能にするだけで、実際のゲートはExchangeです。 Exchange側で権限がなければ、同意がどうであってもGraphは403を返します。

管理者は、Exchange管理センター(受信者 → メールボックス → 共有メールボックス → 委任)で、次の1つ以上を付与します。

権利

有効になること

効果

完全アクセス(Full Access)

メールボックス内の読み取り、一覧表示、移動、削除、下書き

そのメールボックスに対するすべての読み取り・書き込みツールで必要です。送信時に共有メールボックスの送信済みアイテムにコピーを残す場合にも必要です。

別名で送信(Send As)

共有メールボックスを送信者として送信

受信者には共有メールボックスのみが表示されます。

代理送信(Send on Behalf)

メールボックスの代理として送信

受信者には「ユーザー共有メールボックス に代わって」と表示されます。ユーザーはOutlookでこれを自分自身に付与できます。Send As を付与できるのは管理者のみです。

権限を付与してから反映されるまで、最大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.tspolicyAllowsMailboxresolveActor から到達します)。

フロー。 /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 を持ちます:

フィールド

意味

allowedTools

"*" は、将来のツールを含むすべてを許可します。管理者やサービスアカウント向けの全アクセス設定。配列は固定の許可リストです。上がるとしては--。

toolPermissions

ツールごとの {name: boolean} マップ。デフォルト拒否です: ツールは、そのエントリが true のときにのみcall できます。allowedTools 配列より優先されます。"*" が依然として優先します。

allowWrites

その上に、変更(mutating)とマークされたすべてのツールで必要です。管理、CLIではありません。

grantedMailboxes

コネクト時にメールボックスの同意ページでユーザーが承認したもの。欠落している場合は、承認が実施されず、そのユーザー自身のメールボックスのみにアクセスできます。/oauth/consent が記入し、作成。

allowedMailboxes

その上に設ける管理者の上限: "*"(デフォルト)、明示的なアドレスリスト、あるいは null。実際にアローは grantedMailboxes との交差。API キーの ID 場合は、電話がページを表示しないため、このフィールドが全体の制御となります。

allowAppOnly

アプリ専用に対してのユーザーごとのゲート。デフォルトは false で、 不注意な又は侵害されたユーザーが、自分の Exchange 権限を超えて静かに昇格することはありません。

rateLimitPerMin

ユーザーごとの呼び出しレート制限。デフォルトは 60。

disabled

アイデンティティを削除せずに無効にします。

ユーザーに呼び出せないツールは、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 alice
npm 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 key

connection 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_PREVIOUSconnection 回ります でローテーションしてください。決して置き換えないでください。

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 ではなく説明付きのエラーとして失敗します。personalUser.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 を参照してください。

-
license - not tested
Not graded
quality - not tested
C
maintenance

Maintenance

Maintainers
Response time
Release cycle
Releases (12mo)
Commit activity

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

View all MCP Connectors

Latest Blog Posts

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