google-workspace

SKILL.md

Google Workspace Skill

Unified Google Workspace management for AI agents. One skill, one auth, eight services.

Replaces: gmail, google-calendar, google-contacts, google-drive, google-photos Run scripts/upgrade.sh to migrate from the old per-service skills.

Services

Service Script Capabilities
Gmail scripts/gmail.py Search, read, thread, draft, send, reply, reply-all, forward, trash, filters, empty-trash/spam
Calendar scripts/google_calendar.py List, get, search, create (RRULE), update, delete, quick-add, secondary calendars, Drive attachments
Contacts scripts/contacts.py Search, list, get, create, update, delete contacts
Drive scripts/google_drive.py List, search, upload, download, export, mkdir, move, copy, rename, trash, delete, empty-trash, share, revisions, comments
Docs scripts/google_docs.py Read text, create, append, replace, insert-heading, format-text, insert-image, comments
Sheets scripts/google_sheets.py Read, create, append, update, clear, tabs, format-range, freeze-panes, auto-resize
Photos scripts/google_photos.py List, search, download, upload media, manage albums
NotebookLM scripts/google_notebooklm.py Create/manage notebooks, add sources (URL/file/text/Drive), chat, generate audio/reports/quizzes/flashcards

Prerequisites

  1. Google Cloud Project with these APIs enabled:
    • Gmail API
    • Google Calendar API
    • Google People API
    • Google Drive API
    • Google Docs API
    • Google Sheets API
    • Photos Library API
  2. OAuth 2.0 Credentials β€” Desktop app client (credentials.json).

Setup

One-Time Setup (all services at once)

uv run scripts/setup_workspace.py

This authenticates once with all scopes and stores a unified token at ~/.google_workspace/.

Manual Setup (gcloud ADC)

gcloud auth application-default login \
  --scopes https://mail.google.com/,\
https://www.googleapis.com/auth/calendar,\
https://www.googleapis.com/auth/contacts,\
https://www.googleapis.com/auth/drive,\
https://www.googleapis.com/auth/documents,\
https://www.googleapis.com/auth/spreadsheets,\
https://www.googleapis.com/auth/photoslibrary,\
https://www.googleapis.com/auth/photoslibrary.sharing,\
https://www.googleapis.com/auth/cloud-platform

Install Token Maintenance (recommended)

Prevents hourly token expiry by running a systemd timer that refreshes the access token every 45 minutes:

bash scripts/install_services.sh
systemctl --user enable --now workspace-token-maintainer.timer

Verify

Run the preflight check first. It validates credentials, token, scopes, systemd timer, and API reachability in one command:

# Full check (credentials + token + API pings for all services)
uv run scripts/preflight.py

# Quick check (credentials + token only, no API pings)
uv run scripts/preflight.py --quick

The output is structured JSON with ok, error, and fix fields for each check. If all checks pass, exit code is 0.

You can also verify individual services:

uv run scripts/gmail.py verify
uv run scripts/google_calendar.py verify
uv run scripts/contacts.py verify
uv run scripts/google_drive.py verify
uv run scripts/google_docs.py verify
uv run scripts/google_sheets.py verify
uv run scripts/google_photos.py verify

Credential Lookup Order

Every script checks credentials in this order:

  1. Service-specific env var (e.g., GMAIL_CREDENTIALS_DIR) β€” for overrides.
  2. Unified directory ~/.google_workspace/ β€” primary location.
  3. Legacy per-service directory (e.g., ~/.gmail_credentials/) β€” backward compat.

No migration required. If you already have legacy credentials, they still work.


πŸ›‘ Notice: No More SQLite Cache

The local SQLite caching system (scripts/cache.py) has been removed in favor of live API queries. Do not attempt to use cache.py, sync, or search against a local database. Always use the live API commands provided by the individual service scripts (e.g., gmail.py search --query "...").


Gmail

Full CRUD Gmail management. Search, read, compose, reply, forward, and manage messages.

Search for Emails

# Unread emails from a sender
uv run scripts/gmail.py search --query "from:obiwan@jedi.org is:unread"

# Emails with attachments
uv run scripts/gmail.py search --query "has:attachment subject:plans" --limit 5

Read a Message / Thread

uv run scripts/gmail.py read --id "18e..."
uv run scripts/gmail.py thread --id "18e..."

# Prefer HTML body
uv run scripts/gmail.py read --id "18e..." --html

Create a Draft

Safest option β€” creates a draft for the user to review before sending.

