Skip to main content

Google integration

A small suite of MCP tools lets the agent talk to your Google account: Gmail, Calendar, Contacts, Drive, Sheets, Docs, Slides. OAuth tokens live in the macOS Keychain (or 0600 files on Linux), never on disk in plaintext or in .env.

One-time setup

  1. Go to console.cloud.google.com/apis/credentials.
  2. Create credentials → OAuth client ID → Desktop app.
  3. Copy the client ID + secret into .env:
    GOOGLE_OAUTH_CLIENT_ID=…
    GOOGLE_OAUTH_CLIENT_SECRET=…
  4. Enable the APIs you'll use (Gmail, Calendar, Drive, Sheets, Docs, Slides, People).
  5. Add yourself as a test user under the OAuth consent screen (or publish the app).
  6. Log in:
    bun run google login # default account
    bun run google login work # named alias

The browser will pop up; finish the consent flow. A refresh token is stored in Keychain under google-refresh-token:<alias>.

Multiple accounts

bun run google login personal
bun run google login work
bun run google status # which accounts are linked, which is default
bun run google logout work # forget one
bun run google test # smoke-check the default account

All MCP tools accept an optional account argument; if omitted the default alias is used.

Available tools

Defined in src/mcp/. The agent sees them automatically because the MCP server is wired up in .mcp.json.

ToolSourceWhat it does
gmail_recentgmail.tsList recent inbox messages
gmail_searchgmail.tsSearch Gmail with the standard query language
gmail_getgmail.tsFetch a specific message (headers + body)
gmail_sendgmail.tsCompose and send
contacts_searchcontacts.tsResolve a name to email/phone
calendar_list_eventscalendar.tsList events in a time range
calendar_create_eventcalendar.tsCreate an event
calendar_rawcalendar.tsRaw HTTP escape hatch to the Calendar API
drive_searchdrive.tsSearch Drive by name/mime/owner
drive_readdrive.tsRead a file's content (export as text if it's a Google doc)
drive_rawdrive.tsRaw HTTP escape hatch
sheets_readsheets.tsRead a range of cells
sheets_writesheets.tsWrite a range of cells
sheets_rawsheets.tsRaw HTTP escape hatch
docs_readdocs.tsRead a Google Doc as text
docs_rawdocs.tsRaw HTTP escape hatch
slides_readslides.tsRead a Google Slides deck
slides_rawslides.tsRaw HTTP escape hatch
google_accountsgoogle_accounts.tsList linked aliases + default

Each "raw" tool is a thin pass-through to the underlying Google API endpoint with auth bolted on. They exist so the agent can do things we haven't wrapped — sending an HTTP path + body — without you having to add a new TypeScript tool.

Scopes

Scopes granted per account are stored alongside the refresh token (google-scopes:<alias>). Adding a new tool may require re-logging in to grant new scopes:

bun run google logout personal
bun run google login personal

Security model

  • Refresh tokens never enter .env or the SQLite DB.
  • The MCP server receives only the OAuth client ID/secret via env passthrough — the refresh token is read from Keychain on demand inside the tool handler.
  • Access tokens are fetched from Google at call time, used once, discarded.
  • The token storage layer (src/google/keychain.ts) prefers macOS security CLI; on Linux it falls back to ~/.config/marsclaw/secrets/<key> with mode 0600.

Failure modes

SymptomCauseFix
invalid_grantRefresh token revoked (you removed access from myaccount.google.com/permissions)bun run google login <alias>
insufficient_scopeTool needs a scope you didn't grantlogout + login again
Quota exceededDaily project-level quota in Google CloudBump quota in console, or wait
No default accountNever logged inbun run google login
Tool can't find an aliasTypo / not logged inbun run google status