gmail-research-mcp
Allows searching, reading, and synchronizing Gmail conversations and threads to local Markdown and JSON storage.
Click on "Install 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., "@gmail-research-mcpsearch for emails about Project Alpha from last year"
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.
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.readonlyThe 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.
Related MCP server: Gmail Streamable MCP Server
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 testCreate 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 .envExample:
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_dataGMAIL_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 doctorauth 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-localThis 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 20Add --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_IDThe 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 \
--attachmentsSynchronize 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 \
--attachmentsPreviously 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_dataAttachment 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.mdGmail 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 mcpAdd 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 listIn Codex TUI, inspect the server with:
/mcpThe 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-syncIf 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-syncExample 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 -qTests 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.
This server cannot be installed
Resources
Unclaimed servers have limited discoverability.
Looking for Admin?
If you are the server author, to access and configure the admin panel.
Latest Blog Posts
- Your AI Chatbot Just Exposed Your CEO's Salary to an InternBy Om-Shree-0709 on .Agent IdentityMCP SecurityOAuth Delegation
- Why MCP Servers Need Execution Sandboxing (And Why Your Current Stack Isn't Enough)By Om-Shree-0709 on .Agentic AiPrompt InjectionWebAssembly
MCP directory API
We provide all the information about MCP servers via our MCP API.
curl -X GET 'https://glama.ai/api/mcp/v1/servers/bajor/gmail-mcp-read-only'
If you have feedback or need assistance with the MCP directory API, please join our Discord server