@davidianstyle/google-mcp
v0.2.0
Published
Consolidated Google MCP server — Gmail, Calendar, Meet, Drive, Docs, Sheets, Slides in one process.
Maintainers
Readme
google-mcp
A consolidated Google Workspace MCP server: Gmail, Calendar, Meet, Drive, Docs, Sheets, and Slides tools in a single stdio process. Written in TypeScript against the official googleapis client.
Process model: one process per account
Each running server is bound to exactly one Google account, selected with --slug:
google-mcp --slug jane-acme-com [--token-dir <dir>]- Credentials are read from
<token-dir>/google-<slug>-credentials.json;--token-dirdefaults to~/.config/openbrain/tokens, so the usual path is~/.config/openbrain/tokens/google-<slug>-credentials.json. - The credentials file holds the OAuth client id/secret and a refresh token. Refreshed access tokens are automatically persisted back to the file (atomic tmp-file + rename), so new processes reuse them instead of re-refreshing.
- In practice these processes are launched by openbrain's
google-mcp.sh, one per configured account.
Adding an account
Accounts are provisioned by bootstrap/lib/add-google-account.sh, which lives in the external openbrain repo (not in this repository). It runs the OAuth consent flow and writes the credentials file for the slug. Re-run it for an existing slug to grant newly required scopes (error messages from this server reference it when a refresh token has expired or been revoked).
Hosted / env-token mode
For hosts that own token custody themselves (e.g. a hosted runtime that decrypts a per-connection OAuth bundle and refreshes tokens centrally), the server can instead take a ready-to-use access token from an environment variable:
GOOGLE_ACCESS_TOKEN=ya29... google-mcp --access-token-env # reads GOOGLE_ACCESS_TOKEN
MY_TOKEN_VAR=ya29... google-mcp --access-token-env MY_TOKEN_VAR- The token is read once at startup and used for the life of the process. Refresh is the host's job — there is no refresh token, no OAuth client id/secret, and on a 401 the readable API error is surfaced for the host to classify; the server never attempts a refresh.
- Nothing touches disk: no credentials file is read and no token writeback of any kind happens.
- The flag is mutually exclusive with an explicit
--token-dir.--slugbecomes optional and, when given, is only used as an account label in error hints. - Startup fails fast with a clear error naming the variable if it is unset or empty.
Tool families
| Family | Prefix | What it covers |
|---|---|---|
| Gmail | gmail_* | Search/read/send/reply (threaded, with attachments), threads, drafts, labels, filters, vacation responder, profile |
| Calendar | calendar_* | List/create/update/respond to events, recurring-event instances, free/busy, propose meeting times, Meet links on events, out-of-office/working-location events |
| Meet | meet_* | Create standalone Meet spaces, list conference records, fetch transcripts |
| Drive | drive_* | List/search/move/copy/rename/delete, folder management, download/upload, sharing and permissions |
| Docs | docs_* | Read/write Google Docs (native markdown in/out), text styling, tables, tabs, comments |
| Sheets | sheets_* | Read/write values (single, batch, append), sheet/tab management, formatting, borders/merges, charts, find/replace, named ranges |
| Slides | slides_* | Create/edit presentations, slides, text/shapes/images, styling, PDF export, thumbnails |
By default every family registers. --families gmail,calendar restricts the server to just the listed families (valid names: gmail, calendar, meet, drive, docs, sheets, slides); an unknown name fails startup with an error listing the valid ones. Works in both auth modes.
Operational defaults
- Compact output — tool results are unindented JSON; responses are pruned to the fields a model actually needs (list views trim heavyweight nested objects).
- Size caps —
docs_read_documentcaps output at 50k characters (raise viamaxLength); Gmail message bodies are capped at 50k characters with a truncation note; Sheets reads fail loudly past a 10,000-cell cap (narrow the range and retry); Gmail attachment downloads over 2 MB are written to disk instead of returned inline (Drive downloads inline up to 10 MB). - Pagination — list-style tools accept a
pageTokenand return anextPageTokenwhen more results exist; tools that fan out to multiple underlying lists use a single composite token with the same contract. - Retries — every Google API call retries 429s and 5xx errors up to 3 times globally, honoring the server's
Retry-Afterheader. - Error mapping — all tool handlers are wrapped centrally; Google API failures come back as readable errors (HTTP status + API message) with hints for expired auth and quota problems.
- Token persistence — refreshed access tokens are written back to the per-account credentials file automatically.
- Downloads — files fetched to disk land in a shared temp cache (
$TMPDIR/google-mcp) that is swept of entries older than 24h.
Meet notes
meet_create_linkcreates a standalone Meet space via the Meet REST API v2 (spaces.create) — nothing is placed on the calendar. For a titled, scheduled meeting usecalendar_create_event(withconferenceData) orcalendar_add_meet_link.- Scope requirement:
spaces.createneeds thehttps://www.googleapis.com/auth/meetings.space.createdOAuth scope. Existing accounts were authorized before this scope was requested and currently get403 — Request had insufficient authentication scopesfrommeet_create_link(verified live). Re-runadd-google-account.sh(external openbrain repo) for the account slug to re-consent with the new scope. - Conference records and transcripts require Google Workspace (transcripts: Business Standard or higher).
Development
npm install
npm run build # tsc -> dist/
npm test # vitest run
npm run dev # tsx src/index.ts (needs --slug)CI runs tsc --noEmit and the vitest suite on every push/PR.
