gws-mcp
# gws-mcp
> [!IMPORTANT]
> The OAuth app behind this repo is published but **not verified** by Google,
> and it serves only my own two Google accounts. The consent screen shows the
> "Google hasn't verified this app" warning by design, and its unverified user
> cap leaves no room for anyone else. To run this yourself, create your own
> Google Cloud project and Desktop OAuth client with the same three scopes. The
> setup section below covers every step, and the account emails come from
> environment variables, never from code. If enough demand shows up I may put
> the app through Google's verification process. Open an issue if you want
> that.
Minimal Google Workspace MCP server for Claude Code. Gmail, Drive, Docs,
Sheets, and Calendar across two fixed accounts. Thirteen tools, roughly
1,750 schema tokens.
It replaces `workspace-mcp`, whose same-service surface measured 65 tools and
roughly 28,000 tokens.
## Tool surface
| Tool | Purpose |
| --- | --- |
| `gmail_search` | Gmail query syntax in, message metadata out |
| `gmail_read` | Decoded body of one message or a whole thread |
| `gmail_send` | Send or draft, `thread_id` makes it a reply |
| `gmail_modify` | Label names on messages or threads, covers archive, read state, star, trash |
| `drive_search` | Drive query plus MIME filter |
| `drive_get` | Docs export as Markdown, Sheets as CSV, text files raw |
| `drive_update` | Batch move, rename, trash. Per-file results |
| `drive_folder_create` | New folder under a parent |
| `docs_create` | New Doc from Markdown |
| `docs_update` | Append text or replace all occurrences |
| `sheets_read` | A1 range in, values out |
| `sheets_write` | Write or append to an A1 range |
| `calendar_event_create` | New event, all-day or timed, optional RRULE recurrence and popup reminder |
Every tool takes `account`, an enum of `personal` and `professional`. The
alias-to-email map comes from `GWS_MCP_PERSONAL_EMAIL` and
`GWS_MCP_PROFESSIONAL_EMAIL`, set in `.envrc`.
No tool can permanently delete anything. Trashing is the only removal the
tool surface performs. The stored token itself carries the full `drive`
scope, which Google classifies as restricted, so guard the token store, and
know that the credential could do more than the tools allow.
## Google Cloud setup (one time)
1. Create a project at https://console.cloud.google.com/projectcreate. The
string "google" is banned in project IDs and app names.
2. Enable five APIs: Gmail, Google Drive, Google Docs, Google Sheets,
Google Calendar.
3. Google Auth Platform: External audience, no test users.
4. Publish to Production immediately. A Testing-status app expires every
refresh token 7 days after consent.
5. Create a Desktop-app OAuth client. Copy the client ID and secret.
6. Do not submit the app for verification. The unverified-app warning at
consent time is expected.
## Secrets and environment
The client credentials live in safetybox:
```
gwsmcp/google-client-id -> GWS_MCP_GOOGLE_CLIENT_ID
gwsmcp/google-client-secret -> GWS_MCP_GOOGLE_CLIENT_SECRET
```
`.envrc` is not committed. Copy `.envrc.example`, fill in the two account
emails, then run `direnv allow`. The safetybox line loads both client
credentials with one identity unlock, reading the passphrase from passage
via `secret-get`. Only the auth CLI reads these variables. OAuth tokens
land in `~/.local/share/gws-mcp/tokens.db`, file mode 0600, directory
0700, never in the repo.
## Install and authenticate
```
uv sync
uv run gws-mcp-auth personal
uv run gws-mcp-auth professional
```
Each run opens a browser. Sign in as the matching account and click through
Advanced at the unverified-app warning. The CLI verifies the signed-in
address against the alias and refuses to save a mismatch.
A scope added to `SCOPES` never reaches an already-stored grant. Re-run
`gws-mcp-auth <alias>` for each account, otherwise the new tool fails
with an insufficient-scope 403.
Then verify with the live read-only smoke script:
```
uv run python scripts/smoke.py
```
## Register with Claude Code
```
claude mcp add gws-mcp -s user -- uv run --directory /Users/samuelstidham/code/samuel-stidham/gws-mcp gws-mcp
```
## Testing
```
uv run pytest
```
The suite fakes every Google endpoint with respx and never touches the live
APIs. It covers account routing, the single-refresh 401 rule, rate-limit
backoff, MIME decoding, label resolution, batch Drive semantics, Sheets
append, protocol round-trips, stdout cleanliness, and the schema budget.
Measure the schema at any time:
```
uv run python scripts/measure_schema.py
uv run python scripts/measure_schema.py --probe uvx workspace-mcp --tools gmail drive docs sheets
```
The probe spawns the child with the MCP SDK's safe environment allowlist,
so no secret from your shell reaches it. Pass `--env KEY=VALUE` for
anything the probed server genuinely needs.
## Known failure modes
- A Google password change revokes that account's refresh token. Re-run
`gws-mcp-auth <alias>`. A running server picks up the new grant on its
next refresh, no restart needed.
- A refresh token unused for six months expires.
- The server refreshes once on 401 and then fails with the exact re-auth
command in the error message. A dead grant fails a whole batch
immediately instead of retrying per item.
- `sheets_write` uses `USER_ENTERED`, so a value starting with `=` becomes
a live formula. That is deliberate, and it means untrusted text should
not be written to sheets verbatim.
TDQS
Scored across 12 tools
All tools are clearly separated by service (gmail, drive, docs, sheets) and action, with no overlap. Gmail and Drive have their own search tools, and each service's operations are distinct and unambiguous.
All tools follow a consistent pattern of {service}_{action} in snake_case (e.g., drive_search, sheets_write). Even drive_folder_create uses a clear verb-noun structure. No mixed conventions.
With 12 tools, the server covers core operations for Gmail, Drive, Docs, and Sheets without being overly large. Each tool serves a clear purpose and the count fits the domain well.
The tool set covers essential CRUD operations: search, read, create, update for most services. Minor gaps exist (e.g., no create spreadsheet, no hard delete), but common workflows are supported, especially via modificational labeling and trashing.