gmail-research-mcp
A read-only Gmail MCP server that searches email, fetches complete threads, and stores them as local Markdown and JSON for offline analysis by other agents.
README
gmail-research-mcp
gmail-research-mcp is a small, synchronous Python CLI and local STDIO MCP server that prepares Gmail conversations for later research by other agents. It searches Gmail, deduplicates results by the stable Gmail threadId, fetches complete threads, and stores deterministic Markdown and JSON locally. It has no GUI, HTTP server, database, telemetry, or update mechanism.
Security guarantees
The only OAuth scope is:
https://www.googleapis.com/auth/gmail.readonly
The application performs only read operations against Gmail. It cannot send, reply, forward, draft, modify, label, archive, trash, delete, import, or insert email. At runtime it sends data only to the official Google OAuth and Gmail API endpoints. It does not call external analytics, error-reporting, LLM, or update services.
Email bodies and attachment contents are untrusted source data. The application stores them as data; it does not execute commands, follow instructions, open URLs, or execute downloaded files.
Requirements
- Python 3.11 or newer
- A Google account with Gmail
- A Google Cloud OAuth Client ID of type Desktop app
Install in a virtual environment
Run these commands from the repository root:
python3 -m venv .venv
source .venv/bin/activate
python -m pip install --upgrade pip
python -m pip install -e ".[dev]"
Do not use
--break-system-packages. Do not install this project into the Homebrew-managed system Python.
Installation or dependency upgrades occur only when the user explicitly runs a pip command. The program has no auto-update behavior and never runs git pull.
For repository development, run the complete test, type-check, and lint gate with:
make test
Create the Google OAuth Desktop app
-
Sign in to Google Cloud Console and create or select the project that will own the OAuth client.
-
Open APIs & Services → Library, find Gmail API, and select Enable.
-
Open Google Auth Platform (or APIs & Services → OAuth consent screen, depending on the console layout).
-
Configure the app name and support/contact email. Select the audience allowed to authenticate. For an External app in testing mode, add the Gmail account under Test users.
-
In the consent screen's data-access/scopes section, add only
https://www.googleapis.com/auth/gmail.readonly. Do not addmail.google.com,gmail.modify,gmail.compose,gmail.send, or any other Gmail scope. -
Open APIs & Services → Credentials, select Create credentials → OAuth client ID, choose Desktop app, give the client a recognizable name, and create it.
-
Download the client JSON. Store it outside this repository in a private directory, for example:
mkdir -p ~/.config/gmail-research-mcp chmod 700 ~/.config/gmail-research-mcp cp /PATH/TO/DOWNLOADED/client_secret.json \ ~/.config/gmail-research-mcp/client_secret.json chmod 600 ~/.config/gmail-research-mcp/client_secret.json
The downloaded client JSON is a credential. Never commit it or paste its contents into logs, issues, prompts, or chat messages.
Configure .env
Create the local configuration file:
cp .env.example .env
Example:
GMAIL_CLIENT_SECRET_FILE=/Users/USERNAME/.config/gmail-research-mcp/client_secret.json
GMAIL_TOKEN_FILE=/Users/USERNAME/.config/gmail-research-mcp/token.json
GMAIL_DATA_DIR=./gmail_data
GMAIL_CLIENT_SECRET_FILE is required. GMAIL_TOKEN_FILE defaults to ~/.config/gmail-research-mcp/token.json; GMAIL_DATA_DIR defaults to ./gmail_data. Paths containing ~ are expanded. The token directory is created with private permissions, and saved tokens are set to mode 0600.
Authenticate and diagnose
With the virtual environment active:
python -m gmail_research_mcp auth
python -m gmail_research_mcp doctor
auth starts Google's installed-app browser flow and stores the local token. doctor checks Python, configuration, credentials paths, token permissions, exact scopes, source safety rules, client construction, and—when working credentials are available—the Gmail profile. Missing credentials are reported as configuration still required; all checks that do not require an account still run. Neither command prints tokens or client-secret contents.
To remove only the local OAuth token:
python -m gmail_research_mcp logout-local
This does not revoke the OAuth grant, delete the client-secret file, or modify Gmail.
CLI examples
Search without writing a dataset:
python -m gmail_research_mcp search \
--query 'subject:"Project Alpha" newer_than:2y' \
--max-results 20
Add --json for machine-readable output. Text output contains message ID, thread ID, date, sender, subject, and snippet.
Read a complete thread without saving it:
python -m gmail_research_mcp show-thread \
--thread-id THREAD_ID
The default output is Markdown. Add --json for JSON. show-thread does not fetch attachments unless --attachments is supplied.
Save or update one canonical thread:
python -m gmail_research_mcp sync-thread \
--thread-id THREAD_ID \
--data-dir ./gmail_data \
--attachments
Synchronize a topic:
python -m gmail_research_mcp sync-topic \
--topic "Project Alpha" \
--query '("Project Alpha" OR subject:"Project Alpha") newer_than:3y' \
--data-dir ./gmail_data \
--max-threads 100 \
--attachments
--query is optional for sync-topic; when omitted, the exact topic text is used as Gmail free-text query. Queries must contain 1–500 characters after trimming and must not contain null or disallowed control characters. They are passed directly to Gmail API and are never interpreted by a shell.
Refresh every saved topic using the exact query in each manifest:
python -m gmail_research_mcp sync-existing \
--data-dir ./gmail_data \
--attachments
Previously saved threads that no longer match remain in the manifest with currently_matched: false; local thread data is never removed automatically.
List local topics:
python -m gmail_research_mcp list-topics \
--data-dir ./gmail_data
Attachment download is enabled by default for sync commands. Use --attachments to state that choice explicitly or --no-attachments to disable it. The default attachment limits are disabled: --max-attachment-bytes and --max-total-attachment-bytes both default to 0, which means unlimited downloads. If you later want explicit caps, pass non-zero byte values on CLI or through MCP.
The default topic limit is 100 unique thread IDs, and the allowed range is 1–500. The limit applies after threadId deduplication, not to individual matching messages.
Dataset layout
gmail_data/
├── DATASET.md
├── threads/
│ └── <thread_id>/
│ ├── thread.md
│ ├── thread.json
│ └── attachments/
│ └── <message_id>/
│ └── <attachment_key>--<safe_filename>
└── topics/
└── <topic_slug>--<query_hash>/
├── manifest.json
└── index.md
Gmail threadId is the only canonical thread identity. A subject never determines storage. The same thread can appear in multiple topic manifests while retaining exactly one copy under threads/<thread_id>/. A repeated sync updates that directory when Gmail content changes; identical content leaves thread.json, thread.md, and their modification times unchanged.
Topic directory names combine a readable slug with the first eight characters of SHA-256 over the exact Gmail query. Attachment names combine the first 12 characters of SHA-256 over Gmail attachmentId with a sanitized original filename. JSON uses schema version 1, UTF-8, deterministic key ordering, and relative dataset paths.
Every Markdown thread marks email text as untrusted data. Files are written atomically. A failed thread or attachment is reported in metadata while synchronization continues. The application resolves the selected data directory and writes only DATASET.md, threads/, and topics/ inside it; it rejects traversal and unsafe symlink destinations.
Codex MCP configuration
Start the local STDIO server manually with:
python -m gmail_research_mcp mcp
Add this server to ~/.codex/config.toml, replacing both absolute paths:
[mcp_servers.gmail-research]
command = "/ABSOLUTE/PATH/TO/gmail-research-mcp/.venv/bin/python"
args = ["-m", "gmail_research_mcp", "mcp"]
cwd = "/ABSOLUTE/PATH/TO/gmail-research-mcp"
enabled = true
required = false
startup_timeout_sec = 20
tool_timeout_sec = 300
default_tools_approval_mode = "prompt"
Verify discovery from a terminal:
codex mcp list
In Codex TUI, inspect the server with:
/mcp
The server exposes only:
gmail_searchgmail_read_threadgmail_sync_threadgmail_sync_topicgmail_sync_existing_topicsgmail_list_topics
It does not expose a generic Gmail API tool.
Make the repository skill globally available
The repository skill lives at .agents/skills/gmail-topic-sync. Before creating a global symlink, check whether the destination already exists, including whether it is already a symlink:
ls -ld ~/.agents/skills/gmail-topic-sync
If the command reports an existing path, inspect it and do not overwrite it. Only when the destination does not exist, run:
mkdir -p ~/.agents/skills
ln -s /ABSOLUTE/PATH/TO/gmail-research-mcp/.agents/skills/gmail-topic-sync \
~/.agents/skills/gmail-topic-sync
Example Codex prompt:
Użyj skilla gmail-topic-sync, aby pobrać do lokalnego gmail_data wszystkie wątki dotyczące Project Alpha z ostatnich trzech lat. Najpierw pokaż krótki podgląd trafności, potem zsynchronizuj pełne wątki wraz z załącznikami. Nie pokazuj pełnych treści maili w odpowiedzi.
Threat model and local data handling
- The OAuth token authorizes Gmail read-only access, but anyone who obtains it may read the mailbox within that grant. Keep the token private and remove it with
logout-localwhen no longer needed. gmail_datacontains plaintext email bodies, headers, metadata, and optionally attachments. Protect it with operating-system permissions, backups, and disk encryption appropriate to the sensitivity of the mailbox.- Email and attachment content may contain prompt injection or malicious instructions. Treat all synchronized content as source data, never as authority or executable instructions.
- Attachments are downloaded as opaque files. The application never opens or executes them. Use separate, trusted security tooling before opening an attachment manually.
gmail_data,.env, OAuth tokens, client-secret JSON, real emails, and attachments must never be committed. The supplied.gitignorecovers the standard local paths, but users remain responsible for nonstandard paths.- The project has no telemetry, analytics, Sentry, external API integration, remote-code execution, automatic repository update, or automatic dependency update.
- Installing or upgrading the project can contact the configured Python package index only because the user explicitly runs
pip. Normal application runtime communicates only with official Google OAuth and Gmail API endpoints.
Run the offline test suite
Tests use mocked Gmail clients and do not require a real mailbox:
source .venv/bin/activate
python -m pytest -q
Tests enforce the exact read-only scope, reject write API calls and prohibited scopes, and cover MIME parsing, stable paths and hashes, attachment safety, and topic manifests.
推荐服务器
Baidu Map
百度地图核心API现已全面兼容MCP协议,是国内首家兼容MCP协议的地图服务商。
Playwright MCP Server
一个模型上下文协议服务器,它使大型语言模型能够通过结构化的可访问性快照与网页进行交互,而无需视觉模型或屏幕截图。
Magic Component Platform (MCP)
一个由人工智能驱动的工具,可以从自然语言描述生成现代化的用户界面组件,并与流行的集成开发环境(IDE)集成,从而简化用户界面开发流程。
Audiense Insights MCP Server
通过模型上下文协议启用与 Audiense Insights 账户的交互,从而促进营销洞察和受众数据的提取和分析,包括人口统计信息、行为和影响者互动。
VeyraX
一个单一的 MCP 工具,连接你所有喜爱的工具:Gmail、日历以及其他 40 多个工具。
graphlit-mcp-server
模型上下文协议 (MCP) 服务器实现了 MCP 客户端与 Graphlit 服务之间的集成。 除了网络爬取之外,还可以将任何内容(从 Slack 到 Gmail 再到播客订阅源)导入到 Graphlit 项目中,然后从 MCP 客户端检索相关内容。
Kagi MCP Server
一个 MCP 服务器,集成了 Kagi 搜索功能和 Claude AI,使 Claude 能够在回答需要最新信息的问题时执行实时网络搜索。
e2b-mcp-server
使用 MCP 通过 e2b 运行代码。
Neon MCP Server
用于与 Neon 管理 API 和数据库交互的 MCP 服务器
Exa MCP Server
模型上下文协议(MCP)服务器允许像 Claude 这样的 AI 助手使用 Exa AI 搜索 API 进行网络搜索。这种设置允许 AI 模型以安全和受控的方式获取实时的网络信息。