uv run scripts/gmail.py draft \
  --to "yoda@dagobah.net" \
  --subject "Training Schedule" \
  --body "Master, when shall we begin the next session?" \
  --cc "mace@jedi.org"

# Create HTML draft
uv run scripts/gmail.py draft --to "yoda@dagobah.net" --subject "HTML Test" --body "<b>Bold</b>" --html

Send an Email

uv run scripts/gmail.py send \
  --to "yoda@dagobah.net" \
  --subject "Urgent: Sith Sighting" \
  --body "Master, I sense a disturbance in the Force."

# Send HTML
uv run scripts/gmail.py send --to "yoda@dagobah.net" --subject "HTML" --body "<h1>Alert</h1>" --html

Reply / Reply All / Forward

# Reply (draft by default, --send for immediate)
uv run scripts/gmail.py reply --id "18e..." --body "Acknowledged." --send

# Reply with HTML
uv run scripts/gmail.py reply --id "18e..." --body "<b>Agreed.</b>" --send --html

# Reply all
uv run scripts/gmail.py reply-all --id "18e..." --body "Council noted." --send

# Forward
uv run scripts/gmail.py forward --id "18e..." --to "luke@tatooine.net" --body "FYI"
uv run scripts/gmail.py forward --id "18e..." --to "luke@tatooine.net" --send

Trash / Labels / Attachments / Filters

uv run scripts/gmail.py trash --id "18e..."
uv run scripts/gmail.py untrash --id "18e..."

# Empty Trash or Spam (permanent β€” no recovery)
uv run scripts/gmail.py empty-trash
uv run scripts/gmail.py empty-spam

uv run scripts/gmail.py labels
uv run scripts/gmail.py modify-labels --id "18e..." --add STARRED --remove UNREAD

uv run scripts/gmail.py attachments --id "18e..." --output-dir ./downloads

# Filters β€” auto-route or label incoming mail
uv run scripts/gmail.py list-filters
uv run scripts/gmail.py create-filter \
  --from "noreply@galactic-senate.gov" --archive --mark-read
uv run scripts/gmail.py create-filter \
  --subject "URGENT" --add-label "IMPORTANT"
uv run scripts/gmail.py delete-filter --filter-id "ANe1BmhV..."

Safety Guidelines

  1. Prefer draft over send for new compositions β€” let the user review first.
  2. Reply/Forward defaults to draft β€” use --send only when explicitly requested.
  3. Trash is reversible β€” empty-trash is permanent, so confirm intent.

Token Maintenance & Persistence

7-Day Expiry Fix (Important)

If you are using a personal Gmail account with a "Testing" GCP project, your refresh token will expire every 7 days. To fix this, follow the guide at references/GCP_SETUP.md to set up a proper app or use an Internal app (Workspace users).

Background Refresh

To keep your 1-hour access token fresh and avoid CLI latency:

# Install the maintenance service
cp scripts/workspace-token-maintainer.service ~/.config/systemd/user/
cp scripts/workspace-token-maintainer.timer ~/.config/systemd/user/
systemctl --user enable --now workspace-token-maintainer.timer

This runs scripts/maintain_token.py every 45 minutes to refresh the token on disk.


Calendar

Full CRUD Calendar management. List, create, update, delete, and search events.

List / Search Events

# Next 5 events
uv run scripts/google_calendar.py list --limit 5

# Events in a date range
uv run scripts/google_calendar.py list \
  --after "2026-02-16T00:00:00Z" --before "2026-02-22T23:59:59Z"

# Search by text
uv run scripts/google_calendar.py search --query "Training" --limit 5

Create / Update / Delete Events

# Timed event
uv run scripts/google_calendar.py create \
  --summary "Jedi Council Meeting" \
  --start "2026-05-04T10:00:00" --end "2026-05-04T11:00:00" \
  --location "Council Chamber, Coruscant" \
  --attendees yoda@dagobah.net mace@jedi.org

# All-day event
uv run scripts/google_calendar.py create \
  --summary "May the 4th" --start "2026-05-04" --end "2026-05-05" --all-day

# Recurring event (RRULE β€” RFC 5545)
uv run scripts/google_calendar.py create \
  --summary "Weekly Stand-up" \
  --start "2026-03-03T09:00:00" --end "2026-03-03T09:30:00" \
  --rrule "RRULE:FREQ=WEEKLY;BYDAY=MO"

