gws-mcp
Allows interaction with a Gmail account, providing tools for searching threads, retrieving messages and threads, downloading attachments, listing labels and drafts, creating and updating drafts, modifying labels, sending drafts and messages, deleting drafts, and trashing messages.
Click on "Deploy Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@gws-mcpsearch my inbox for unread emails from this week and summarize them"
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
gws-mcp
A small local MCP server that gives a coding agent (I use it with Claude Code) controlled access to one Google account: Gmail, Calendar and Drive. It's two Python files, 21 tools, and it runs over stdio from the user's own OAuth token.
I built it because the hosted Google connectors loaded in one Claude Code surface and never in another. That surface's session token lacked the MCP scope, so the connectors silently weren't there, even while the settings page said "Connected". Running my own server removes that dependency, and it let me enforce rules in code that the hosted connector could not.
The rules it enforces
A draft can't send. gmail_create_draft calls drafts.create and nothing else. A
hosted connector's "create draft" tool once sent four emails I had not reviewed. Sending
is two separate tools, both marked destructive, and every send returns a reminder that
SENT is not DELIVERED: check for a bounce before reporting success.
Every tool declares what it can do. Each tool carries MCP annotations: 11 are read-only, 5 write, and 5 are destructive (send, trash, delete). The client can gate on them, and the agent's instructions say any send or calendar write needs the user's yes in the same turn.
One account, asserted. The first call checks the token's address against
$GWS_EXPECTED_ACCOUNT and refuses if they differ. It never reads a different mailbox
quietly.
Least-privilege scopes. gmail.modify (no permanent delete), calendar.events +
calendar.readonly (it can't create, share or delete a calendar), and drive.readonly.
Unknown is not empty. A missing or broken credential raises an error that says to report the source as UNKNOWN. An unreadable inbox must never look like an empty one.
Errors carry their reason. A bare "Error executing tool" can't be diagnosed, so every failure is re-raised with its exception type and message.
Related MCP server: AgentBridge
What production taught it
Parallel calls segfaulted the server. The client fires tool calls in parallel, the SDK runs sync tools on worker threads, and the Google client objects aren't thread-safe. Two concurrent calls killed the process (exit 139, reproduced), and the client only saw a generic error for both. A re-entrant lock now makes calls queue.
Bursts hit Gmail's per-user rate limit. Every request now retries 403/429/5xx with exponential backoff.
Thread search was slow. Fetching 44 threads one by one took 19 seconds. They're now fetched in batches of 20, and any batch member that fails is retried singly, never dropped.
Output size is a cost. A hosted connector returned 72 KB for two weeks of calendar. Here, list calls return trimmed summaries with descriptions cut to 160 characters, and bodies are capped.
Plain-text links get rewritten. Gmail wraps bare URLs in a redirect, so the send and draft tools take an
html_bodywith real anchors.
Tools
Area | Tools |
Gmail, read |
|
Gmail, write |
|
Gmail, destructive |
|
Calendar |
|
Drive |
|
Setup
In your own Google Cloud project, enable the Gmail, Calendar and Drive APIs and create a Desktop app OAuth client. Download its JSON.
Install and authorize:
python3 -m venv .venv && . .venv/bin/activate
pip install -r requirements.txt
export GWS_EXPECTED_ACCOUNT=you@example.com
export GWS_CLIENT_SECRET=~/path/to/client_secret.json
python gws_auth.py --url # open the URL, approve, copy the failed localhost URL
python gws_auth.py --code '<URL>' # saves ~/.config/gws-personal/token.json (mode 600)
python gws_auth.py --status # one REAL call per API; a file on disk proves nothingRegister it with Claude Code at user scope, so every session gets it:
claude mcp add -s user \
-e GWS_EXPECTED_ACCOUNT=you@example.com -e GWS_TZ=Asia/Manila \
gws -- "$PWD/.venv/bin/python" "$PWD/gws_mcp.py"python gws_mcp.py --selftest checks MIME building (HTML + plain text, attachments,
reply threading) offline. gws_auth.py --export prints the token as one base64 line for
a cloud environment variable ($GWS_PERSONAL_TOKEN). Treat that line as a live
credential.
Related
How I work with AI agents: the agent setup this server is part of, with real sessions replayed.
ProjectCRM: a CRM whose order intake puts a language model behind the same kind of guardrails.
License
See LICENSE.
This server cannot be deployed
Maintenance
Related MCP Connectors
Multiple Gmail accounts, editable Google Sheets & Docs for AI agents. Deny-by-default access rules.
Permissioned access to Gmail, Drive and Calendar via the user's own Google account
Gmail, Outlook, Drive, OneDrive and calendars for AI agents. Many accounts, one endpoint, audit log.
MCP server connecting AI agents to 100+ apps (Gmail, Slack, Notion, GitHub) via one-click OAuth.
Related MCP Servers
- AlicenseNot gradedqualityDmaintenanceEnables AI agents to send emails, create drafts in Gmail, and append content to Google Docs via standardized MCP tools.35 npmISC
- FlicenseNot gradedqualityCmaintenanceEnables agents to access Google Calendar events, search Gmail, and semantically search local notes through MCP tools, with safety guardrails and human-in-the-loop approval.-
- AlicenseNot gradedqualityBmaintenanceEnables AI agents to send Gmail emails, create drafts, and append content to Google Docs through MCP tools. Provides secure OAuth-based integration with Google Workspace.312 npmMIT
- AlicenseNot gradedqualityCmaintenanceEnables AI agents to send and draft Gmail emails and append content to Google Docs through standardized MCP tools.11 npmMIT