Gmail を AI に裁かせる MCP を自作した話 — 制作日誌 #3
朝の Gmail を AI に仕分けさせ、必要に応じて返信ドラフトまで作らせたい。第三者 SaaS を挟まずローカルで完結させたい。そのために自作した Gmail MCP の中身。
前回 はブラウザ自動化 MCP を自作した話を書いた。今回は同じく自作した Gmail 周りの MCP の話。
「AI にメールを仕分けさせて、必要なら返信ドラフトまで書かせる」というシンプルな要望から始まったプロジェクト。
Gmail MCP の選択肢
Gmail を AI から触る方法は、いくつかある。
| 選択肢 | できること | 個人開発者目線の難点 |
|---|---|---|
| claude.ai の Gmail 連携 | 一覧・本文読み取り中心 | 送信・添付など書き込み系が薄い |
| Composio など third-party SaaS の Gmail MCP | 送信・添付含むフル機能 | SaaS にトークン預ける形、月額発生する場合あり |
| Zapier / Make 経由の AI 連携 | 送信できる | ワークフローが冗長になりがち、リアルタイム性が弱い |
| Gmail API を直接叩く自前 MCP | 何でもできる | 自作の手間がかかる |
最初の数日は claude.ai のメール連携で済ませようとしたが、仕分けはできるけど返信ドラフトの保存ができない、添付付きで送りたいときに別経路を踏まないといけないで詰まった。
Composio など third-party は良くできているが、
- OAuth トークンが第三者の手元を経由する
- 月額課金が乗ってくる
- 自分の Gmail にどこまでアクセス可能か、自分側で完全に握れない
個人開発者として、**「自分の OAuth トークンを自分の PC だけで持つ」「全部ローカルで完結させる」**形に倒したかった。なら自作する、ということになった。
自作するなら全部入りで
k-gmail を作るときに決めた要件:
- メール検索・本文取得(公式相当の読み取り全部)
- 下書き作成・送信(
gmail_create_draft+gmail_send_draft) - 直接送信(
gmail_send_message) - 添付ファイル付き送信(
gmail_send_with_attachments) - 添付ファイルのダウンロード
- ラベル一覧の取得
- From 表示名を引数で切り替え(個人 vs クライアント案件で名前を変える)
文字通り「Gmail を AI から全操作できる MCP」を目指した。
OAuth と Gmail API 周りの実装メモ
Gmail API は OAuth 2.0 で認証する。詰まったポイントを残しておく。
スコープを最小権限で
最初は楽したくて https://mail.google.com/(全権限)を要求するクライアントを作ったが、本人のセキュリティを考えるとよろしくない。最終的には:
gmail.modify— メッセージ読み書き・ラベル操作gmail.compose— 下書き作成gmail.send— 純粋送信
の3つに絞った。これで「メール完全削除」「設定変更」などの破壊操作は API から不可能になる。AI が暴走しても最悪の事故が起きないところまでガードしてある。
Refresh Token はローカル .env に集約
OAuth の refresh token をどこに置くか。シークレット管理サービスを使う選択肢もあるが、結局全部 ~/.claude/secrets/.env に集約することにした。
他のシークレット(Cloudflare API トークン、各種 SaaS の API キーなど)と同じ場所にあれば、PC 移行のときに同じファイル1つコピーすれば済む。バックアップも一括。
ハードコーディング禁止のルールを徹底して、コード側は必ず os.getenv("KGMAIL_REFRESH_TOKEN") 経由で読む。
添付ファイル送信の実装
これが意外と面倒で、Gmail API はメールを RFC 2822 形式の生メッセージとして組み立ててから base64url でエンコードして送る必要がある。
Python だと email.mime.multipart の MIMEMultipart で本文と添付パートを組んで、MIMEApplication で添付ファイルを足して、最後に .as_bytes() してから base64.urlsafe_b64encode でエンコード。
msg = MIMEMultipart()
msg["to"] = to
msg["subject"] = subject
msg.attach(MIMEText(body, "plain"))
for path in attachments:
with open(path, "rb") as f:
part = MIMEApplication(f.read(), Name=os.path.basename(path))
part["Content-Disposition"] = f'attachment; filename="{os.path.basename(path)}"'
msg.attach(part)
raw = base64.urlsafe_b64encode(msg.as_bytes()).decode()
service.users().messages().send(userId="me", body={"raw": raw}).execute()
たった10行のコードを「正しく書く」ために半日溶かした。RFC 2822 の歴史的経緯に振り回されながら。
From 表示名のルール化
送信元アドレスは1つしかなくても、表示名は使い分けたい。個人ブログとして送る時と、クライアント案件で送る時で名前を変えたい。
MCP のパラメータとして from_name を受けて、msg["from"] = formataddr((from_name, EMAIL)) で動的に組み立てるようにした。AI 側からはツール引数1つで切り替わる。
AI 側のフローはどう変わったか
k-gmail を入れてから、AI に頼める仕事の幅が一気に広がった。
- 「朝の Gmail を全部仕分けて、即対応 / カレンダー化 / Notion タスク化 / スルーに分類して、即対応分は返信ドラフトを作っておいて」
- 「この見積りPDFをクライアントに送って」(添付付き送信)
- 「この案件のスレッド全部読んで、要点を要約して」
- 「この件、後でフォローアップ送るから下書きだけ作っておいて」
特に1つ目は毎朝のルーチンになった。仕分けの結果を簡潔に出してもらって、「即対応」だけ自分で目視確認して送信ボタン押す。20通あるメールの処理が10分から2分になった。
公式 / 第三者 / 自作の役割分担
ここまで読むと「全部自作した方がいい」と感じるかもしれないが、現実は使い分け。
| 用途 | 何で動かすか |
|---|---|
| ちょっとした閲覧・要約 | claude.ai 内の Gmail 連携で十分 |
| 業務システムに組み込みたい | Composio など third-party MCP(運用面が楽) |
| ローカル完結 + 自分でトークン握りたい | 自作 k-gmail |
「自作する価値があるか」は、用途の継続性次第。毎日何回も使う、トークンを第三者に預けたくない、Claude Code CLI から呼びたい——どれかが該当するなら自作の手間は元が取れる。
セキュリティ余談
自分のメールを AI に触らせるのは、当然リスクがある。やっておくべき自衛策を1つ。
送信前に必ず Claude のレスポンスを目視で確認するルールを徹底する。
具体的には、gmail_send_message を呼ぶ前のターンで、AI から「この内容で送信しますか?」と聞かせる Hook を仕込む。settings.json の hooks 機能で強制してもいい。
「AI が便利だから送信まで任せる」ではなく、「AI が下書きまでやって、最終確認だけ人がやる」のラインを保てば、事故は起きない。これは1年運用して0件のトラブル。
次回予告
次回は 「Notion をタスクキューにする MCP を作った話」。期間方式(Due.start + Due.end)と繰り返し(Repeat select)の組み合わせで、「いつ何をするか」を全部 Notion に集約した自作 MCP。
まとめ
- Gmail を AI から触る方法は複数ある。claude.ai 連携 / third-party MCP / 自作 MCP
- 自作の動機は「ローカル完結」「トークンを自分で握る」「Claude Code CLI から呼べる」の3点
- 自作 k-gmail は送信・下書き・添付付き送信・添付ダウンロードを全部実装
- OAuth スコープは最小限(
gmail.modify+compose+send) - 送信前確認は人間がやるルールでリスクは抑えられる
Comments (0)
No comments yet. Be the first to leave one.