uv run scripts/google_calendar.py create \
  --summary "Monthly Report" \
  --start "2026-03-01T14:00:00" --end "2026-03-01T15:00:00" \
  --rrule "RRULE:FREQ=MONTHLY;BYMONTHDAY=1;COUNT=12"

# Event with Drive file attachment
uv run scripts/google_calendar.py create \
  --summary "Design Review" \
  --start "2026-03-10T14:00:00" --end "2026-03-10T15:00:00" \
  --drive-files "FILE_ID_1" "FILE_ID_2"

# Update (patch semantics β€” only provided fields change)
uv run scripts/google_calendar.py update --id "abc123" --summary "Updated Meeting"

# Delete
uv run scripts/google_calendar.py delete --id "abc123"

Quick Add / Calendar Management

uv run scripts/google_calendar.py quick --text "Lunch with Padme tomorrow at noon"

# List all calendars
uv run scripts/google_calendar.py calendars

# Subscribe to a secondary calendar (e.g. public holiday calendar)
uv run scripts/google_calendar.py add-calendar \
  --calendar-id "en.usa#holiday@group.v.calendar.google.com"

# Unsubscribe
uv run scripts/google_calendar.py remove-calendar \
  --calendar-id "en.usa#holiday@group.v.calendar.google.com"

Contacts

Full CRUD Contacts management via the People API.

Search / List / Get

uv run scripts/contacts.py search --query "Han Solo"
uv run scripts/contacts.py list --limit 50
uv run scripts/contacts.py get --id "people/c12345"

Create / Update / Delete

uv run scripts/contacts.py create \
  --first "Lando" --last "Calrissian" \
  --email "lando@cloudcity.com" --phone "555-0123" \
  --org "Cloud City Administration" --title "Baron Administrator"

# Update (patch semantics, etag-based conflict detection)
uv run scripts/contacts.py update --id "people/c12345" --phone "555-9999" --title "General"

uv run scripts/contacts.py delete --id "people/c12345"

Drive

Full CRUD Drive management. List, search, upload, download, export, organize, and share files.

List / Search / Get

uv run scripts/google_drive.py list
uv run scripts/google_drive.py list --folder "FOLDER_ID" --limit 20
uv run scripts/google_drive.py search --query "Death Star plans"
uv run scripts/google_drive.py search --query "budget" --mime-type "application/vnd.google-apps.spreadsheet"
uv run scripts/google_drive.py get --id "FILE_ID"

Upload / Download / Export

uv run scripts/google_drive.py upload --file "./blueprints.pdf" --folder "FOLDER_ID"
uv run scripts/google_drive.py download --id "FILE_ID" --output "./local_copy.pdf"

# Export Google Workspace docs
uv run scripts/google_drive.py export --id "DOC_ID" --output "./report.docx" --format docx
uv run scripts/google_drive.py export --id "SHEET_ID" --output "./data.csv" --format csv

Export Formats: Google Docs (pdf, docx, txt, html, md), Sheets (pdf, xlsx, csv), Slides (pdf, pptx), Drawings (pdf, png, svg).

Organize (mkdir, move, copy, rename)

uv run scripts/google_drive.py mkdir --name "Project Stardust" --parent "PARENT_ID"
uv run scripts/google_drive.py move --id "FILE_ID" --to "DEST_FOLDER_ID"
uv run scripts/google_drive.py copy --id "FILE_ID" --name "Copy of Plans"
uv run scripts/google_drive.py rename --id "FILE_ID" --name "Updated Plans v2"

Trash / Delete / Share / Revisions / Comments

# Soft delete (reversible)
uv run scripts/google_drive.py trash --id "FILE_ID"
uv run scripts/google_drive.py untrash --id "FILE_ID"

# Empty entire trash (permanent, no recovery)
uv run scripts/google_drive.py empty-trash

# Permanent delete of a specific file
uv run scripts/google_drive.py delete --id "FILE_ID"

# Share
uv run scripts/google_drive.py share --id "FILE_ID" --email "luke@tatooine.net" --role writer
uv run scripts/google_drive.py share --id "FILE_ID" --type anyone --role reader
uv run scripts/google_drive.py permissions --id "FILE_ID"
uv run scripts/google_drive.py unshare --id "FILE_ID" --permission-id "PERM_ID"

# Revision history
uv run scripts/google_drive.py list-revisions --id "FILE_ID"
uv run scripts/google_drive.py restore-revision --id "FILE_ID" --revision-id "REV_ID"

# Comments
uv run scripts/google_drive.py list-comments --id "FILE_ID"
uv run scripts/google_drive.py add-comment --id "FILE_ID" --content "LGTM, approved."

