Give Claude Code access to Gmail, Google Drive, Calendar, Sheets, Docs, and Slack — with full support for multiple Google accounts (e.g., personal + work).
Claude Desktop and Claude Cowork have built-in Google Workspace integrations — flip a toggle and you're connected. Claude Code has nothing. As of March 2026, there's no official integration, no plugin that handles multi-account, and no documentation on how to set it up.
This repo is the result of ~6 hours of research and debugging to get it working. The final solution is simple, but getting there required discovering several undocumented behaviors in Claude Code's MCP system, Google's OAuth2 implementation, and the gws CLI.
It's not one problem — it's a stack of them, each undocumented:
-
Claude Code silently ignores MCP config in the wrong file. If you put
mcpServersinsettings.local.json(where you'd expect it), servers don't start. No error. No log. You can spend hours restarting and re-checking JSON syntax. The config must go in.mcp.jsonat the project root. (Claude Code Issue #24477) -
One OAuth client for two Google accounts breaks both. Google invalidates account A's refresh token when you authenticate account B with the same OAuth client. You need separate OAuth Desktop App clients — one per account — in the same GCP project.
-
The credential file env var doesn't work.
GOOGLE_WORKSPACE_CLI_CREDENTIALS_FILEis supposed to let you point the Google Workspace CLI at different credential files, but it doesn't reliably route to the right account. You have to mint fresh access tokens and pass them viaGOOGLE_WORKSPACE_CLI_TOKEN(the highest-priority auth method). -
Go binaries use single-dash flags. Both the
gwsCLI andslack-mcp-serverare Go binaries.--transport stdiofails silently. It's-t stdio— or just omit it (stdio is the default). -
MCP servers only start at session launch. Config changes don't take effect until you restart Claude Code entirely.
Each of these individually cost 30-120 minutes to diagnose. Together they make the setup feel broken when it's actually a solvable configuration problem.
| Approach | Why It Failed |
|---|---|
Legacy google-workspace Claude Code plugin |
Single-account only, port 8000 conflict blocks second instance |
| Single OAuth client for both accounts | Google invalidates first account's refresh token on second auth |
CREDENTIALS_FILE env var per MCP server |
Doesn't reliably route to the right account |
mcpServers in settings.local.json |
Silently ignored — Claude Code never reads MCP config from there |
mcpServers in .claude/settings.json |
Also silently ignored |
| Service account auth | Can't access personal Gmail/Drive without domain-wide delegation |
| Requesting all 85+ OAuth scopes | Google blocks unverified apps requesting too many scopes |
For the full investigation log, see docs/research-notes.md.
The working setup uses:
- Google Workspace CLI (
gwsv0.7+) — Google's official CLI with built-in MCP server mode - Separate OAuth Desktop clients — one per Google account, in the same GCP project
- A token wrapper script — mints fresh access tokens from each account's credential file, passes them via the highest-priority env var
.mcp.jsonat project root — the only config location Claude Code actually reads for MCP servers- slack-mcp-server — Go-based Slack MCP, read-only by default
| MCP Server | Capabilities |
|---|---|
gws-personal |
Gmail, Drive, Calendar, Sheets, Docs for your personal Google account |
gws-work |
Same, for your work Google account (optional) |
slack |
Read channels, search messages, browse threads, list users |
Once running, Claude Code can search your email, read Drive files, check your calendar, read/write spreadsheets, and search Slack — all through natural language.
- Claude Code installed and working
- Node.js 18+ (for
npx) - Python 3 (for token minting — uses only stdlib)
- gcloud CLI (for enabling APIs and IAM)
- A Google account with Gmail/Drive access
- (Optional) A Slack workspace where you can create an app
-
Clone this repo and open Claude Code in it:
git clone https://github.com/YOUR_USERNAME/claude-code-google-workspace.git cd claude-code-google-workspace claude -
Tell Claude Code: "Set up Google Workspace MCP for me"
The
CLAUDE.mdin this repo contains machine-readable setup instructions. Claude Code will walk you through each step, prompting you only for browser-based OAuth steps that can't be automated. -
Restart Claude Code to load the new MCP servers.
If you prefer step-by-step instructions: docs/manual-setup.md.
To understand every decision and failed approach: docs/research-notes.md.
Claude Code session start
│
├─── reads .mcp.json (project root)
│
├─── starts gws-personal server:
│ │
│ ├── gws-token-wrapper.sh personal.json
│ │ │
│ │ ├── reads personal.json (client_id_A + refresh_token)
│ │ ├── POST https://oauth2.googleapis.com/token → access_token
│ │ └── exec: GOOGLE_WORKSPACE_CLI_TOKEN=<token> gws mcp -s gmail,drive,...
│ │
│ └── gws mcp ←→ Google APIs (Gmail, Drive, Calendar, Sheets, Docs)
│
├─── starts gws-work server:
│ │
│ └── (same flow, different credential file with client_id_B)
│
└─── starts slack server:
│
└── npx slack-mcp-server (SLACK_MCP_XOXP_TOKEN env var)
The gws CLI stores one set of credentials at a time internally. For multiple accounts, we can't rely on its credential file routing (CREDENTIALS_FILE env var is unreliable). Instead, we:
- Read the exported credential file (has
client_id,client_secret,refresh_token) - Call Google's token endpoint directly to mint a fresh access token
- Pass it via
GOOGLE_WORKSPACE_CLI_TOKEN— the highest-priority auth method in thegwsauth chain
This is 15 lines of shell + Python stdlib. No dependencies.
OAuth2 Desktop App clients have a behavior where issuing a new refresh token for account B invalidates the existing refresh token for account A — if they share the same client_id. Creating separate OAuth Desktop clients in the same GCP project gives each account its own independent token lifecycle.
After setup, you need two things in any project where you want these MCP servers:
.mcp.jsonat the project root — copy from this repo's template and fill in your paths~/.config/gws/— credential files + wrapper script (created once during setup, shared across all projects)
Or add the mcpServers config to ~/.claude.json for global access in every project.
Always gitignore .mcp.json — it contains OAuth tokens.
These are ordered by how much time they'll cost you if you hit them unaware:
| # | Gotcha | Time Cost | Symptom | Fix |
|---|---|---|---|---|
| 1 | mcpServers in settings.local.json is silently ignored |
2+ hours | Servers never start, zero errors | Use .mcp.json (project root) or ~/.claude.json (global). Issue #24477 |
| 2 | Same OAuth client for two accounts | 1-2 hours | First account stops working after second account authenticates | Create separate OAuth Desktop clients per account |
| 3 | CREDENTIALS_FILE env var unreliable |
1 hour | Wrong account's data returned | Use GOOGLE_WORKSPACE_CLI_TOKEN via wrapper script |
| 4 | Work account shows "Access blocked" | 30 min | OAuth consent screen blocks non-test users | Add all accounts as test users on OAuth consent screen + grant IAM serviceUsageConsumer role |
| 5 | MCP servers only start at session launch | 15 min | Config changes don't take effect | Restart Claude Code after any .mcp.json change |
| 6 | Go binaries use single-dash flags | 15 min | --transport stdio fails silently |
Use -t stdio or omit (stdio is default) |
| 7 | Access tokens expire after ~1 hour | 5 min | API calls fail on long sessions | Restart Claude Code for fresh tokens |
| 8 | gws auth login opens foreground browser |
5 min | Wrong Chrome profile = wrong account authenticated | Bring correct Chrome profile to front before running |
| 9 | Too many OAuth scopes requested | 10 min | Google blocks unverified app | Use -s drive,gmail,calendar,sheets,docs (not all scopes) |
- Written March 2026 — tested with
gwsCLI v0.7.0 and current Claude Code - Google and Anthropic will likely make this easier natively — this repo fills the gap until then
- GCP project + OAuth consent screen setup requires manual browser steps (can't be fully automated)
- Access tokens minted at session start expire after ~1 hour — restart for long sessions
- Tested on macOS — Linux should work, Windows untested
- The
gwsCLI is pre-1.0 — flags and auth behavior may change
- Google Workspace CLI — the core tool powering Google Workspace access
- slack-mcp-server — Slack MCP server
- Claude Code MCP docs — official MCP server documentation
- MCP Specification — the Model Context Protocol spec
- Google OAuth2 documentation — OAuth flow and token lifecycle
- Claude Code Issue #24477 —
settings.local.jsonsilent ignore bug
Issues and PRs welcome. If you hit a new gotcha, please open an issue — every undocumented behavior we capture saves someone hours.
MIT