Metadata-Version: 2.4
Name: google-workspace-suite-mcp
Version: 0.6.1
Summary: Aligned MCP servers for Gmail, Calendar, Sheets, Docs, and Drive with persistent, multi-account auth
Author: Nitai Aharoni
License-Expression: MIT
Project-URL: Homepage, https://github.com/nitaiaharoni1/google-workspace-mcp
Project-URL: Repository, https://github.com/nitaiaharoni1/google-workspace-mcp
Keywords: mcp,google,gmail,calendar,sheets,docs,drive,workspace,claude
Classifier: Development Status :: 4 - Beta
Classifier: Intended Audience :: Developers
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Topic :: Communications :: Email
Classifier: Topic :: Office/Business
Requires-Python: >=3.10
Description-Content-Type: text/markdown
License-File: LICENSE
Requires-Dist: mcp<3,>=2
Requires-Dist: anyio>=4
Requires-Dist: google-auth-core>=0.3.0
Requires-Dist: gmail-cli-oauth>=1.6.0
Requires-Dist: google-calendar-cli>=1.7.0
Requires-Dist: google-api-python-client>=2.0.0
Provides-Extra: dev
Requires-Dist: pytest>=7; extra == "dev"
Requires-Dist: pytest-anyio>=0.0.0; extra == "dev"
Requires-Dist: anyio>=4; extra == "dev"
Requires-Dist: ruff>=0.4; extra == "dev"
Dynamic: license-file

# google-workspace-mcp

Five aligned MCP servers for Google Workspace: **Gmail**, **Calendar**,
**Sheets**, **Docs**, and **Drive**. They share one credential store and one runtime core, so they behave
identically and support:

- **Persistent auth**: log in once; tokens are reused and access tokens refresh
  silently. Server restarts never re-prompt for consent.
- **Multiple accounts in parallel**: every tool takes an `account` argument
  (email or alias). Calls for different accounts use separate, cached clients and
  never interfere.
- **Many operations**: ~44 Gmail tools, ~27 Calendar tools, ~65 Sheets tools (incl. text editing),
  ~40 Docs tools, ~29 Drive tools, plus shared `list_accounts` / `whoami` /
  `auth_status` on every server.

## Architecture

```
google-auth-core      shared ~/.google token store + authorized-service cache
        |                     (also used by gmail-cli and google-calendar-cli)
        v
google_workspace_mcp/
  core/      build_server, account resolution + warm-client cache, error
             mapping, the read-only gate, the response envelope, common tools
  gmail/     wraps gmail_cli.api.GmailAPI            -> gmail-mcp
  calendar/  wraps google_calendar_cli.api.CalendarAPI -> gcal-mcp
  sheets/    SheetsAPI (Sheets API v4)               -> gsheets-mcp
  docs/      DocsAPI (Docs API v1)                   -> gdocs-mcp
  drive/     DriveAPI (Drive API v3)                 -> gdrive-mcp
  contacts/  PeopleAPI (People API v1)               -> search_contacts on gmail-mcp + gcal-mcp
```

Every tool: takes `account`, resolves it, gets a cached per-account client,
runs the Google call through shared error mapping, and returns
`{"ok": true, "account": "<resolved>", "data": ...}`.

### Docs (`gdocs-mcp`)

- **Markdown in/out** — `create_document_from_markdown`, `append_markdown`, and
  `replace_document_with_markdown` let an agent write plain markdown while Google
  converts it to native headings, lists, links, and tables; `read_document_as_markdown`
  round-trips it back.
- **Edits that can't land at stale positions**: `find_text` returns each match's
  start/end index, a short context snippet, and the doc's `revisionId` without
  dumping the whole document (table cells included, UTF-16 indices). `format_matches`
  styles or links every match in one call, resolving positions on the server and
  guarding the write with that read's revision, e.g. `pattern="LIG-\d+"`, `regex=true`,
  `link_url_template="https://linear.app/acme/issue/{match}"`; `clear_link=true`
  removes links instead. Every index-based write (`batch_update`, `format_text`,
  `insert_text`, `delete_range`, table and paragraph tools) accepts
  `required_revision_id`: pass the `revisionId` from `find_text`, `get_content_map`
  or `get_document`, and if the doc changed since, the write fails with "re-read"
  instead of hitting shifted text.

### Sheets (`gsheets-mcp`)

- **Human-readable layouts** — `write_table` writes data and formats it as a native
  table (or banded range) in one call; `optimize_layout` sizes every column to its
  content with a width cap, wraps only what must wrap, and auto-fits row heights.
  Header freezing is opt-in.
