MS Graph MCP Server
by MT-Git01
README.md
# MS Graph MCP Server for Cloud Run
Microsoft Graph API(Outlook メール、Teams チャット、ユーザー情報)を
MCP (Model Context Protocol) ツールとして提供する Python サーバーです。
Google Cloud Run 上にデプロイし、AI アシスタント(Claude Desktop、Cursor 等)から
Streamable HTTP 経由で呼び出すことで、自然言語によるメール操作・Teams 連携を実現します。
---
## 目的
本プロジェクトは、組織内の Microsoft 365 サービスを **AI エージェントから安全に操作**するためのブリッジとして機能します。
- **Outlook メール操作の自動化** — 下書き作成、送信、受信トレイ確認を AI アシスタントから実行
- **Teams メッセージ送信** — 1:1 チャット、グループチャット、チャネルへのメッセージ送信
- **ユーザー情報取得** — 部署・地域情報に基づくアクセス制御やリージョナルコンプライアンス(EU GDPR、中国 AI 規制等)の判定支援
- **セキュアなクラウドデプロイ** — GCP Secret Manager によるシークレット管理、Cloud Run の内部通信制限
---
## 仕様
### システムアーキテクチャ
```
┌──────────────────────┐ Streamable HTTP ┌──────────────────────────┐
│ │ POST /mcp │ │
│ MCP Client │─────────────────────────▶ │ FastAPI + FastMCP │
│ (Claude Desktop, │ │ (Cloud Run) │
│ Cursor, etc.) │◀─────────────────────────│ │
│ │ JSON Response │ │
└──────────────────────┘ └────────┬────────┬────────┘
│ │
MSAL Token │ │ Graph API
Request │ │ Calls
▼ ▼
┌────────────┐ ┌────────────────┐
│ Microsoft │ │ Microsoft │
│ Entra ID │ │ Graph API │
└────────────┘ └────────────────┘
```
### 技術スタック
| カテゴリ | 技術 |
|:---|:---|
| 言語 | Python 3.11+ |
| Web フレームワーク | FastAPI + uvicorn |
| MCP SDK | `mcp` (FastMCP / Streamable HTTP) |
| 認証 | MSAL (Client Credentials Flow) |
| HTTP クライアント | httpx (非同期) |
| シークレット管理 | GCP Secret Manager |
| デプロイ先 | Google Cloud Run |
### MCP ツール一覧
| カテゴリ | ツール名 | 説明 |
|:---|:---|:---|
| Outlook メール | `create_email_draft` | ユーザーのメールボックスに下書きメールを作成 |
| Outlook メール | `send_email_draft` | 作成済みの下書きメールを送信 |
| Outlook メール | `list_inbox_messages` | 受信トレイの最新メッセージ一覧を取得 |
| Teams チャット | `send_teams_message` | 1:1/グループチャット/チャネルにメッセージを送信 |
| ユーザー情報 | `get_user_department_and_region` | 部署・役職・国/地域などのプロフィール情報を取得 |
### 必要な Microsoft Graph API 権限
以下は **アプリケーション権限**(管理者の同意が必要)です:
| 権限 | 用途 |
|:---|:---|
| `Mail.ReadWrite` | メールの下書き作成・受信トレイ読取 |
| `Mail.Send` | メールの送信 |
| `Chat.ReadWrite.All` | Teams チャットへのメッセージ送信 |
| `User.Read.All` | ユーザープロフィール情報の取得 |
---
## プロジェクト構成と Python モジュールの役割分担
```
MS-Graph-MCP-Server_for_Cloud_Run/
├── app/
│ ├── __init__.py # パッケージ初期化
│ ├── main.py # ① エントリーポイント(FastAPI + MCP マウント)
│ ├── graph_client.py # ② Microsoft Graph API クライアント
│ └── mcp_tools.py # ③ MCP ツール定義(①と②の橋渡し)
├── requirements.txt # Python 依存パッケージ
├── Dockerfile # マルチステージ Docker ビルド
├── deploy.sh # GCP Cloud Run デプロイスクリプト
├── Gemini.md # プロジェクト仕様書
└── README.md # 本ドキュメント
```
### ① `app/main.py` — エントリーポイント
**役割**: アプリケーション全体の起動と HTTP エンドポイントの定義
- `FastMCP` サーバーを `stateless_http=True` で初期化(Cloud Run のステートレススケーリング対応)
- `mcp.streamable_http_app()` を FastAPI の `/mcp` パスにマウント
- `lifespan` コンテキストマネージャーでセッションマネージャーのライフサイクルと Graph クライアントのクリーンアップを管理
- `GET /health` ヘルスチェックエンドポイントを提供(Cloud Run の Readiness/Liveness プローブ用)
- `mcp_tools.py` の `register_tools()` を呼び出してツールを登録
```
リクエスト → FastAPI (main.py) → /mcp → FastMCP → ツール実行 → レスポンス
```
### ② `app/graph_client.py` — Microsoft Graph API クライアント
**役割**: Microsoft Graph API との認証・通信を一手に担当
- **`MSGraphClient` クラス**: MSAL の `ConfidentialClientApplication` を使用した Client Credentials Flow による認証
- **トークン管理**: `acquire_token_silent` → `acquire_token_for_client` の順でキャッシュ優先のトークン取得
- **HTTP ヘルパー**: `_get()` / `_post()` メソッドで認証ヘッダー付きの非同期リクエストを実行
- **シークレット解決**: 環境変数 → GCP Secret Manager のフォールバック方式
- **ビジネスロジック**: Outlook メール操作、Teams メッセージ送信、ユーザー情報取得の各メソッドを提供
```
mcp_tools.py → MSGraphClient → MSAL (トークン取得) → httpx (Graph API 呼び出し)
```
### ③ `app/mcp_tools.py` — MCP ツール定義
**役割**: `main.py` と `graph_client.py` の橋渡し。MCP プロトコルのインターフェース層
- `register_tools(mcp)` 関数で全5ツールを `@mcp.tool()` デコレータにより FastMCP に登録
- 各ツールは `MSGraphClient` のメソッドを呼び出し、結果を構造化 JSON 文字列に整形して返却
- **遅延初期化**: `MSGraphClient` はツール初回呼び出し時にインスタンス化(起動時のシークレット取得失敗を回避)
- **エラーハンドリング**: 全ツールで例外をキャッチし、エラー情報を JSON で返却(MCP クライアント側でのクラッシュを防止)
- `shutdown_client()` でアプリケーション終了時の HTTP クライアントクリーンアップを提供
```
MCP Client → FastMCP → mcp_tools.py (ツール定義) → graph_client.py (API 呼出) → Graph API
```
### モジュール間の依存関係
```
main.py ──imports──▶ mcp_tools.py ──imports──▶ graph_client.py
│ │ │
│ FastMCP 初期化 │ ツール登録 │ MSAL 認証
│ FastAPI マウント │ エラーハンドリング │ Graph API 通信
│ lifespan 管理 │ レスポンス整形 │ シークレット取得
▼ ▼ ▼
HTTP 層 インターフェース層 インフラ/通信層
```
---
## 他のアプリケーションとの連携方法
### MCP クライアントからの接続
本サーバーは MCP 準拠の Streamable HTTP エンドポイントを提供します。
MCP 対応クライアントであれば、以下の URL を設定するだけで接続可能です。
```
https://<YOUR_CLOUD_RUN_URL>/mcp
```
### Claude Desktop との連携
`claude_desktop_config.json` に以下を追加します:
```json
{
"mcpServers": {
"ms-graph": {
"url": "https://<YOUR_CLOUD_RUN_URL>/mcp",
"transport": "streamable-http"
}
}
}
```
### Cursor との連携
Cursor の MCP 設定画面で以下を入力します:
- **Name**: `ms-graph`
- **Type**: `streamable-http`
- **URL**: `https://<YOUR_CLOUD_RUN_URL>/mcp`
### 独自アプリケーションからの連携(Python)
MCP Python SDK を使用してプログラムから接続できます:
```python
from mcp import ClientSession
from mcp.client.streamable_http import streamablehttp_client
async def main():
async with streamablehttp_client("https://<YOUR_CLOUD_RUN_URL>/mcp") as (
read_stream, write_stream, _
):
async with ClientSession(read_stream, write_stream) as session:
await session.initialize()
# ツール一覧の取得
tools = await session.list_tools()
print(tools)
# メール下書き作成の例
result = await session.call_tool(
"create_email_draft",
arguments={
"user_email": "user@example.com",
"subject": "テスト件名",
"body_html": "<p>テスト本文</p>",
"to_recipients": ["recipient@example.com"],
},
)
print(result)
```
### 認証が必要な場合(Cloud Run IAM)
Cloud Run が `--no-allow-unauthenticated` で設定されている場合、
クライアント側で ID トークンを取得してリクエストヘッダーに付与する必要があります:
```bash
# gcloud CLI で ID トークンを取得
TOKEN=$(gcloud auth print-identity-token)
curl -H "Authorization: Bearer ${TOKEN}" https://<YOUR_CLOUD_RUN_URL>/health
```
---
## 開発環境
### 前提条件
- Python 3.11 以上
- Azure AD にアプリ登録済み(Client Credentials 用)
- (任意) Docker Desktop(ローカルでのコンテナビルド確認用)
### セットアップ
```bash
# リポジトリをクローン
git clone <repository-url>
cd MS-Graph-MCP-Server_for_Cloud_Run
# 仮想環境を作成・有効化
python3 -m venv .venv
source .venv/bin/activate
# 依存パッケージをインストール
pip install -r requirements.txt
```
### 環境変数の設定(ローカル開発)
```bash
export TENANT_ID="your-azure-tenant-id"
export CLIENT_ID="your-azure-client-id"
export CLIENT_SECRET="your-azure-client-secret"
```
### ローカル起動
```bash
# uvicorn で起動
uvicorn app.main:app --host 0.0.0.0 --port 8080 --reload
# ヘルスチェック
curl http://localhost:8080/health
# → {"status":"healthy","service":"ms-graph-mcp-server"}
```
### MCP Inspector でのデバッグ
```bash
# MCP Inspector を使ってツール一覧・動作確認
mcp dev app/main.py
```
---
## 実行環境(本番)
### Google Cloud Run
| 設定項目 | 値 |
|:---|:---|
| プラットフォーム | managed |
| Ingress | `internal-and-cloud-load-balancing` |
| 認証 | `--no-allow-unauthenticated`(IAM 認証必須) |
| メモリ | 512 Mi |
| CPU | 1 |
| タイムアウト | 300 秒 |
| 最小インスタンス | 0 |
| 最大インスタンス | 10 |
### シークレット管理
| シークレット名 | 説明 |
|:---|:---|
| `TENANT_ID` | Azure AD テナント ID |
| `CLIENT_ID` | Azure AD アプリケーション (クライアント) ID |
| `CLIENT_SECRET` | Azure AD クライアントシークレット |
シークレットは Cloud Run のデプロイ時に `--set-secrets` オプションで環境変数としてマウントされます。
### デプロイ手順
```bash
# 1. 環境変数を設定
export GCP_PROJECT_ID="your-gcp-project-id"
export GCP_REGION="asia-northeast1" # 任意(デフォルト: asia-northeast1)
# 2. シークレットの値を登録(初回のみ)
echo -n "YOUR_TENANT_ID" | gcloud secrets versions add TENANT_ID --data-file=- --project=${GCP_PROJECT_ID}
echo -n "YOUR_CLIENT_ID" | gcloud secrets versions add CLIENT_ID --data-file=- --project=${GCP_PROJECT_ID}
echo -n "YOUR_CLIENT_SECRET" | gcloud secrets versions add CLIENT_SECRET --data-file=- --project=${GCP_PROJECT_ID}
# 3. デプロイ実行
chmod +x deploy.sh
./deploy.sh
```
### エンドポイント
| パス | メソッド | 説明 |
|:---|:---|:---|
| `/mcp` | POST | MCP Streamable HTTP エンドポイント |
| `/health` | GET | ヘルスチェック |
---
## メンテナンス方法
### ログの確認
Cloud Run のログは Google Cloud Console または `gcloud` CLI で確認できます:
```bash
# 直近のログを確認
gcloud run services logs read ms-graph-mcp-server \
--region=asia-northeast1 \
--project=${GCP_PROJECT_ID} \
--limit=100
# リアルタイムでログを追跡
gcloud run services logs tail ms-graph-mcp-server \
--region=asia-northeast1 \
--project=${GCP_PROJECT_ID}
```
### シークレットのローテーション
Azure AD のクライアントシークレットを更新した場合:
```bash
# 1. Secret Manager に新しい値を追加
echo -n "NEW_CLIENT_SECRET" | gcloud secrets versions add CLIENT_SECRET \
--data-file=- --project=${GCP_PROJECT_ID}
# 2. Cloud Run サービスを再デプロイ(最新のシークレットバージョンを取得)
gcloud run services update ms-graph-mcp-server \
--region=asia-northeast1 \
--project=${GCP_PROJECT_ID} \
--set-secrets="TENANT_ID=TENANT_ID:latest,CLIENT_ID=CLIENT_ID:latest,CLIENT_SECRET=CLIENT_SECRET:latest"
```
### 新しい MCP ツールの追加
1. `app/graph_client.py` に新しい Graph API 操作メソッドを追加
2. `app/mcp_tools.py` の `register_tools()` 関数内に `@mcp.tool()` デコレータ付きの新しいツール関数を追加
3. 必要に応じて Azure AD のアプリ権限を追加・管理者承認
### 依存パッケージの更新
```bash
# 仮想環境内で最新バージョンを確認
pip list --outdated
# requirements.txt を更新後、再デプロイ
./deploy.sh
```
### トラブルシューティング
| 症状 | 考えられる原因 | 対処 |
|:---|:---|:---|
| `Failed to acquire token` | Client ID/Secret/Tenant ID が不正 | Secret Manager の値を確認 |
| `403 Forbidden` (Graph API) | API 権限が不足 | Azure AD でアプリ権限を追加・管理者承認 |
| `401 Unauthorized` (Cloud Run) | IAM 認証トークンが無効 | `gcloud auth print-identity-token` で再取得 |
| ヘルスチェック失敗 | コンテナ起動エラー | `gcloud run services logs read` でログ確認 |
| タイムアウト | Graph API の応答遅延 | Cloud Run のタイムアウト値を延長 |
---
## ライセンス
本プロジェクトは社内利用を想定しています。ライセンスについては組織のポリシーに従ってください。
This server cannot be deployed
Maintenance
ActivityMaintained
ResponsivenessSyncing