📧 GMAIL · API · 雲端排程

Claude 怎麼收發 Gmail?
三種方式全解析

MCP、Google API Token、App Password SMTP——搞懂這三條路的差異,以及為什麼雲端定時任務發不出郵件的根本原因。

真實踩坑: Thomas 設了一個每天早晚自動發摘要郵件的定時任務,裡面用了 Gmail App Password + SMTP 發郵件。腳本本身沒問題(本地測試登入成功),但定時任務跑了好幾個月,從來沒收到過一封。最後查出原因:雲端環境封鎖了 SMTP 465 端口,所有發送請求靜默失敗。解決方法是改用 Gmail REST API(走 HTTPS 443),雲端不封這個。
🔀 三種 Claude 收發 Gmail 的方式
🔌
Gmail MCP
透過 claude.ai 的 OAuth 連接器,讓 Claude 直接操作 Gmail。設定一次,永久有效。
只讀 + 草稿,不能直接發送
🔑
Google API Token
用你在 GCP 申請的 OAuth 全家桶 Token,透過 Gmail REST API 直接發送郵件。走 HTTPS,雲端也能用。
✅ 推薦:讀 + 寫 + 發送全功能
📮
App Password + SMTP
用 Gmail 的應用程式密碼透過 SMTP 465 端口發送郵件。本地可用,但雲端被封。
❌ 雲端環境 SMTP 端口被封
📊 三種方式功能對比
功能 Gmail MCP Google API Token App Password SMTP
搜索郵件
讀取郵件內容
建立草稿
直接發送郵件
帶附件發送
雲端環境可用❌(SMTP被封)
操作 Drive / YouTube
設定難度最簡單中等簡單
Token 可撤銷需手動刪
🖥️ 三個使用環境能調用什麼
💡 核心限制
Google API Token 存在 GCP Secret Manager,需要 gcloud CLI 才能讀取。只有本地電腦裝了 gcloud,手機 APP 和雲端任務都碰不到它。
🖥️ 電腦 Claude Code
Google API Token(有gcloud)
Gmail MCP
App Password SMTP
本地文件 / Bash
📱 手機 Claude APP
Google API Token(無gcloud)
Gmail MCP(只讀+草稿)
App Password SMTP
本地文件
☁️ RemoteTrigger 雲端
⚠️ Token 直接內嵌可用
Gmail MCP(只讀+草稿)
SMTP(端口被封)
HTTPS REST API(443)
🚫 為什麼雲端封 SMTP?
背景知識: AWS、GCP、Azure 等主流雲端平台默認封鎖 TCP 端口 25、465、587(SMTP 端口)。原因是防止被濫用發垃圾郵件。這個封鎖是平台層面的,不是你的代碼問題,也不是密碼問題。

解決方法: 改用 Gmail REST API,走 HTTPS 443 端口。這個端口所有雲端平台都開放,因為它就是普通的 HTTPS 網頁請求。
發送方式端口雲端環境原因
SMTP (App Password)465 / 587❌ 被封防垃圾郵件策略
Gmail REST API443 (HTTPS)✅ 可用標準 HTTPS 請求
MCP (OAuth)443 (HTTPS)⚠️ 可讀不可發MCP 工具集限制
📤 用 Gmail REST API 發送郵件(Python)

這是雲端任務裡用的完整代碼,不依賴任何外部庫,只用 Python 標準庫:

import urllib.request, urllib.parse, json, base64, datetime
from email.mime.multipart import MIMEMultipart
from email.mime.text import MIMEText

# OAuth 憑證(直接內嵌,雲端無法用 gcloud)
CLIENT_ID = 'your-client-id.apps.googleusercontent.com'
CLIENT_SECRET = 'your-client-secret'
REFRESH_TOKEN = 'your-refresh-token'

# Step 1: 用 refresh_token 換取 access_token
tok_data = urllib.parse.urlencode({
    'client_id': CLIENT_ID,
    'client_secret': CLIENT_SECRET,
    'refresh_token': REFRESH_TOKEN,
    'grant_type': 'refresh_token'
}).encode()
with urllib.request.urlopen(
    urllib.request.Request('https://oauth2.googleapis.com/token',
                           data=tok_data, method='POST')
) as r:
    access_token = json.loads(r.read())['access_token']

# Step 2: 組裝郵件
msg = MIMEMultipart('alternative')
msg['Subject'] = '測試郵件'
msg['From'] = 'thomastangnz@gmail.com'
msg['To'] = 'thomastangnz@gmail.com'
msg.attach(MIMEText('

Hello!

', 'html', 'utf-8')) raw = base64.urlsafe_b64encode(msg.as_bytes()).decode() # Step 3: 用 Gmail API 發送 with urllib.request.urlopen(urllib.request.Request( 'https://gmail.googleapis.com/gmail/v1/users/me/messages/send', data=json.dumps({'raw': raw}).encode(), headers={'Authorization': f'Bearer {access_token}', 'Content-Type': 'application/json'}, method='POST' )) as r: result = json.loads(r.read()) print('Sent! ID:', result.get('id'))
🔑 關鍵:refresh_token 和 access_token 的區別
  • refresh_token:長期有效,不會過期(除非撤銷授權),存在 GCP Secret Manager 或直接內嵌
  • access_token:短期有效(約1小時),每次執行都要用 refresh_token 換一個新的
  • 代碼裡每次都重新換 token,不需要存儲 access_token
⏰ 怎麼升級雲端定時任務
診斷問題:本地測 App Password 登入成功 → 確認不是密碼問題。
結論:是雲端環境封了 SMTP 465 端口,靜默失敗。
取得 OAuth 憑證:從 GCP Secret Manager 讀取 refresh_tokenclient_idclient_secret
禁用舊觸發器:用 RemoteTrigger update {enabled: false} 禁用舊任務(API 不支持刪除,去 claude.ai/code/scheduled 手動刪)。
創建新觸發器:用 RemoteTrigger create,腳本裡將 SMTP 改為 Gmail REST API。OAuth 憑證直接內嵌在腳本裡(雲端沒有 gcloud)。
測試:用 RemoteTrigger run 立即觸發一次,5分鐘後檢查收件箱。
⚠️ 安全提醒
雲端觸發器腳本裡的 refresh_token 是明文的。任何能訪問你 claude.ai 帳號的人都能看到。

相比 App Password:refresh_token 可以隨時在 Google 帳號授權頁面 撤銷,而且撤銷後馬上失效。App Password 被盜後需要手動找到並刪除,相對麻煩。

實際建議:帳號開啟 2FA,定期檢查 Google 帳號的已授權應用列表。