$npx -y skills add AlexAI-MCP/hermes-CCC --skill google-workspaceAutomate Google Workspace — Gmail, Drive, Sheets, Docs, Calendar via Google API Python client or gcloud CLI.
| 1 | # Google Workspace |
| 2 | |
| 3 | Automate Gmail, Drive, Sheets, Docs, and Calendar via the Google API Python client. |
| 4 | |
| 5 | ## Setup |
| 6 | |
| 7 | ```bash |
| 8 | pip install google-api-python-client google-auth-httplib2 google-auth-oauthlib |
| 9 | ``` |
| 10 | |
| 11 | Create credentials at console.cloud.google.com → APIs & Services → Credentials → OAuth 2.0. |
| 12 | |
| 13 | ```python |
| 14 | from google.oauth2.credentials import Credentials |
| 15 | from google_auth_oauthlib.flow import InstalledAppFlow |
| 16 | from googleapiclient.discovery import build |
| 17 | import os, pickle |
| 18 | |
| 19 | SCOPES = [ |
| 20 | "https://www.googleapis.com/auth/gmail.readonly", |
| 21 | "https://www.googleapis.com/auth/drive", |
| 22 | "https://www.googleapis.com/auth/spreadsheets", |
| 23 | "https://www.googleapis.com/auth/calendar", |
| 24 | ] |
| 25 | |
| 26 | def get_credentials(): |
| 27 | creds = None |
| 28 | if os.path.exists("token.pickle"): |
| 29 | with open("token.pickle", "rb") as f: |
| 30 | creds = pickle.load(f) |
| 31 | if not creds or not creds.valid: |
| 32 | flow = InstalledAppFlow.from_client_secrets_file("credentials.json", SCOPES) |
| 33 | creds = flow.run_local_server(port=0) |
| 34 | with open("token.pickle", "wb") as f: |
| 35 | pickle.dump(creds, f) |
| 36 | return creds |
| 37 | ``` |
| 38 | |
| 39 | --- |
| 40 | |
| 41 | ## Gmail |
| 42 | |
| 43 | ```python |
| 44 | service = build("gmail", "v1", credentials=get_credentials()) |
| 45 | |
| 46 | # List messages |
| 47 | msgs = service.users().messages().list(userId="me", q="is:unread", maxResults=10).execute() |
| 48 | |
| 49 | # Read message |
| 50 | def read_email(msg_id): |
| 51 | msg = service.users().messages().get(userId="me", id=msg_id, format="full").execute() |
| 52 | headers = {h["name"]: h["value"] for h in msg["payload"]["headers"]} |
| 53 | return { |
| 54 | "subject": headers.get("Subject"), |
| 55 | "from": headers.get("From"), |
| 56 | "snippet": msg["snippet"] |
| 57 | } |
| 58 | |
| 59 | # Send email |
| 60 | import base64 |
| 61 | from email.mime.text import MIMEText |
| 62 | |
| 63 | def send_email(to, subject, body): |
| 64 | msg = MIMEText(body) |
| 65 | msg["to"] = to |
| 66 | msg["subject"] = subject |
| 67 | raw = base64.urlsafe_b64encode(msg.as_bytes()).decode() |
| 68 | service.users().messages().send(userId="me", body={"raw": raw}).execute() |
| 69 | ``` |
| 70 | |
| 71 | --- |
| 72 | |
| 73 | ## Google Drive |
| 74 | |
| 75 | ```python |
| 76 | drive = build("drive", "v3", credentials=get_credentials()) |
| 77 | |
| 78 | # List files |
| 79 | files = drive.files().list( |
| 80 | q="mimeType='application/vnd.google-apps.spreadsheet'", |
| 81 | fields="files(id, name, modifiedTime)" |
| 82 | ).execute() |
| 83 | |
| 84 | # Upload file |
| 85 | from googleapiclient.http import MediaFileUpload |
| 86 | media = MediaFileUpload("report.pdf", mimetype="application/pdf") |
| 87 | drive.files().create( |
| 88 | body={"name": "report.pdf", "parents": ["FOLDER_ID"]}, |
| 89 | media_body=media |
| 90 | ).execute() |
| 91 | |
| 92 | # Download file |
| 93 | import io |
| 94 | from googleapiclient.http import MediaIoBaseDownload |
| 95 | request = drive.files().get_media(fileId="FILE_ID") |
| 96 | fh = io.BytesIO() |
| 97 | downloader = MediaIoBaseDownload(fh, request) |
| 98 | done = False |
| 99 | while not done: |
| 100 | _, done = downloader.next_chunk() |
| 101 | ``` |
| 102 | |
| 103 | --- |
| 104 | |
| 105 | ## Google Sheets |
| 106 | |
| 107 | ```python |
| 108 | sheets = build("sheets", "v4", credentials=get_credentials()) |
| 109 | |
| 110 | SHEET_ID = "your-spreadsheet-id" |
| 111 | |
| 112 | # Read range |
| 113 | result = sheets.spreadsheets().values().get( |
| 114 | spreadsheetId=SHEET_ID, range="Sheet1!A1:D10" |
| 115 | ).execute() |
| 116 | rows = result.get("values", []) |
| 117 | |
| 118 | # Write range |
| 119 | sheets.spreadsheets().values().update( |
| 120 | spreadsheetId=SHEET_ID, |
| 121 | range="Sheet1!A1", |
| 122 | valueInputOption="RAW", |
| 123 | body={"values": [["Name", "Score"], ["Alice", 95], ["Bob", 87]]} |
| 124 | ).execute() |
| 125 | |
| 126 | # Append rows |
| 127 | sheets.spreadsheets().values().append( |
| 128 | spreadsheetId=SHEET_ID, |
| 129 | range="Sheet1!A:A", |
| 130 | valueInputOption="RAW", |
| 131 | body={"values": [["New Row", "Data"]]} |
| 132 | ).execute() |
| 133 | ``` |
| 134 | |
| 135 | --- |
| 136 | |
| 137 | ## Google Calendar |
| 138 | |
| 139 | ```python |
| 140 | cal = build("calendar", "v3", credentials=get_credentials()) |
| 141 | |
| 142 | # List events |
| 143 | events = cal.events().list( |
| 144 | calendarId="primary", |
| 145 | maxResults=10, |
| 146 | singleEvents=True, |
| 147 | orderBy="startTime" |
| 148 | ).execute() |
| 149 | |
| 150 | for event in events.get("items", []): |
| 151 | print(event["summary"], event["start"].get("dateTime")) |
| 152 | |
| 153 | # Create event |
| 154 | cal.events().insert( |
| 155 | calendarId="primary", |
| 156 | body={ |
| 157 | "summary": "Team Meeting", |
| 158 | "start": {"dateTime": "2026-04-08T10:00:00+09:00"}, |
| 159 | "end": {"dateTime": "2026-04-08T11:00:00+09:00"}, |
| 160 | "attendees": [{"email": "colleague@example.com"}], |
| 161 | } |
| 162 | ).execute() |
| 163 | ``` |
| 164 | |
| 165 | --- |
| 166 | |
| 167 | ## gcloud CLI Alternative |
| 168 | |
| 169 | ```bash |
| 170 | # Auth |
| 171 | gcloud auth login |
| 172 | gcloud auth application-default login |
| 173 | |
| 174 | # Drive: list files |
| 175 | gdrive list |
| 176 | |
| 177 | # Sheets: export as CSV |
| 178 | gsheet export SHEET_ID --format csv |
| 179 | ``` |