Skip to main content
Glama
README.md
# gws-mcp

アクセス範囲と操作権限を厳しく絞った、**Google Workspace MCP サーバー**です。

LLM の誤操作による意図しない変更・削除・情報流出を防ぐための制限をコード側で強制します。

- 外部依存は **Google 公式ライブラリのみ**です。サードパーティ製フレームワークは使いません。

## 対応サービスと操作範囲

| サービス              | 読み取り | 作成 | 更新  | 削除 |
| --------------------- | :------: | :--: | :---: | :--: |
| Drive(マイドライブ) |    ✅    |  ✅  | ✅ ※1 |  ❌  |
| Drive(共有ドライブ) |    ✅    |  ❌  |  ❌   |  ❌  |
| スプレッドシート      |    ✅    |  ✅  | ✅ ※1 |  ❌  |
| ドキュメント          |    ✅    |  ✅  | ✅ ※1 |  ❌  |
| スライド              |    ✅    |  ✅  | ✅ ※1 |  ❌  |
| カレンダー            |    ✅    |  ✅  | ✅ ※2 |  ❌  |
| Gmail                 |    ✅    |  ❌  |  ❌   |  ❌  |

※1 対象は「**自分が所有者**」かつ「**共有ドライブに属していない(`driveId` がない)**」ファイルだけです。どちらかを満たさないファイルへの書き込みは拒否します。
※2 作成・更新できるのは自分のメインカレンダー(primary)だけです。更新は自分が主催者の予定に限ります。

## セキュリティ設計

### 書き込みガード

Drive・スプレッドシート・ドキュメント・スライドの書き込み系ツールは、実行前に Drive API(`files.get`)で対象ファイルの情報を取得し、次のいずれかに当てはまれば **API を呼ばずにエラーを返します**。

- `driveId` がある(共有ドライブ内のファイル)
- `ownedByMe` が `true` でない(他人が所有するファイル)
- ゴミ箱に入っている
- ショートカットである(リンク先が共有ドライブのファイルかどうかを ID から判別できないため)

Drive のフォルダ作成・ファイル作成・コピーでは、作成先の親フォルダに同じ判定をかけます(既定の作成先はマイドライブ直下)。スプレッドシート・ドキュメント・スライドの新規作成は常にマイドライブ直下に作られ、作成後にできたファイルを同じ条件で確認します。ファイル ID は形式をチェックしてから使います。

カレンダーは Drive のファイルではないため、予定を取得して自分が主催者かを判定します(※2)。

OAuth スコープではマイドライブと共有ドライブを区別できないため、この制限はスコープではなくコード([gws_mcp/guard.py](gws_mcp/guard.py))で強制しています。

さらに Drive API の書き込み呼び出し(作成・更新・名前変更)には `supportsAllDrives` を付けません。付けなければ共有ドライブのファイルは API 側で「見つからない」扱いになるため、ガードとは別の二重の防御になります(共有ドライブのファイルを読むために必要なコピーだけは例外で、コピー先はガードで判定します)。

### ツールとして公開しない操作

- ファイルの削除、ゴミ箱への移動、ゴミ箱を空にする
- 共有設定(`permissions`)の変更
- ファイルの移動(親フォルダの付け替え)
- 共有ドライブの作成・変更
- カレンダーの予定の削除、カレンダー自体の作成・削除・共有設定
- Gmail の送信・下書き作成・ラベル変更などの書き込み全般
- 任意の API を直接呼べる汎用ツール

### その他の安全策

- **スプレッドシート**: 書き込みは既定で `RAW`(数式として解釈しない)です。`IMPORTDATA` などの数式でデータが外部に送られるのを防ぎます。数式を入れたいときだけ `USER_ENTERED` を明示します。
- **カレンダー**: 参加者がいる予定の作成・更新では、招待・通知メールを送るか(`send_updates`)の指定を必須にしており、指定がなければ実行しません。LLM には送るかをユーザーに確認してから指定するよう指示しています。書き込み先は自分のメインカレンダーに固定しており、共有カレンダーは編集権限があっても変更しません。
- **トークン**: 権限 `0600` で保存します(ディレクトリは `0700`)。サーバー実行中にブラウザ認証を始めることはありません。
- **ログ**: 標準エラー出力だけに出します(stdout は MCP 通信専用)。

### OAuth スコープ

| スコープ                                          | 用途                                         |
| ------------------------------------------------- | -------------------------------------------- |
| `https://www.googleapis.com/auth/drive`           | Drive / Docs / Sheets / Slides の読み書き    |
| `https://www.googleapis.com/auth/gmail.readonly`  | Gmail の読み取り                             |
| `https://www.googleapis.com/auth/calendar.events` | 予定の読み書き(カレンダー自体の操作は不可) |

