Gmail Mcp — independently scanned and version-tracked by SaferSkills.
SaferSkills independently audited Gmail Mcp (Agent Skill) and scored it 100/100 (green). The audit ran 55 deterministic rules across Security, Supply Chain, Maintenance, Transparency, and Community; it found 0 high-severity and 0 lower-severity findings. The full rule-by-rule trace and per-finding evidence are below. Free, methodology-open.
Findings & checks · 0 flagged
Every scanned point with the score it earned and what moved between them.
First recorded scan — no prior version to compare against.
The primary manifest — the file an agent reads to learn what this artifact does.
Full Gmail control for any MCP-compatible AI agent. Exposes ~40 tools covering reading, composing, labeling, filtering, threading, and account management — works with Claude, Cursor, Copilot, Goose, OpenCode, and any agent that speaks the MCP stdio protocol.
Detects Arch or Ubuntu/Debian automatically, creates a virtualenv, installs deps, sets credential permissions, and prints the MCP config snippet ready to paste:
bash install.shManual steps follow below if you prefer full control.
pip install -r requirements.txtrequirements.txt:
mcp
google-api-python-client
google-auth-oauthlib
google-auth-httplib2gmail-mcp) → CreateGmail API → click it → EnableGmail MCP)Important: While the app is in Testing mode only the users listed here can authenticate. The app does not need to be published.
| Scope | Why |
|---|---|
https://mail.google.com/ | Full read/write/delete/send access |
https://www.googleapis.com/auth/gmail.settings.basic | Create/manage filters and labels |
gmail.settings.basicis required specifically forfilters.create/filters.delete.mail.google.comalone is not sufficient for filter management despite appearing to be a superset scope.
credentials.json in this project directoryRun the server once manually to trigger the OAuth browser flow:
python server.pyA browser window opens. Log in with the Google account added as a test user and grant both requested permissions.
This writes token.pickle to the project directory. Subsequent runs use this token silently (auto-refreshed by Google's OAuth library).
If you change the requested scopes (e.g. add gmail.settings.basic later), the old token must be deleted:
rm token.pickle
python server.pyAll tools below use the same MCP stdio transport. The only difference between them is where the config file lives.
Common values to substitute throughout:
PYTHON → full path to the Python binary with dependencies (e.g. /home/user/.local/share/mamba/bin/python)SERVER → full path to server.py (e.g. /home/user/gmail/server.py)~/.claude.json:
{
"mcpServers": {
"gmail": {
"type": "stdio",
"command": "PYTHON",
"args": ["SERVER"],
"env": {}
}
}
}~/.claude/settings.json:
{
"mcpServers": {
"gmail": {
"command": "PYTHON",
"args": ["SERVER"]
}
}
}Restart Claude Code after editing.
Global config ~/.cursor/mcp.json (or per-project .cursor/mcp.json):
{
"mcpServers": {
"gmail": {
"command": "PYTHON",
"args": ["SERVER"]
}
}
}Reload via Cursor Settings → MCP or restart Cursor.
~/.codeium/windsurf/mcp_config.json:
{
"mcpServers": {
"gmail": {
"command": "PYTHON",
"args": ["SERVER"]
}
}
}Reload via Windsurf Settings → MCP Servers → Refresh.
VS Code 1.99+ supports MCP natively. Add to your User `settings.json` (Ctrl+Shift+P → Open User Settings JSON):
{
"mcp": {
"servers": {
"gmail": {
"type": "stdio",
"command": "PYTHON",
"args": ["SERVER"]
}
}
}
}Or create a workspace-scoped .vscode/mcp.json:
{
"servers": {
"gmail": {
"type": "stdio",
"command": "PYTHON",
"args": ["SERVER"]
}
}
}Copilot picks it up automatically. No restart needed.
Open the Cline sidebar → MCP Servers tab → Edit MCP Settings.
This opens cline_mcp_settings.json. Add:
{
"mcpServers": {
"gmail": {
"command": "PYTHON",
"args": ["SERVER"],
"disabled": false,
"autoApprove": []
}
}
}Linux path: ~/.config/Code/User/globalStorage/saoudrizwan.claude-dev/settings/cline_mcp_settings.json
~/.continue/config.json:
{
"mcpServers": [
{
"name": "gmail",
"command": "PYTHON",
"args": ["SERVER"]
}
]
}Or ~/.continue/config.yaml:
mcpServers:
- name: gmail
command: PYTHON
args:
- SERVERReload with Ctrl+Shift+P → Continue: Reload Config.
~/.config/zed/settings.json:
{
"context_servers": {
"gmail": {
"command": {
"path": "PYTHON",
"args": ["SERVER"],
"env": {}
},
"settings": {}
}
}
}Zed reloads context servers automatically on save.
~/.aws/amazonq/mcp.json:
{
"mcpServers": {
"gmail": {
"command": "PYTHON",
"args": ["SERVER"]
}
}
}Restart the Q Developer extension or CLI after editing.
~/.config/goose/config.yaml:
extensions:
gmail:
name: gmail
type: stdio
cmd: PYTHON
args:
- SERVER
enabled: trueRun goose session — Goose loads extensions at startup.
~/.config/opencode/config.json:
{
"mcp": {
"gmail": {
"command": "PYTHON",
"args": ["SERVER"]
}
}
}pip install anthropicimport anthropic
client = anthropic.Anthropic()
with client.beta.messages.stream(
model="claude-opus-4-8",
max_tokens=4096,
mcp_servers=[{
"type": "stdio",
"command": "PYTHON",
"args": ["SERVER"],
}],
messages=[{"role": "user", "content": "List my last 5 unread emails."}],
betas=["mcp-client-2025-04-04"],
) as stream:
for text in stream.text_stream:
print(text, end="", flush=True)pip install openai-agentsimport asyncio
from agents import Agent, Runner
from agents.mcp import MCPServerStdio
async def main():
async with MCPServerStdio(
params={"command": "PYTHON", "args": ["SERVER"]}
) as mcp:
agent = Agent(
name="gmail-agent",
instructions="You manage Gmail for the user.",
mcp_servers=[mcp],
)
result = await Runner.run(agent, "List my last 5 unread emails.")
print(result.final_output)
asyncio.run(main())claude --mcp-config /path/to/mcp-config.json \
-p "Summarize my unread emails from today."mcp-config.json:
{
"mcpServers": {
"gmail": {
"type": "stdio",
"command": "PYTHON",
"args": ["SERVER"]
}
}
}The OAuth flow requires a browser the first time. For agents running headlessly:
token.pickletoken.pickle and credentials.json to the headless machinechmod 600 credentials.json token.pickleDo not pass secrets via environment variables.
| Tool | Description |
|---|---|
list_messages | List/search messages (Gmail search syntax) |
get_message | Full plain-text content of a message |
get_message_html | HTML body of a message |
list_threads | List conversation threads |
get_thread | Full thread with all messages |
list_attachments | List attachments on a message |
get_attachment | Download attachment to local path |
| Tool | Description |
|---|---|
send_email | Send a new email |
reply_email | Reply to a thread |
forward_email | Forward a message |
save_draft | Save a draft |
list_drafts | List drafts |
delete_draft | Delete a draft |
| Tool | Description |
|---|---|
trash_message | Move to trash |
untrash_message | Restore from trash |
delete_permanently | Delete bypassing trash |
archive_message | Archive (remove from inbox) |
move_to_spam | Mark as spam |
mark_read | Mark as read |
mark_unread | Mark as unread |
star_message | Star a message |
unstar_message | Unstar a message |
apply_label | Apply label by ID |
remove_label | Remove label by ID |
batch_trash | Trash multiple messages |
batch_delete_permanently | Permanently delete multiple messages |
batch_archive | Archive multiple messages |
batch_mark_read | Mark multiple messages read |
trash_by_query | Trash all messages matching a search |
archive_by_query | Archive all messages matching a search |
| Tool | Description |
|---|---|
list_labels | List all labels (system + user) |
create_label | Create a label (supports / for hierarchy) |
delete_label | Delete a label by ID |
rename_label | Rename a label |
| Tool | Description |
|---|---|
list_filters | List all Gmail filters |
create_filter | Create a filter (from/to/subject/query + label actions) |
delete_filter | Delete a filter by ID |
| Tool | Description |
|---|---|
get_profile | Email address, message count, storage quota |
download_all_messages | Export all messages to JSON file (paginated) |
get_download_status | Check progress of an ongoing export |
403 insufficientPermissions on filter operationsThe token was created without gmail.settings.basic. Delete it and re-authenticate:
rm token.pickle
python server.pyVerify SCOPES in server.py contains both scopes:
SCOPES = [
"https://mail.google.com/",
"https://www.googleapis.com/auth/gmail.settings.basic",
]Token has been expired or revokedDelete token.pickle and re-authenticate. This happens if access is revoked in Google account settings or if the app has been inactive for 6+ months in testing mode.
redirect_uri_mismatchThe OAuth client must be type Desktop app, not Web application. Delete the existing client ID and create a new one with the correct type.
credentials.json and token.pickle exist in the project directorypython server.py manually to check for import errors~/.claude.json~30 seconds. Free. No account. Every finding cites a rule and a line of evidence.