skills/gmail-fetch/SKILL.md
Fetch recent emails from Gmail with sender, subject, body, and thread context
npx skillsauth add sofer/.agents gmail-fetchInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
Fetch emails from Gmail so that other skills (particularly the email drafter twin) can work with inbox data. This skill is read-only; it never modifies, sends, or deletes emails.
This skill uses the Gmail API via existing OAuth credentials at ~/code/airmail/. The following files must exist:
~/code/airmail/client_secret.json — Google OAuth client credentials~/code/airmail/gmail_token.json — OAuth token (auto-refreshes)Load the OAuth credentials and build a Gmail API client:
from google.oauth2.credentials import Credentials
from google.auth.transport.requests import Request
from googleapiclient.discovery import build
TOKEN_FILE = os.path.expanduser("~/code/airmail/gmail_token.json")
CLIENT_SECRET = os.path.expanduser("~/code/airmail/client_secret.json")
creds = Credentials.from_authorized_user_file(TOKEN_FILE)
if creds.expired and creds.refresh_token:
creds.refresh(Request())
with open(TOKEN_FILE, "w") as f:
f.write(creds.to_json())
service = build("gmail", "v1", credentials=creds)
If the token file is missing, stop and report:
"Gmail token not found at ~/code/airmail/gmail_token.json. Run the OAuth flow in the airmail project first."
If authentication fails after refresh, stop and report:
"Gmail authentication failed. The token may be expired or revoked. Re-authenticate in ~/code/airmail/."
Accept the following optional parameters. Apply defaults where not provided:
| Parameter | Default | Description |
|---|---|---|
| query | is:unread | Gmail search query (same syntax as the Gmail search bar) |
| max_results | 20 | Maximum number of emails to return |
| include_threads | true | Whether to fetch prior messages in each thread |
List messages:
results = service.users().messages().list(
userId="me", q=query, maxResults=max_results
).execute()
messages = results.get("messages", [])
For each message ID returned, fetch full details:
msg = service.users().messages().get(
userId="me", id=msg_id, format="full"
).execute()
If include_threads is true, fetch the thread:
thread = service.users().threads().get(
userId="me", id=thread_id, format="full"
).execute()
For each email, extract and normalise:
From header)To header)Cc header, if present)Subject header)text/plain part, use it. If it only has text/html, strip the HTML tags to produce plain text. Truncate to 10,000 characters if longer, appending "[truncated]".Date header), formatted as ISO 8601If include_threads is true, for each thread include up to 10 of the most recent prior messages (excluding the current message) with:
Present the results as a structured list of emails ordered by date (most recent first), along with:
result_count: 3
query_used: "is:unread"
emails:
- id: "msg_abc123"
thread_id: "thread_xyz789"
from: "Alice Smith <[email protected]>"
to: ["[email protected]"]
cc: []
subject: "Re: Partnership proposal"
body: "Hi, thanks for sending over the details..."
date: "2026-02-25T09:30:00Z"
labels: ["INBOX", "UNREAD"]
snippet: "Hi, thanks for sending over the details..."
thread_messages:
- id: "msg_prev456"
from: "[email protected]"
date: "2026-02-24T16:00:00Z"
body: "Hi Alice, please find attached..."
tools
Check whether Claude and Codex have equivalent access to shared agent resources, skills, hooks, plugins, MCP servers, permissions, startup behaviour, and provider-specific adapter config. Use when comparing agent environments, debugging missing capabilities after restart, or deciding whether to symlink a resource or configure a runtime.
testing
Record substantive skill use in an append-only local log. Use after choosing or invoking a non-system skill for real work, when a skill is inspected but not used, or when a skill fails to apply. Do not use for routine system skills or incidental file reads.
testing
Turn a vague or underspecified request into a self-contained problem statement. Use when the user has a rough idea, when a request would fail if handed directly to an agent, or before non-trivial work that needs shared understanding.
data-ai
Append a one-line learning to ~/.agents/learning-log.md. Use when the user types /learning, or when something genuinely worth remembering surfaced during work and the user confirms it should be captured.