## 必要なもの

- Python 3.10 以上
- Google Workspace アカウント
- Google Cloud プロジェクト(OAuth クライアント作成用)

## セットアップ

以下の手順はスキル(`/setup`)で対話的に進められます

### 1. Google Cloud の準備

1. [Google Cloud Console](https://console.cloud.google.com/) でプロジェクトを作ります。
2. 「API とサービス」→「ライブラリ」で次の API を有効にします。
   - Google Drive API
   - Gmail API
   - Google Calendar API
   - Google Sheets API
   - Google Docs API
   - Google Slides API
3. 「Google Auth Platform」で OAuth 同意画面を設定し、「対象」でユーザーの種類に「内部」を選びます(組織利用が前提。個人利用で「外部」かつ「テスト中」にすると、トークンが 7 日で失効します)。
4. 「認証情報」→「認証情報を作成」→「OAuth クライアント ID」で、種類に **デスクトップアプリ** を選びます。
5. JSON をダウンロードし、`~/.config/gws-mcp/credentials.json` として保存します。

```sh
mkdir -p ~/.config/gws-mcp && chmod 700 ~/.config/gws-mcp
mv ~/Downloads/client_secret_*.json ~/.config/gws-mcp/credentials.json
chmod 600 ~/.config/gws-mcp/credentials.json
```

保存場所は環境変数 `GWS_MCP_CONFIG_DIR` で変更できます。

### 2. インストール

```sh
cd /path/to/gws-mcp
python3 -m venv .venv
.venv/bin/pip install -r requirements.lock
```

`requirements.lock` は、推移的な依存まで動作確認済みのバージョンで固定したものです。直接の依存(Google 公式4パッケージ)は `requirements.txt` で管理し、更新したときは `.venv/bin/pip freeze > requirements.lock` で作り直します。

### 3. 初回認証

```sh
.venv/bin/python scripts/authorize.py
```

ブラウザが開くので、Google アカウントでログインして権限を許可します。トークンは `~/.config/gws-mcp/token.json` に保存されます。スコープを変えたときやトークンが無効になったときも、このコマンドを実行し直してください。

### 4. MCP クライアントへの登録

`/path/to/gws-mcp` は実際のパスに置き換えてください。[config/](config/) に設定例があります(Claude Code のプロジェクト設定 `.mcp.json` 用: [claude-code.mcp.example.json](config/claude-code.mcp.example.json)、VS Code 用: [vscode.mcp.example.json](config/vscode.mcp.example.json))。

**Claude Code**

```sh
claude mcp add --scope user gws -e PYTHONPATH=/path/to/gws-mcp -- /path/to/gws-mcp/.venv/bin/python -m gws_mcp
```

(`--scope user` でどのプロジェクトからも使えるようにします。`PYTHONPATH` はリポジトリ外から起動しても `gws_mcp` を読み込めるようにするためです)

**VS Code**(`.vscode/mcp.json`)

```json
{
  "servers": {
    "gws": {
      "type": "stdio",
      "command": "/path/to/gws-mcp/.venv/bin/python",
      "args": ["-m", "gws_mcp"],
      "env": { "PYTHONPATH": "/path/to/gws-mcp" }
    }
  }
}
```

## ツール一覧

### Drive

| ツール                   | 種別 | 説明                                                                        |
| ------------------------ | ---- | --------------------------------------------------------------------------- |
| `drive_search`           | 読取 | キーワードでファイルを検索(共有ドライブを含む)                            |
| `drive_list_folder`      | 読取 | フォルダ内のファイル一覧                                                    |
| `drive_get_metadata`     | 読取 | ファイルのメタデータを取得                                                  |
| `drive_read_file`        | 読取 | 本文をテキストで取得(Google ドキュメント類はエクスポート、バイナリは不可) |
| `drive_create_folder`    | 書込 | マイドライブにフォルダを作成                                                |
| `drive_create_text_file` | 書込 | マイドライブにテキストファイルを作成                                        |
| `drive_update_text_file` | 書込 | テキストファイルの本文を更新                                                |
| `drive_rename`           | 書込 | ファイル名を変更                                                            |
| `drive_copy_file`        | 書込 | ファイルをマイドライブのフォルダにコピー(コピー元は共有ドライブも可)      |

### Gmail(読み取りのみ)

| ツール              | 説明                                       |
| ------------------- | ------------------------------------------ |
| `gmail_search`      | Gmail の検索構文でメールを検索(最大50件) |
| `gmail_get_message` | メール本文を取得(text/plain 優先)        |
| `gmail_get_thread`  | スレッドを取得                             |
| `gmail_list_labels` | ラベル一覧                                 |

### カレンダー

| ツール                  | 種別 | 説明                               |
| ----------------------- | ---- | ---------------------------------- |
| `calendar_list_events`  | 読取 | 期間・キーワードで予定を一覧       |
| `calendar_get_event`    | 読取 | 予定の詳細を取得                   |
| `calendar_create_event` | 書込 | 自分のメインカレンダーに予定を作成 |
| `calendar_update_event` | 書込 | 自分が主催者の予定を部分更新       |

### スプレッドシート

| ツール                | 種別 | 説明                       |
| --------------------- | ---- | -------------------------- |
| `sheets_get_metadata` | 読取 | シート構成を取得           |
| `sheets_read_range`   | 読取 | 範囲の値を取得             |
| `sheets_create`       | 書込 | スプレッドシートを新規作成 |
| `sheets_write_range`  | 書込 | 範囲に値を書き込み         |
| `sheets_append_rows`  | 書込 | 行を追記                   |
| `sheets_add_sheet`    | 書込 | シート(タブ)を追加       |

### ドキュメント

| ツール              | 種別 | 説明                   |
| ------------------- | ---- | ---------------------- |
| `docs_read`         | 読取 | 本文をテキストで取得   |
| `docs_create`       | 書込 | ドキュメントを新規作成 |
| `docs_append_text`  | 書込 | 末尾にテキストを追記   |
| `docs_replace_text` | 書込 | テキストを一括置換     |

### スライド

| ツール                  | 種別 | 説明                           |
| ----------------------- | ---- | ------------------------------ |
| `slides_read`           | 読取 | 各スライドのテキストを取得     |
| `slides_create`         | 書込 | プレゼンテーションを新規作成   |
| `slides_add_text_slide` | 書込 | タイトル+本文のスライドを追加 |
| `slides_replace_text`   | 書込 | テキストを一括置換             |

## ディレクトリ構成

```
gws-mcp/
├── gws_mcp/
│   ├── server.py      # JSON-RPC 2.0 stdio サーバー(MCP プロトコル処理)
│   ├── schema.py      # ツール入力の検証
│   ├── auth.py        # OAuth 資格情報の読み込み・更新
│   ├── guard.py       # 書き込みガード(マイドライブ判定)
│   └── tools/         # サービスごとのツール実装(drive / gmail / calendar / sheets / docs / slides)
├── scripts/
│   └── authorize.py   # 初回 OAuth 認証
├── tests/             # unittest(Google API はモック)
├── config/            # 各 MCP クライアント用の設定例
├── .claude/           # Claude Code 用の skill(/setup・/check-updates など)と、資格情報へのアクセスを防ぐフック・設定
├── requirements.txt   # 直接の依存
└── requirements.lock  # 推移的な依存まで固定したもの(インストールに使う)
```

## テスト

標準ライブラリの `unittest` だけで動きます(Google API はモックするので認証は不要です)。

```sh
.venv/bin/python -m unittest discover tests
```

## メンテナンス

- **書き込みガード**: 安全性を支える中心部分です。書き込みツールを追加するときは、`Tool` に `guard=`(判定の種類は [tools/\_\_init\_\_.py](gws_mcp/tools/__init__.py))を宣言し、tests のガードのテスト対象一覧にも追加してください。どちらかが抜けていると、登録時のエラーかテストの失敗になります。書き込み用の API 呼び出しには `supportsAllDrives` を付けないでください。
- **旧方式のプロトコル対応**: Claude Code と VS Code の両方が新方式で動くことを確認できてから、削除を検討します。どちらの方式で接続したかは、標準エラー出力のログ(`initialize:` か `server/discover:` か)で分かります。
- **入力検証**: [schema.py](gws_mcp/schema.py) が対応している JSON Schema のキーワードは一部だけです。ツールの引数に新しい種類の制約が必要になったら、先に schema.py を拡張してください。
- **依存の更新**: テストを実行し、`requirements.lock` を作り直したうえで、実際の環境で主要なツールを動かして確認してください。テストは Google API をモックしているため、API 側の変更は検出できません。

### 確認方法(`/check-updates` スキル)

定期的な更新確認は、Claude Code の `/check-updates` skill([.claude/skills/check-updates/SKILL.md](.claude/skills/check-updates/SKILL.md))で行えます。

## 注意事項

- `credentials.json` と `token.json` は絶対にコミットしないでください(`.gitignore` で除外済み)。
- `drive` スコープはファイル全体への読み書き権限です。実際の書き込み制限はガードのコードに依存するので、ガードを変更するときはテストも更新してください。
- トークンを取り消すには、[Google アカウントのサードパーティ接続](https://myaccount.google.com/connections) からアプリへのアクセスを削除し、`token.json` を消します。

## ライセンス

[MIT License](LICENSE)