- **Rules and guards without raw batch_update**: `add_conditional_format` (value
  comparisons, text contains, custom formula, color scale), `list_conditional_formats`,
  `delete_conditional_format`; `protect_range` (a range or a whole sheet, editors or
  warning-only), `list_protected_ranges`, `unprotect_range`; and `add_named_range`,
  `list_named_ranges`, `update_named_range`, `delete_named_range`.

### Contact lookup (`gmail-mcp`, `gcal-mcp`)

- **Name to email** — `search_contacts(query="Dana")` lets an agent told "email Dana"
  or "invite Dana" find the address first. It searches saved contacts, people the
  account has emailed with ("other contacts"), and, on Workspace accounts, the
  company directory. Each result has `name`, `emails`, `phone`, and `source`
  (`contact`, `other`, `directory`); `skipped` lists any source that could not be
  searched and why (a personal Gmail account has no directory).
- **Setup** — enable the **People API** in your OAuth client's Cloud project. The
  token needs `contacts.readonly`, `contacts.other.readonly`, and
  `directory.readonly`; tokens created before these were added must be refreshed
  with `google-auth login <account>`. Until then the tool returns an error naming
  the missing scopes.

## Install

```bash
pip install google-workspace-suite-mcp
```

This pulls in the shared `google-auth-core` token store and the
`gmail-cli-oauth` / `google-calendar-cli` clients automatically, and installs
six console scripts onto your PATH: the five servers (`gmail-mcp`, `gcal-mcp`,
`gsheets-mcp`, `gdocs-mcp`, `gdrive-mcp`) plus the `google-auth` CLI used to log
in (see below).