Docs

Google Docs content manipulation. Read text, create documents, and modify text.

Read / Create

# Read text from a document
uv run scripts/google_docs.py read --id "DOC_ID"

# Create a new document
uv run scripts/google_docs.py create --title "Project Requirements"

Edit Text

# Append text to the end of the document
uv run scripts/google_docs.py append --id "DOC_ID" --text "Additional requirements:\n1. Must be fast."

# Replace text
uv run scripts/google_docs.py replace --id "DOC_ID" --old "[COMPANY_NAME]" --new "Acme Corp"

# Insert a heading
uv run scripts/google_docs.py insert-heading --id "DOC_ID" --text "Project Overview" --level 1

# Format a text range (index-based)
uv run scripts/google_docs.py format-text --id "DOC_ID" --start 0 --end 20 --bold
uv run scripts/google_docs.py format-text --id "DOC_ID" --start 0 --end 20 --italic --underline

# Insert a public image
uv run scripts/google_docs.py insert-image --id "DOC_ID" --uri "https://example.com/banner.png"

Comments

uv run scripts/google_docs.py list-comments --id "DOC_ID"
uv run scripts/google_docs.py add-comment --id "DOC_ID" --content "Please review this section."

Sheets

Google Sheets manipulation. Read, append, update, and clear ranges.

Read / Create

# Read values from a specific range
uv run scripts/google_sheets.py read --id "SHEET_ID" --range "Sheet1!A1:D10"

# Create a new spreadsheet
uv run scripts/google_sheets.py create --title "Q3 Budget"

Update / Append / Clear / Format

# Append a row (expects a 2D JSON array)
uv run scripts/google_sheets.py append --id "SHEET_ID" --range "Sheet1!A:A" --values '[["2026-03-01", "Rent", 1200]]'

# Update a specific range
uv run scripts/google_sheets.py update --id "SHEET_ID" --range "Sheet1!A2:C2" --values '[["2026-03-02", "Utilities", 150]]'

# Clear a range
uv run scripts/google_sheets.py clear --id "SHEET_ID" --range "Sheet1!A2:D10"

# Worksheet tab management
uv run scripts/google_sheets.py list-sheets --id "SHEET_ID"
uv run scripts/google_sheets.py add-sheet --id "SHEET_ID" --title "February"
uv run scripts/google_sheets.py rename-sheet --id "SHEET_ID" --title "January" --new-title "Jan 2026"
uv run scripts/google_sheets.py delete-sheet --id "SHEET_ID" --title "February"

# Format a range (bold header, background color)
uv run scripts/google_sheets.py format-range \
  --id "SHEET_ID" --range "Sheet1!A1:D1" --bold --background-color "#4A90D9"

# Freeze the first row and first column
uv run scripts/google_sheets.py freeze-panes --id "SHEET_ID" --rows 1 --cols 1

# Auto-resize columns to fit content
uv run scripts/google_sheets.py auto-resize --id "SHEET_ID" --range "Sheet1!A:D"

Photos

Google Photos management. Browse, search, download, upload, and organize albums.

API Limitation: The Photos Library API only allows modifying media items uploaded by this application.

List / Search / Get / Download

uv run scripts/google_photos.py list
uv run scripts/google_photos.py search --date-start "2026-01-01" --date-end "2026-02-16"
uv run scripts/google_photos.py search --categories LANDSCAPES FOOD
uv run scripts/google_photos.py get --id "MEDIA_ID"
uv run scripts/google_photos.py download --id "MEDIA_ID" --output "./sunset.jpg"

Search Categories: ANIMALS, ARTS, BIRTHDAYS, CITYSCAPES, CRAFTS, DOCUMENTS, FASHION, FLOWERS, FOOD, GARDENS, HOLIDAYS, HOUSES, LANDMARKS, LANDSCAPES, NIGHT, PEOPLE, PERFORMANCES, PETS, RECEIPTS, SCREENSHOTS, SELFIES, SPORT, TRAVEL, UTILITY, WEDDINGS, WHITEBOARDS.

Upload

uv run scripts/google_photos.py upload \
  --file "./hologram.jpg" --description "Leia's holographic message" --album "ALBUM_ID"

Albums

uv run scripts/google_photos.py albums
uv run scripts/google_photos.py album-get --id "ALBUM_ID"
uv run scripts/google_photos.py album-create --title "Tatooine Sunsets"
uv run scripts/google_photos.py album-add --album "ALBUM_ID" --items "ITEM_1" "ITEM_2"
uv run scripts/google_photos.py album-remove --album "ALBUM_ID" --items "ITEM_1"

