A Claude Code skill that reads Gmail using Chrome's cached session cookies — no OAuth setup required.
Chrome stores Google session cookies in a local SQLite database, encrypted with a key from the macOS Keychain. This skill:
- Reads the Chrome cookies database at
~/Library/Application Support/Google/Chrome/Default/Cookies - Decrypts cookie values using the Chrome Safe Storage key from macOS Keychain
- Authenticates to Gmail's Atom feed (
mail.google.com/mail/feed/atom/) using those cookies - Parses and returns unread messages
No OAuth flow. No API key. No app registration. Just reuses credentials Chrome already has.
- macOS (tested on macOS 15+)
- Google Chrome with Gmail logged in
- Python 3.8+
pycryptodomelibrary
pip install pycryptodome
# If that fails (Python 3.12+ externally managed):
pip install pycryptodome --break-system-packages# List 5 most recent unread messages (default account)
python3 ~/.claude/skills/gmail-access/gmail_unread.py
# For second Google account in Chrome
python3 ~/.claude/skills/gmail-access/gmail_unread.py 1from gmail_unread import get_google_cookies, fetch_unread
cookies = get_google_cookies()
messages = fetch_unread(cookies, account=0, max_results=5)
for msg in messages:
print(f"{msg['from_name']} <{msg['from_email']}>: {msg['subject']}")Ask Claude to read your Gmail:
"Check my Gmail for unread messages" "What's in my inbox?" "Do I have any unread emails?"
Claude will invoke the gmail-access skill automatically.
=== 5 Most Recent Unread Messages ===
1. GitHub <noreply@github.com>
Subject: [repo] PR #42 merged
Preview: Your pull request was merged into main.
2. Stripe <receipts@stripe.com>
Subject: Your receipt from March 5
Preview: Thanks for your payment of $29.00...
| Problem | Solution |
|---|---|
Chrome Safe Storage key not found |
Open Chrome and ensure you're logged into Gmail |
No Google cookies found |
Log into Gmail in Chrome, then retry |
401 Auth failed / no messages |
Cookies may be expired — visit Gmail in Chrome to refresh |
Database is locked |
Chrome may be running. Try quitting Chrome first, or it may work anyway (WAL mode) |
ImportError: No module named 'Crypto' |
Run pip install pycryptodome |
Could not decrypt cookie |
Chrome may have updated its format. See docs/chrome-encryption.md |
gmail-access/
SKILL.md # Claude skill metadata and quick reference
gmail_unread.py # Main script (also importable as a library)
README.md # This file
docs/
chrome-encryption.md # Chrome cookie encryption format (deep dive)
gmail-atom-feed.md # Gmail Atom feed API reference
Chrome 145 (and likely 127+) uses a new format on macOS:
[v10][32-byte metadata][AES-128-CBC encrypted value]
Older Chrome used:
[v10][AES-128-CBC encrypted value]
The script handles both formats by trying both offsets. See docs/chrome-encryption.md for a full breakdown including how this was discovered.
The Atom feed (/mail/feed/atom/) is used instead of the Gmail REST API (googleapis.com/gmail/v1/) because:
- REST API requires OAuth2 Bearer tokens (not available from cookie cache)
- Atom feed accepts browser session cookies directly
- Atom feed supports Gmail search operators via the
qparameter
See docs/gmail-atom-feed.md for full Atom feed documentation including search operators and response format.
- Cookies are read-only; the script never modifies browser data
- The temp copy of the cookies DB is deleted immediately after reading
- Cookies are sent only to
mail.google.comover HTTPS - The Chrome Safe Storage key is accessed via
security find-generic-password, which may prompt for Keychain access
- macOS + Chrome only — relies on macOS Keychain and Chrome's specific storage format
- Read-only — cannot send mail, archive, or modify messages via the Atom feed
- Unread only (by default) — change
q=is:unreadto search other messages - No full body — Atom feed provides a ~200-char summary, not the complete email
- Cookies must be valid — if you've been logged out of Gmail in Chrome, re-login first