with one click
Create a draft email in Gmail from provided email data
npx skills add https://github.com/sofer/.agents --skill gmail-draftCopy and paste this command into Claude Code to install the skill
Create a draft email in Gmail from provided email data
npx skills add https://github.com/sofer/.agents --skill gmail-draftCopy and paste this command into Claude Code to install the skill
Use when inspecting, testing, or reproducing behaviour in a non-public web app that requires a recognised login session, cookie, token, or browser-authenticated user.
Create a learning module for the platform. Teaches a topic collaboratively, structures it into chapters with steps and questions, then generates upload-ready markdown. Use when the user wants to create curriculum content.
Use when auditing a FAC platform Learn module, workshop module, lesson content, exercise content, module progress, or learning module report.
Guide for creating effective skills. This skill should be used when users want to create a new skill (or update an existing skill) that extends Codex's capabilities with specialized knowledge, workflows, or tool integrations.
Install Codex skills into $CODEX_HOME/skills from a curated list or a GitHub repo path. Use when a user asks to list installable skills, install a curated skill, or install a skill from another repo (including private repos).
Notice, log, review, and formalise candidate improvements to skills or reusable agent workflows without prematurely editing skills. Use when a conversation reveals repeated workflow friction, a user correction about skill behaviour, or a possible new skill pattern worth preserving for later review.
| name | gmail-draft |
| description | Create a draft email in Gmail from provided email data |
| type | atomic |
| input | Email data: to, subject, body (required); cc, bcc, thread_id, in_reply_to, references (optional) |
| output | Confirmation with draft ID, thread ID (if threaded), and a human-readable summary |
Create a draft email in the user's Gmail account. The draft is saved for human review — this skill never sends emails. It accepts already-composed email content and passes it through to the Gmail API.
This skill uses the existing Gmail draft script and OAuth credentials at ~/code/airmail/. The following files must exist:
~/code/airmail/gmail_draft.py — the draft creation script~/code/airmail/client_secret.json — Google OAuth client credentials~/code/airmail/gmail_token.json — OAuth token (auto-refreshes)~/code/airmail/email-signature.html — email signature (appended automatically)Check that the following fields are present in the input:
If any of these are missing or empty, stop and report which field(s) are missing. Do not proceed.
Use the existing gmail_draft.py script in ~/code/airmail/:
For a reply in an existing thread:
python3 ~/code/airmail/gmail_draft.py "<thread_id>" "<to>" "<body>"
For a new conversation:
python3 ~/code/airmail/gmail_draft.py new "<to>" "<subject>" "<body>"
The script:
email-signature.htmlOn success: Report the result with:
On error: Report the result with:
Do not attempt to retry or work around errors. Report them as-is.
If the script is unavailable or a more complex draft is needed (e.g. with CC/BCC), use the Gmail API directly:
import os
from google.oauth2.credentials import Credentials
from google.auth.transport.requests import Request
from googleapiclient.discovery import build
from email.mime.text import MIMEText
import base64
TOKEN_FILE = os.path.expanduser("~/code/airmail/gmail_token.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)
# Build the message
message = MIMEText(body)
message["to"] = to
message["subject"] = subject
if cc:
message["cc"] = cc
if in_reply_to:
message["In-Reply-To"] = in_reply_to
message["References"] = references or in_reply_to
raw = base64.urlsafe_b64encode(message.as_bytes()).decode()
draft_body = {"message": {"raw": raw}}
if thread_id:
draft_body["message"]["threadId"] = thread_id
draft = service.users().drafts().create(userId="me", body=draft_body).execute()
to, subject, or body is missing, stop and report which required field(s) are absent~/code/airmail/thread_id is invalid, do not fall back to creating an unthreaded draft — report the errorOn success:
status: created
draft_id: {id from API}
thread_id: {thread id, if threaded}
summary: Draft created: {subject} → {to}
On error:
status: error
error: {error description}