Upgrading from v1 (per-service skills)

If you previously installed the separate gmail, google-calendar, google-contacts, google-drive, and/or google-photos skills:

# Dry run β€” shows what will change without modifying anything
bash scripts/upgrade.sh --dry-run

# Run the migration
bash scripts/upgrade.sh

The upgrade script:

  1. Consolidates credentials β€” copies token.json and credentials.json from any legacy dir (~/.gmail_credentials/, etc.) into ~/.google_workspace/.
  2. Removes old skill installations β€” deletes old per-service skill directories from .agent/skills/ if present.
  3. Preserves legacy dirs β€” does not delete ~/.gmail_credentials/ etc., so existing tools that depend on them still work.
  4. Verifies auth β€” confirms the unified token works for all services.

NotebookLM

Manage Google NotebookLM notebooks, sources, artifacts, and chat.

Auth: NotebookLM uses browser cookie auth (not OAuth). Auth state is stored at ~/.notebooklm/storage_state.json.

Setup

# Interactive setup: copies your __Secure-1PSID cookie from an open browser session
uv run scripts/google_notebooklm.py setup

# Or set the cookie value directly
uv run scripts/google_notebooklm.py setup --cookie "YOUR_COOKIE_VALUE"

Common Commands

# List notebooks
uv run scripts/google_notebooklm.py list

# Create a notebook
uv run scripts/google_notebooklm.py create --title "Death Star Schematics"

# Add sources
uv run scripts/google_notebooklm.py add-url --id NOTEBOOK_ID --url "https://example.com/article"
uv run scripts/google_notebooklm.py add-file --id NOTEBOOK_ID --file ./document.pdf
uv run scripts/google_notebooklm.py add-text --id NOTEBOOK_ID --title "Notes" --text "Key findings..."
uv run scripts/google_notebooklm.py add-drive --id NOTEBOOK_ID --drive-id "DRIVE_FILE_ID"

# Chat with a notebook
uv run scripts/google_notebooklm.py chat --id NOTEBOOK_ID --message "Summarize the main themes"

# Generate artifacts
uv run scripts/google_notebooklm.py generate-audio --id NOTEBOOK_ID
uv run scripts/google_notebooklm.py generate-report --id NOTEBOOK_ID --type "study_guide"
uv run scripts/google_notebooklm.py generate-quiz --id NOTEBOOK_ID
uv run scripts/google_notebooklm.py generate-flashcards --id NOTEBOOK_ID

# Download generated artifacts
uv run scripts/google_notebooklm.py list-artifacts --id NOTEBOOK_ID
uv run scripts/google_notebooklm.py download-audio --id NOTEBOOK_ID --output ./overview.mp3
uv run scripts/google_notebooklm.py download-report --id NOTEBOOK_ID --output ./report.md

JSON Output

All commands produce JSON for easy parsing. See the individual service sections above for output schemas.

All error outputs include structured JSON with status, error, and type fields. Auth errors additionally include a fix field with the exact command to run:

{
  "status": "error",
  "message": "Gmail API not authenticated.",
  "type": "AuthError"
}

Troubleshooting

Token expired / 401 errors

Access tokens expire after 1 hour. If the systemd timer is running, tokens refresh automatically. If not:

# Check timer status
systemctl --user status workspace-token-maintainer.timer

# Install and enable if missing
bash scripts/install_services.sh
systemctl --user enable --now workspace-token-maintainer.timer

7-day refresh token expiry (@gmail.com accounts)

GCP projects in "Testing" status cause refresh tokens to expire every 7 days. Options:

  1. Publish the app in GCP Console (OAuth consent screen > Publish) to get permanent refresh tokens.
  2. Re-authenticate weekly: uv run scripts/setup_workspace.py
  3. Use a Google Workspace account instead of @gmail.com.

Missing scopes

If a service returns scope errors, re-run the unified setup to request all scopes:

uv run scripts/setup_workspace.py

credentials.json not found

Download OAuth client secret from GCP Console > APIs & Services > Credentials, then save to ~/.google_workspace/credentials.json. See references/GCP_SETUP.md for step-by-step instructions.

Weekly Installs
11
First Seen
Feb 17, 2026
Installed on
codex11
opencode10
github-copilot10
kimi-cli10
gemini-cli10
amp10