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