io.github.orieg/gws-connector
Multi-account Google Workspace MCP — Gmail, Calendar, Drive, Sheets, Docs with smart routing.
Open source Open in the app JSON README (API)
About
Multi-account Google Workspace MCP — Gmail, Calendar, Drive, Sheets, Docs with smart routing.
Details
- Kind
- MCP servers
- Topic
- Productivity
- Publisher
- orieg
- Origin
- official
- Category
- ferramentas
- Transport
- local
- Version
- 0.3.5
- Stars
- 1
- Open pull requests
- 3
- Last push
- 2026-09-04T19:05:06Z
- Repository state
- ativo
- Language
- Go
- License
- MIT
- Added
- 2026-08-29 04:00:58
- Updated
- 2026-08-29 04:00:58
- Origin id
io.github.orieg/gws-connector
README
# GWS Connector
[](https://github.com/orieg/gws-connector/actions/workflows/ci.yml)
[](https://github.com/orieg/gws-connector/releases/latest)
[](https://go.dev/)
[](LICENSE)
Multi-account Google Workspace MCP server — connect multiple Gmail, Google Calendar, and Google Drive accounts with smart routing.
Works with **Claude Code**, **Gemini CLI**, **GitHub Copilot**, **Cursor**, **OpenAI Codex**, and any MCP-compatible client.
## Why
Most AI coding assistants support a single Google account. If you use multiple Google accounts (personal + work, multiple clients, different orgs), you need to switch between them manually. This MCP server lets you connect them all at once and route requests by label, email, or domain.
## Features
- **Multi-account** — connect unlimited Gmail and Google Workspace accounts
- **Smart routing** — target accounts by label (`work`), email, or domain
- **Per-account OAuth** — different orgs can use their own GCP credentials
- **Secure storage** — client secrets and tokens stored in OS keychain (file fallback on Linux without GNOME Keyring)
- **47 tools** — Mail (11), Calendar (7), Drive (3), Sheets (6), Docs (4), Contacts (2), Tasks (5), Slides (3), account management (6)
- **Account management** — add, remove, set default, list accounts
- **Cross-platform** — standard MCP server works with any compatible client
## How it compares
There are several good Google Workspace MCP servers. GWS Connector is the one to
pick when **multiple accounts** and **operational simplicity** matter:
| | Most GWS MCP servers | **GWS Connector** |
|---|---|---|
| **Accounts** | One account per server instance | Unlimited accounts in one instance, routed by label / email / domain |
| **Multiple orgs** | Shared OAuth app | Per-account OAuth — each org uses its own GCP credentials |
| **Credential storage** | `.env` / plaintext token files | OS keychain (Keychain / GNOME Keyring / Credential Manager) |
| **Runtime** | Python/Node + dependencies | Single static Go binary, no runtime to install |
| **Clients** | Usually one | Claude Code, Gemini CLI, Copilot, Cursor, Codex, any MCP client |
| **Install** | Manual config | Claude Code plugin, Gemini extension, one-click `.mcpb`, MCP Registry |
If you only ever use a single Google account and want the widest possible tool
surface (Forms, Chat, Vault, …), a single-account server like
[taylorwilsdon/google_workspace_mcp](https://github.com/taylorwilsdon/google_workspace_mcp)
may fit better. GWS Connector focuses on doing multi-account Gmail / Calendar /
Drive / Sheets / Docs / Contacts / Tasks / Slides cleanly and securely.
## Upgrading (Google Tasks)
The Google Tasks tools (`gws.tasks.*`) are added behind one new OAuth scope
(`tasks`). **Existing users must re-authorize each connected account** so new
tokens are minted with this scope:
```
/gws:reauth
```
Before approving the browser consent screen, review what the new scope grants —
full read and write access to the account's Google Tasks lists and tasks. See
the [scope rationale table](#google-cloud-setup) below for details.
You must also add the `tasks` scope and enable the **Tasks API** in your GCP
project's OAuth consent screen configuration before re-auth, or the consent
screen will reject the request. Until an account is re-authorized, the
`gws.tasks.*` tools return an insufficient-scope error naming the reauth tool
to run.
## Upgrading — Contacts / People API
The Contacts tools (`gws.contacts.search`, `gws.contacts.directory_search`)
add two new read-only OAuth scopes (`contacts.readonly`, `directory.readonly`).
**Existing users must re-authorize each connected account** so new tokens are
minted with these scopes:
```
/gws:reauth
```
Before approving the browser consent screen, review what the new scopes grant —
read-only access to your Google Contacts and (for Workspace accounts) the
organization directory. See the [scope rationale table](#google-cloud-setup)
below for details.
You must also enable the **People API** and add the two new scopes in your GCP
project's OAuth consent screen configuration before re-auth, or the consent
screen will reject the request. `gws.contacts.directory_search` requires a
Google Workspace account — personal Gmail accounts have no organization
directory and receive a clear explanatory message instead of results.
## Upgrading (Google Slides tools)
The Slides tools (`gws.slides.*`) add one new OAuth scope,
`https://www.googleapis.com/auth/presentations`. **Existing users must
re-authorize each connected account** so new tokens are minted with the
Slides scope:
```
/gws:reauth
```
You must also enable the **Slides API** and add the `presentations` scope in
your GCP project's OAuth consent screen configuration before re-auth, or the
consent screen will reject the request (see [Google Cloud
Setup](#google-cloud-setup)). Until you re-authorize, `gws.slides.*` calls
return a scope error telling the agent to run `gws.accounts.reauth`; all other
tools keep working.
## Upgrading from v0.2.x
v0.3.0 adds native Google Sheets and Google Docs tools behind two new OAuth
scopes (`spreadsheets`, `documents`). **Existing users must re-authorize
each connected account** so new tokens are minted with these scopes:
```
/gws:reauth
```
Before approving the browser consent screen, review what the new scopes
grant — full read and write access to every spreadsheet and document in that
account's Google Drive, including files shared with the account. See the
[scope rationale table](#google-cloud-setup) below for details.
You must also add the two new scopes (and enable the Sheets and Docs APIs)
in your GCP project's OAuth consent screen configuration before re-auth,
or the consent screen will reject the request.
## Quick Start (Claude Code)
**1. Install the plugin** — run these two commands inside Claude Code:
```
/plugin marketplace add orieg/gws-connector
/plugin install gws@gws-connector
```
**2. Set up Google Cloud credentials** — the interactive wizard walks you through everything:
```
/gws:configure
```
This creates a GCP project, enables APIs, and connects your first account (~5 minutes). See [Google Cloud Setup](#google-cloud-setup) if you prefer manual steps.
**3. Connect additional accounts:**
```
/gws:add-account
```
Each account can use different OAuth credentials from different GCP projects.
### Gemini CLI
```bash
gemini extensions install https://github.com/orieg/gws-connector
```
The binary is downloaded automatically on first use. Then connect accounts inside Gemini:
```
gws.accounts.add(label: "personal", clientId: "your-client-id", clientSecret: "your-secret")
```
### Other clients
<details>
<summary><strong>GitHub Copilot / Cursor / Codex / Any MCP client</strong></summary>
Download a [prebuilt binary](https://github.com/orieg/gws-connector/releases/latest) or build from source:
```bash
git clone https://github.com/orieg/gws-connector && cd gws-connector && make build
```
Then configure your client:
| Client | Config |
|--------|--------|
| **GitHub Copilot** | Auto-detects from `.vscode/mcp.json`, or add `"command": "/path/to/gws-mcp"` to VS Code MCP settings |
| **Cursor** | Auto-detects from `.cursor/mcp.json`, or add via Settings → MCP Servers |
| **Codex CLI** | Auto-detects from `codex.json` |
| **Claude Code (MCP only)** | `claude mcp add --transport stdio gws-connector --scope user -- /path/to/gws-mcp --use-dot-names` |
| **Any MCP client** | `gws-mcp [--use-dot-names]` over stdio |
Connect accounts via MCP tool call:
```
gws.accounts.add(label: "personal", clientId: "your-client-id", clientSecret: "your-secret")
```
Environment variables (all optional): `GWS_GOOGLE_CLIENT_ID`, `GWS_GOOGLE_CLIENT_SECRET`, `GWS_STATE_DIR`
The `--use-dot-names` flag uses `gws.mail.search` naming; without it, tools use `gws_mail_search`.
</details>
<details>
<summary><strong>Local development / testing</strong></summary>
```bash
git clone https://github.com/orieg/gws-connector
cd gws-connector
make build
claude --plugin-dir ./
```
Use `/reload-plugins` inside the session after making changes. Run `claude --debug --plugin-dir ./` to troubleshoot plugin loading.
</details>
<details>
<summary><strong>Claude Desktop / MCPB bundle</strong></summary>
Each [release](https://github.com/orieg/gws-connector/releases/latest) attaches a
one-click **`gws-mcp.mcpb`** bundle. Download it and open it with Claude Desktop
(Settings → Extensions → install from file), or drag it in. The bundle contains
the binaries for macOS and Linux and picks the right one for your machine
automatically. You still complete the [Google Cloud setup](#google-cloud-setup)
and connect accounts on first use.
The server is also published to the
[official MCP Registry](https://registry.modelcontextprotocol.io) as
`io.github.orieg/gws-connector`, so MCP clients that browse the registry can find
and install it directly.
</details>
<details>
<summary><strong>Docker</strong></summary>
A multi-arch image is published to GHCR on each release:
```bash
docker run -i --rm ghcr.io/orieg/gws-connector:latest
```
The server speaks MCP over stdio. Interactive OAuth (`accounts.add` / `reauth`)
opens a browser and stores secrets in the OS keychain, so it needs host access —
day-to-day use is best via the native binary, the Claude Code plugin, or the
Gemini extension. The image is well suited to headless stdio integrations and to
registry/introspection checks. Persist the account registry across runs by
mounting a volume and pointing `GWS_STATE_DIR` at it:
```bash
docker run -i --rm -v gws-state:/state -e GWS_STATE_DIR=/state \
ghcr.io/orieg/gws-connector:latest
```
</details>
## Usage
All `gws.*` tools accept an optional `account` parameter:
```
# Uses default account
gws.mail.search(q: "is:unread")
# Target by label
gws.cal.list_events(account: "work")
# Target by email
gws.drive.search(account: "alice@company.com", q: "quarterly report")
```
### Available tools
| Tool | Description |
|------|-------------|
| `gws.accounts.list` | List all connected accounts |
| `gws.accounts.add` | Connect a new account (waits up to ~60s; returns `pendingId` if slower) |
| `gws.accounts.reauth` | Re-authorize an account (waits up to ~60s; returns `pendingId` if slower) |
| `gws.accounts.complete` | Finalize a pending OAuth flow (only needed if add/reauth returned `pendingId`) |
| `gws.accounts.remove` | Disconnect an account |
| `gws.accounts.set_default` | Change the default account |
| `gws.mail.search` | Search messages (Gmail query syntax) |
| `gws.mail.read_message` | Read a specific message |
| `gws.mail.read_thread` | Read an entire thread |
| `gws.mail.create_draft` | Create an email draft |
| `gws.mail.send_draft` | Send an existing draft |
| `gws.mail.forward` | Build a forward draft of a message (does not send) |
| `gws.mail.get_attachment` | Fetch a message attachment's bytes (base64) |
| `gws.mail.list_labels` | List Gmail labels |
| `gws.mail.create_label` | Create a new label |
| `gws.mail.modify_message` | Add/remove labels on a message |
| `gws.mail.get_profile` | Get account profile info |
| `gws.cal.list_events` | List calendar events |
| `gws.cal.get_event` | Get event details |
| `gws.cal.create_event` | Create a calendar event |
| `gws.cal.update_event` | Update/reschedule an event (patch semantics) |
| `gws.cal.delete_event` | Delete/cancel an event |
| `gws.cal.free_busy` | Query free/busy across calendars |
| `gws.cal.list_calendars` | List available calendars |
| `gws.drive.search` | Search files in Drive |
| `gws.drive.read_file` | Read file content/metadata |
| `gws.drive.list_folder` | List folder contents |
| `gws.sheets.read_range` | Read a single A1 range from a spreadsheet |
| `gws.sheets.write_range` | Write cell values to a range |
| `gws.sheets.append` | Append rows after a table (additive, never overwrites) |
| `gws.sheets.clear` | Clear values in a range (formatting left intact) |
| `gws.sheets.create` | Create a new spreadsheet |
| `gws.sheets.list_tabs` | List tabs (sheets) in a spreadsheet |
| `gws.docs.read` | Read a document as plain text |
| `gws.docs.insert_text` | Insert literal text at a location |
| `gws.docs.replace_text` | Replace all occurrences of a literal substring |
| `gws.docs.create` | Create a new document |
| `gws.contacts.search` | Search your own contacts by name/email/phone (returns name, emails, phones) |
| `gws.contacts.directory_search` | Search the Workspace org directory (returns name, emails); Workspace accounts only |
| `gws.tasks.list_tasklists` | List the account's task lists |
| `gws.tasks.list` | List tasks in a list (add `showCompleted` for done tasks) |
| `gws.tasks.create` | Create a task (`due` is RFC3339; only the date is stored) |
| `gws.tasks.complete` | Mark a task completed (reversible) |
| `gws.tasks.delete` | Permanently delete a task |
| `gws.slides.get` | Read a presentation (slide count + per-slide text) |
| `gws.slides.create` | Create a new presentation |
| `gws.slides.batch_update` | Apply raw Slides API requests to a presentation |
### Skills
Interactive workflows available in both Claude Code and Gemini CLI:
| Skill | Description | Claude Code | Gemini CLI |
|-------|-------------|-------------|------------|
| configure | Interactive setup wizard | `/gws:configure` | "run the GWS configure skill" |
| add-account | Connect a new account | `/gws:add-account` | "add a new GWS account" |
| remove-account | Disconnect an account | `/gws:remove-account` | "remove a GWS account" |
| list-accounts | Show connected accounts | `/gws:list-accounts` | "list my GWS accounts" |
| set-default | Change default account | `/gws:set-default` | "set my default GWS account" |
| reauth | Refresh tokens/scopes | `/gws:reauth` | "reauth my GWS accounts" |
## Recipes
Once accounts are connected, just ask your assistant in plain language — it picks
the tools and the account. Examples:
- **Morning triage across accounts** — "Summarize my unread email from the last
24 hours across all accounts, grouped by account, and flag anything that needs
a reply today."
- **Draft a reply in a thread** — "Find the thread with Acme about the Q3 invoice
on my **work** account and draft a reply confirming the new date. Don't send it."
- **Turn an email into a calendar event** — "Read the latest message from the
events team and create a calendar event on my **personal** calendar with the
date and location from it."
- **Cross-account digest** — "What meetings do I have tomorrow across my work and
personal calendars? List them in one timeline."
- **Find and summarize a doc** — "Search my **client-acme** Drive for the latest
'statement of work' and give me the key deliverables and dates."
- **Log to a spreadsheet** — "Append a row to the 'Expenses' sheet in my personal
Drive: today's date, 'AWS', 42.50."
- **Keep inbox tidy** — "Label all unread messages from newsletters@ as
'Newsletters' and mark them read on my **personal** account."
Tips:
- Target an account explicitly with its label ("on my **work** account"), by
email, or by domain — otherwise the default account is used.
- Write operations (drafts, events, sheet/doc edits) are previewed for your
confirmation before anything is sent or changed.
## Google Cloud Setup
One-time setup (~5 minutes):
1. **Go to [Google Cloud Console](https://console.cloud.google.com/)** and create a new project (e.g., "GWS Connector")
2. **Enable APIs** — click each link and hit "Enable":
- [Gmail API](https://console.cloud.google.com/apis/library/gmail.googleapis.com)
- [Calendar API](https://console.cloud.google.com/apis/library/calendar-json.googleapis.com)
- [Drive API](https://console.cloud.google.com/apis/library/drive.googleapis.com)
- [Sheets API](https://console.cloud.google.com/apis/library/sheets.googleapis.com)
- [Docs API](https://console.cloud.google.com/apis/library/docs.googleapis.com)
- [People API](https://console.cloud.google.com/apis/library/people.googleapis.com)
- [Tasks API](https://console.cloud.google.com/apis/library/tasks.googleapis.com)
- [Slides API](https://console.cloud.google.com/apis/library/slides.googleapis.com)
3. **Configure the [OAuth consent screen](https://console.cloud.google.com/auth/consent)**:
- Choose "External" (or "Internal" for Google Workspace orgs)
- Fill in the app name (e.g., "Claude GWS") and your email for support contact
- Click "Save"
4. **Add scopes** — go to [Data Access](https://console.cloud.google.com/auth/scopes):
- Click "Add or Remove Scopes"
- Add these 11 scopes (paste into the "Manually add scopes" box):
- `https://www.googleapis.com/auth/gmail.modify`
- `https://www.googleapis.com/auth/calendar`
- `https://www.googleapis.com/auth/drive`
- `https://www.googleapis.com/auth/spreadsheets`
- `https://www.googleapis.com/auth/documents`
- `https://www.googleapis.com/auth/contacts.readonly`
- `https://www.googleapis.com/auth/directory.readonly`
- `https://www.googleapis.com/auth/tasks`
- `https://www.googleapis.com/auth/presentations`
- `https://www.googleapis.com/auth/userinfo.email`
- `https://www.googleapis.com/auth/userinfo.profile`
- Click "Update", then "Save"
**Why each scope is requested:**
| Scope | Purpose | Tools |
|-------|---------|-------|
| `gmail.modify` | Read, draft, modify messages and labels | `gws.mail.*` |
| `calendar` | Read and create/update events | `gws.cal.*` |
| `drive` | Search and read files and metadata across Drive | `gws.drive.*` |
| `spreadsheets` | Read and write Google Sheets cell data and metadata | `gws.sheets.*` |
| `documents` | Read and write Google Docs content | `gws.docs.*` |
| `contacts.readonly` | Read-only search of your own Google Contacts | `gws.contacts.search` |
| `directory.readonly` | Read-only search of the Workspace org directory (Workspace accounts only) | `gws.contacts.directory_search` |
| `tasks` | Read and write Google Tasks lists and tasks | `gws.tasks.*` |
| `presentations` | Read and write Google Slides content | `gws.slides.*` |
| `userinfo.email` | Identify the authorizing account (email match on reauth) | account management |
| `userinfo.profile` | Store a display name alongside the email | account management |
5. **Add test users** — go to [Audience](https://console.cloud.google.com/auth/audience):
- Add each Google email address you plan to connect
- ⚠️ **This is required** — without this you'll get "Access blocked: has not completed the Google verification process" (error 403) during OAuth
6. **Create OAuth credentials** — go to [Clients](https://console.cloud.google.com/auth/clients):
- Click "+ Create Client" → "OAuth client ID"
- Application type: **Desktop app**
- Click "Create"
- **Download the JSON file** (click the download icon) — this contains your Client ID and Client Secret
### Multiple organizations
If you connect accounts from different Google Workspace orgs, each org needs its own GCP project. Create OAuth credentials in each project and provide them when connecting:
```
gws.accounts.add(label: "work", clientId: "work-client-id", clientSecret: "work-secret")
gws.accounts.add(label: "personal", clientId: "personal-client-id", clientSecret: "personal-secret")
```
Client secrets are stored in the OS keychain. Client IDs are stored in the account registry.
## Architecture
```
gws-connector/
├── cmd/gws-mcp/ # MCP server entrypoint
├── internal/
│ ├── accounts/ # Account registry & router
│ ├── auth/ # OAuth flow, token store, client factory
│ ├── server/ # MCP tool registration & dispatch
│ └── services/ # Gmail, Calendar, Drive API wrappers
│
├── .claude-plugin/ # Claude Code plugin manifest + marketplace
├── .mcp.json # Claude Code MCP config
├── gemini-extension.json # Gemini CLI extension manifest
├── CONTEXT.md # Shared behavioral context (both agents)
├── skills/ # Slash commands (Claude Code + Gemini CLI)
├── hooks/ # Claude Code session hooks
├── agents/ # Claude Code workspace agent
│
├── .vscode/mcp.json # GitHub Copilot MCP config
├── .cursor/mcp.json # Cursor MCP config
└── codex.json # OpenAI Codex CLI config
```
- **Token storage**: OS keychain (macOS Keychain, GNOME Keyring, Windows Credential Manager) with automatic file fallback
- **Client secrets**: OS keychain per account (not stored in config files)
- **Account registry**: JSON file at `~/.claude/channels/gws/accounts.json` (contains client IDs and metadata, no secrets)
- **Credential resolution**: per-account credentials (keychain) → global env var fallback
- **Protocol**: MCP (Model Context Protocol) over stdio — compatible with any MCP client
## Development
```bash
make build # Build binary
make test # Run tests with race detector
make test-verbose # Run tests with verbose output
make lint # Run go vet
make release # Cross-compile for all platforms
make clean # Remove build artifacts
```
## License
MIT — see [LICENSE](LICENSE).