Google Workspace MCP
by cikeyz
README.md
<p align="center">
<img src="https://upload.wikimedia.org/wikipedia/commons/5/5f/Google_Workspace_Logo.svg" width="480" alt="Google Workspace">
<br />
<strong>Full-context Google Workspace tools for MCP clients, with staged writes.</strong>
</p>
<p align="center">
<img alt="Python 3.14" src="https://img.shields.io/badge/python-3.14-3776AB.svg?logo=python&logoColor=white">
<img alt="Version" src="https://img.shields.io/badge/version-v2.5.0-blue.svg?logo=git&logoColor=white">
<a href="https://deepwiki.com/cikeyz/google-workspace-mcp"><img alt="Ask DeepWiki" src="https://deepwiki.com/badge.svg"></a>
</p>
<p align="center">
<img alt="Gmail" title="Gmail" src="https://cdn.simpleicons.org/gmail" width="28" height="28">
<img alt="Drive" title="Drive" src="https://cdn.simpleicons.org/googledrive" width="28" height="28">
<img alt="Docs" title="Docs" src="https://cdn.simpleicons.org/googledocs" width="28" height="28">
<img alt="Sheets" title="Sheets" src="https://cdn.simpleicons.org/googlesheets" width="28" height="28">
<img alt="Slides" title="Slides" src="https://cdn.simpleicons.org/googleslides" width="28" height="28">
<img alt="Forms" title="Forms" src="https://cdn.simpleicons.org/googleforms" width="28" height="28">
<img alt="Calendar" title="Calendar" src="https://cdn.simpleicons.org/googlecalendar" width="28" height="28">
<img alt="Chat" title="Chat" src="https://cdn.simpleicons.org/googlechat" width="28" height="28">
<img alt="Meet" title="Meet" src="https://cdn.simpleicons.org/googlemeet" width="28" height="28">
<img alt="Tasks" title="Tasks" src="https://cdn.simpleicons.org/googletasks" width="28" height="28">
</p>
<p align="center">
<a href="#why-this-server">Why this server</a> | <a href="#quick-start">Quick start</a> |
<a href="#tools">Tools</a> | <a href="#configuration">Configuration</a> |
<a href="#transport-and-security">Security</a> | <a href="#development">Development</a> |
<a href="#contributing">Contributing</a>
</p>
> [!NOTE]
> v2.1 changed all list tools from bare arrays to cursor envelopes
> (`items`, `next_page_token`, `has_more`). See
> [`docs/MIGRATION-v2.md`](docs/MIGRATION-v2.md). Pin the `v2.0` tag to stay
> on the old shapes.
## Why this server
Wrappers that trim API responses make agents decide on incomplete data. This
server goes the other way:
- Reads return the decision-useful resource, with a `full=True` hatch to the
complete payload where one exists
- Cursor envelopes (`items`, `next_page_token`, `has_more`) on all 11 list
tools, so collections of any size are walkable
- Every mutation is staged first: preview plus checks plus `operation_id`,
then a single-use commit that revalidates, or a cancel
- One env var (`GOOGLE_WORKSPACE_HOME`) points at all state, so any stdio
MCP host can run it
No tools were removed in v2.1. Ten list shapes changed; see the migration
guide.
## What it does
Eleven Google Workspace services behind one server:
- Gmail search, reads, thread summaries, labels, attachment downloads, staged sends
- Drive search, metadata, inline text reads, recency, downloads, uploads, creates,
folders, sharing audit, copy, move, trash
- Docs reads (all tabs), creates, appends, staged batch updates
- Sheets metadata, reads, updates, appends, creates, dimension inserts,
conditional-format reads
- Slides reads, creates, staged batch updates
- Forms definitions, responses, listings
- Calendar lists, events, calendars, search, RSVP, time suggestions, patches,
deletes, free/busy
- Contacts lists, server-side search, single reads, own profile
- Tasks lists, reads, creates, patches, deletes
- Chat spaces, search, messages, members, read-state, staged sends
- Meet spaces, reads, staged creates
- Universal cross-product search, 4 guided prompts (inbox triage, meeting brief,
thread summary, find-anything)
## Architecture
```mermaid
flowchart LR
Client[MCP client] --> Stdio[MCP stdio]
Stdio --> Tools[73 Workspace tools + 4 prompts]
Tools --> Stage[Staged-write gate]
Stage --> Google[Google APIs]
Tools --> State[(State home)]
State --> Token[OAuth token + client]
State --> Audit[Audit log]
```
## Quick start
### Requirements
- Python 3.11+ (developed and CI-tested on 3.14; `.python-version` pins 3.14)
- A Google Cloud project with the 11 Workspace APIs enabled and a Desktop
OAuth client (see `docs/SKILL.md` for the click path)
- An MCP client that can launch stdio
### Windows PowerShell
```powershell
git clone https://github.com/cikeyz/google-workspace-mcp.git
Set-Location google-workspace-mcp
python -m venv .venv
.\.venv\Scripts\Activate.ps1
pip install -r requirements.txt
$env:GOOGLE_WORKSPACE_HOME = "$PWD\state"
python setup/setup.py --client-secret C:\path\to\client_secret.json
python setup/setup.py --auth-url
```
Open the printed URL, approve all scopes, then exchange the redirect:
```powershell
python setup/setup.py --auth-code '<paste-the-redirect-url>'
python setup/tests/verify_server.py
```
Expect `RESULT: ALL CHECKS PASSED`.
### macOS or Linux
```bash
git clone https://github.com/cikeyz/google-workspace-mcp.git
cd google-workspace-mcp
python3.14 -m venv .venv
source .venv/bin/activate
pip install -r requirements.txt
export GOOGLE_WORKSPACE_HOME="$PWD/state"
python setup/setup.py --client-secret /path/to/client_secret.json
python setup/setup.py --auth-url
python setup/setup.py --auth-code '<paste-the-redirect-url>'
python setup/tests/verify_server.py
```
Testing-mode OAuth clients need weekly re-consent unless the app is verified.
## MCP client configuration
```json
{
"mcpServers": {
"Google Workspace": {
"command": "C:\\path\\to\\.venv\\Scripts\\python.exe",
"args": ["C:\\path\\to\\server.py"],
"env": {
"GOOGLE_WORKSPACE_HOME": "C:\\path\\to\\state"
}
}
}
}
```
## Tools
| Tool family | Purpose | Key inputs |
|---|---|---|
| `google_gmail_search`, `google_gmail_get`, `google_gmail_thread_get`, `google_gmail_search_threads` | Search, read, thread summaries | `query`, `max_results`, `page_token`, `full`, `max_body_chars` |
| `google_gmail_labels_list`, `google_gmail_modify_labels` (staged) | Labels | `message_id`, `add_label_ids`, `remove_label_ids` |
| `google_gmail_attachment_download` | Save attachments locally | `message_id`, `attachment_id` |
| `google_gmail_send` (staged) | Send mail | `to`, `subject`, `body`, `cc`, `bcc` |
| `google_drive_search`, `google_drive_get`, `google_drive_recent` | Find and describe files | `query`, `max_results`, `page_token`, `full`, `order` |
| `google_drive_read_content` | Inline text reads (Docs/Slides text, Sheets CSV) | `file_id`, `max_chars`, `start_char` |
| `google_drive_create_file` (staged), `google_drive_download`, `google_drive_upload` (staged) | Fetch and store files | `file_id`, `export_mime`, `local_path`, `name`, `text_content` |
| `google_drive_create_folder`, `google_drive_copy`, `google_drive_update` (staged) | Organize | `name`, `parent_folder_id` |
| `google_drive_share`, `google_drive_permissions` | Share and audit sharing | `file_id`, `email`, `role` |
| `google_drive_trash` (staged) | Recoverable delete | `file_id` |
| `google_docs_read`, `google_docs_create`, `google_docs_append` (staged) | Read and write docs | `document_id`, `title`, `text` |
| `google_docs_update` (staged) | Structural doc edits (insert, delete, replace, style) | `document_id`, `requests`, `destructive_acknowledged` |
| `google_sheets_metadata`, `google_sheets_read` | Inspect and read sheets | `spreadsheet_id`, `range_`, render options |
| `google_sheets_update`, `google_sheets_append`, `google_sheets_create` (staged) | Write cells | `spreadsheet_id`, `range_`, `values` |
| `google_sheets_insert_dimension` (staged) | Insert rows/columns | `spreadsheet_id`, `sheet_id`, `dimension`, `start_index`, `end_index` |
| `google_sheets_conditional_formats` | Read format rules | `spreadsheet_id` |
| `google_slides_get`, `google_slides_create` (staged) | Read and create decks | `presentation_id`, `title` |
| `google_slides_update` (staged) | Structural deck edits (slides, text, replace) | `presentation_id`, `requests`, `destructive_acknowledged` |
| `google_forms_list`, `google_forms_get`, `google_forms_responses` | Forms and answers | `form_id`, `page_token`, `filter_` |
| `google_calendar_list`, `google_calendar_get`, `google_calendar_search_events` | Events | `start`, `end`, `page_token`, `q`, `calendar_id` |
| `google_calendar_list_calendars` | Discover calendars | `max_results`, `page_token` |
| `google_calendar_suggest_time` | Free-slot suggestions | `time_min`, `time_max`, `duration_minutes` |
| `google_calendar_create`, `google_calendar_patch`, `google_calendar_delete`, `google_calendar_respond` (staged) | Manage events + RSVP | `summary`, `start`, `end`, `event_id`, `response` |
| `google_calendar_freebusy` | Availability windows | `time_min`, `time_max` |
| `google_people_contacts`, `google_people_search`, `google_people_search_contacts`, `google_people_get`, `google_people_profile` | Contacts + profile | `max_results`, `page_token`, `query`, `full` |
| `google_tasks_lists`, `google_tasks_list`, `google_tasks_get` | Read tasks | `tasklist_id`, `page_token`, filters |
| `google_tasks_create`, `google_tasks_update`, `google_tasks_delete` (staged) | Manage tasks | `title`, `status`, `due` |
| `google_chat_spaces`, `google_chat_search_conversations`, `google_chat_messages`, `google_chat_members` | Rooms and history | `space_name`, `page_token`, `filter_`, `query` |
| `google_chat_mark_read`, `google_chat_mark_unread` (staged) | Read-state | `space_name` |
| `google_chat_send` (staged) | Post messages | `space_name`, `text`, `thread_key`, `thread_name` |
| `google_universal_search` | Cross-product fan-out | `query`, `sources`, `max_per_source` |
| `triage_inbox`, `prep_meeting_brief`, `summarize_thread`, `find_anything` (prompts) | Guided workflows | per-prompt args |
| `google_meet_create_space` (staged), `google_meet_get_space` | Meetings | `config`, `space_name` |
| `google_auth_status` | Auth health | none |
| `google_write_commit`, `google_write_cancel`, `google_write_list_staged` | Apply staged writes | `operation_id` |
## Staged-write example
Writes never apply directly. Stage, review, then commit:
```json
{ "tool": "google_docs_create", "title": "GW-TEST-doc" }
```
returns `{ "staged": true, "operation_id": "…", "preview": {…} }`, then:
```json
{ "tool": "google_write_commit", "operation_id": "…" }
```
Commits revalidate first and refuse on drift. Cancels and failures are logged
alongside commits in `logs/google-write-audit.jsonl`.
## Pagination
```python
page = gmail_search("is:unread", 10)
msgs = page["items"]
while page["has_more"]:
page = gmail_search("is:unread", 10, page_token=page["next_page_token"])
msgs += page["items"]
```
Empty results are `{"items": [], "has_more": false}`, never an error.
## Configuration
| Variable | Default | Purpose |
|---|---:|---|
| `GOOGLE_WORKSPACE_HOME` | `<server dir>/state` | State home: token, client secret, downloads, audit log |
| `GOOGLE_TOKEN_PATH` | `<state>/google_token.json` | OAuth token override |
| `GOOGLE_CLIENT_SECRET_PATH` | `<state>/google_client_secret.json` | OAuth client override |
| `GOOGLE_DOWNLOAD_DIR` | `<state>/downloads/google` | Download target |
| `GOOGLE_REDIRECT_URI` | `http://localhost:1` | OAuth redirect override |
| `GW_FIXTURE_DOC_ID` | Empty | Test fixture: readable Doc |
| `GW_FIXTURE_FORM_ID` | Empty | Test fixture: readable Form |
| `GW_FIXTURE_RANGE` | `A1:B2` | Test fixture: sheet range |
## Transport and security
Stdio only. No listening ports, no network surface beyond Google's own APIs.
- `state/` holds a Gmail-capable OAuth grant. Keep the directory
user-private and never commit it (already in `.gitignore`).
- Staged writes expire after 24h, cap at 20 concurrent, and fail closed in
cron sessions.
- The audit log records write metadata with bodies redacted to counts and
hashes. Treat it as sensitive.
- Testing-mode OAuth clients need weekly re-consent unless verified.
## Development
```powershell
$env:GOOGLE_WORKSPACE_HOME = "$PWD\state"
.\.venv\Scripts\python.exe setup\tests\verify_server.py
.\.venv\Scripts\python.exe setup\tests\test_server.py
```
`verify_server.py` is the quick battery (no writes). `test_server.py` runs
full stage-commit-verify-cleanup cycles across services and must finish with
`RESULT: ALL CHECKS PASSED` and zero `GW-TEST-` residue. Set
`GW_FIXTURE_DOC_ID` and `GW_FIXTURE_FORM_ID` for full coverage; fixture checks
skip otherwise.
## Upstream and license
- Repository:
[`cikeyz/google-workspace-mcp`](https://github.com/cikeyz/google-workspace-mcp)
- Original project: written from scratch for personal agent use, no upstream.
Released under the [MIT License](LICENSE).
## Contributing
1. Fork the project
2. Create your feature branch (`git checkout -b feature/my-change`)
3. Commit your changes (`git commit -m 'Add my change'`)
4. Push to the branch (`git push origin feature/my-change`)
5. Open a Pull Request
Reads are free to add. Anything mutating must fit the staged-write protocol
(stage, preview, single-use commit) and land in both test batteries.
## Star History
<picture>
<source
media="(prefers-color-scheme: dark)"
srcset="
https://api.star-history.com/svg?repos=cikeyz/google-workspace-mcp&type=Date&theme=dark
"
/>
<source
media="(prefers-color-scheme: light)"
srcset="
https://api.star-history.com/svg?repos=cikeyz/google-workspace-mcp&type=Date
"
/>
<img
alt="Star History Chart"
src="https://api.star-history.com/svg?repos=cikeyz/google-workspace-mcp&type=Date"
/>
</picture>
---
<p align="center">
Made for agents that read everything before they act.
</p>
This server cannot be deployed
Maintenance
ActivityMaintained
ResponsivenessNo issues