If you prefer an isolated install with [pipx](https://pipx.pypa.io):

```bash
pipx install google-workspace-suite-mcp
```

For local development from a clone:

```bash
pip install -e .[dev]
```

## Authenticate (once)

Auth is **out-of-band**: the servers are non-interactive token consumers. You
log in once with the bundled `google-auth` CLI, which writes a unified token to
`~/.google/`; every server then reads and silently refreshes it.

**Quick start (recommended):**

```bash
google-auth setup you@example.com   # wizard: OAuth client → login → API check → .mcp.json
google-auth doctor                  # verify setup anytime
```

The wizard walks you through creating a Desktop OAuth client in Google Cloud
Console, logging in, confirming the five APIs are enabled, and prints
ready-to-paste `.mcp.json` snippets that point at locally installed scripts.
To run the servers through `uvx` instead, use the pinned form below.

You bring your **own** Google OAuth client. There is no shared client embedded
in the package: Gmail and Drive are Google "restricted" scopes, so a shared
published client would require Google's security assessment. Your own client
used just by you skips that review.

**Avoid "Testing" status.** Google expires refresh tokens after 7 days for an
External app left in Testing, so you'd have to re-run `google-auth login` every
week. Instead:

- **Google Workspace account:** set the consent screen's user type to
  **Internal**. No test users, no warning screen, tokens don't expire weekly.
  This needs the Cloud project to belong to your Workspace organization, and
  only accounts in that organization can sign in.
- **Personal Gmail, or accounts from several domains:** use **External**, then
  set publishing status to **In production** (no need to submit for
  verification). Each `google-auth login` shows an "unverified app" warning:
  click **Advanced**, then **Go to (your app name) (unsafe)**. Google caps
  unverified apps at 100 users in total, which doesn't matter for personal use.

Already in Testing? Switch to In production and run `google-auth login` again
for each account to get a token that doesn't expire weekly.

### Manual setup (appendix)

**1. Create an OAuth client** in the [Google Cloud Console](https://console.cloud.google.com):

- Create or select a project.
- Enable the APIs you need: Gmail, Google Calendar, Google Sheets, Google Docs,
  Google Drive, and People (for contact lookup).
- *APIs & Services → OAuth consent screen*: pick **Internal** (Workspace
  account) or **External** (personal Gmail). For External, click
  **Publish app** so the status is **In production** (see above for why).
- *APIs & Services → Credentials → Create credentials → OAuth client ID* →
  application type **Desktop app** → **Download JSON**.
- Save that file as `~/.google/credentials.json`.

**2. Log in:**

```bash
google-auth login you@example.com      # opens a browser, writes a unified token
google-auth alias work you@example.com # optional short alias
google-auth login you@personal.com     # add more accounts
google-auth list                       # show accounts, default, aliases
google-auth status                     # show token + scope health
google-auth doctor                     # diagnose setup issues
```

A single login grants Gmail + Calendar + Sheets + Docs + Drive scopes, shared by
both the CLIs and the MCP servers. If a token is ever missing or scope-short, a
tool returns an actionable error (`Run: google-auth login <account>`) rather
than blocking the protocol.

## Register with Claude

`claude mcp add` or a `.mcp.json` like:

```json
{
  "mcpServers": {
    "google-gmail": {
      "command": "uvx",
      "args": ["--from", "google-workspace-suite-mcp==0.6.1", "gmail-mcp"]
    },
    "google-calendar": {
      "command": "uvx",
      "args": ["--from", "google-workspace-suite-mcp==0.6.1", "gcal-mcp"]
    },
    "google-sheets": {
      "command": "uvx",
      "args": ["--from", "google-workspace-suite-mcp==0.6.1", "gsheets-mcp"]
    },
    "google-docs": {
      "command": "uvx",
      "args": ["--from", "google-workspace-suite-mcp==0.6.1", "gdocs-mcp"]
    },
    "google-drive": {
      "command": "uvx",
      "args": ["--from", "google-workspace-suite-mcp==0.6.1", "gdrive-mcp"]
    }
  }
}
```

The `==0.6.1` pin means launches come from uv's cache after the first one:
fast startup, works offline until the uv cache is cleaned, and a new release
only runs once you choose it. To upgrade, bump the version in all five entries
and restart Claude. If you'd rather get every release automatically, use this
form instead, which checks PyPI on every launch:

```json
"args": ["--refresh", "--from", "google-workspace-suite-mcp@latest", "gmail-mcp"]
```

GUI clients such as Claude Desktop and Cursor do not inherit your shell PATH,
so use the absolute `uvx` path (`which uvx`). Register at user scope to make
the servers available in every project.

Restart Claude after editing.

## Multiple accounts

Pass `account` to any tool (omit it to use the default):

```
list_messages(account="work", query="is:unread")
create_event(account="you@personal.com", summary="Dinner", start_time=..., end_time=...)
read_range(account="work", spreadsheet_id="...", range="Sheet1!A1:C10")
```

## Read-only mode

Set `GOOGLE_MCP_READONLY=1` in a server's environment to hide every mutating
tool (writes, deletes, clears) from that server. Destructive tools are also
clearly marked in their descriptions.

Every tool advertises MCP-native `ToolAnnotations` (`readOnlyHint`,
`destructiveHint`) in `list_tools`, derived from the same flags that drive the
read-only gate. Annotations are hints for well-behaved clients (auto-allow reads,
confirm destructive writes); `GOOGLE_MCP_READONLY` remains the enforcement
mechanism — clients must not rely on annotations alone for security.
Tools that send mail, share files, or overwrite content are marked destructive,
and those that reach people outside the account also set `openWorldHint`.

## Local file access

Tools that read or write local files (mail attachments, Drive upload/download/
export, Gmail attachment download) go through one shared path guard: paths are
resolved (including symlinks) and anything inside the `~/.google` token store
is refused. Set `GOOGLE_MCP_FILE_ROOT=/some/dir` to confine all local reads and
writes to that directory (downloads then need an explicit path inside it).
Downloads never replace an existing file unless the call passes
`overwrite=true`; a clash in the default temp dir gets a new name instead.

## Smaller tool set

The full servers expose about 190 tools, and every tool schema costs agent
context. Set `GOOGLE_MCP_TOOLSET=core` in a server's environment to register
only the everyday tools (10-16 per server: read/search/get, basic
create and update, Gmail send/reply/drafts, Calendar event CRUD, Sheets range
reads/writes and `write_table`, Docs markdown create/read/append, Drive
search/read/upload/move). `list_accounts`, `whoami` and `auth_status` are always
included. The default is `all`. It combines with `GOOGLE_MCP_READONLY`: with
both set you get the read-only part of the core set.

## Testing

```bash
pip install -e .[dev]
pytest                     # unit, protocol (in-memory), isolation, persistence
ruff check .               # minimal lint (import order, syntax, pyflakes)
GOOGLE_MCP_LIVE=1 pytest   # opt-in live smoke tests (needs a real test account)
```

## Notes

- Auth is out-of-band: servers are non-interactive token consumers. If a token
  is missing or scope-short, a tool returns an actionable error
  (`Run: google-auth login <account>`) instead of blocking the protocol.
- The five servers are packaged as one distribution with five console-script
  entry points (a single `pip install`), rather than five separate packages.
  Alignment comes from the shared `core`, not from separate packaging.
