Skip to main content
Glama
MT-Git01
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 のタイムアウト値を延長 |

---

## ライセンス

本プロジェクトは社内利用を想定しています。ライセンスについては組織のポリシーに従ってください。