Gmail MCP Server
A Model Context Protocol server for Gmail that lets AI assistants read, send, search, label, and filter Gmail through natural language, with OAuth2 auto-authentication and full attachment support.
README
Gmail MCP Server
A Model Context Protocol server for Gmail. Lets AI assistants read, send, search, label, and filter Gmail through natural language, with OAuth2 auto-authentication and full attachment support.
Built with TypeScript, Bun, and the googleapis client.
Highlights
- Structured JSON responses.
read_emailandsearch_emailsreturn parseable JSON objects, not pre-formatted text. Easier for downstream tools to consume without fragile string parsing. (See Structured responses below.) - Full attachment support. Send files, read attachment metadata, and download attachments to disk. Path traversal is blocked at the download boundary.
- HTML-first email reading. When a message has both plain text and HTML parts, both are returned so the client can pick.
- Comprehensive label management. Create, update, delete, list, and get-or-create labels. System labels are protected from deletion.
- Gmail filter management. Full CRUD on filters plus six templates (
fromSender,withSubject,withAttachments,largeEmails,containingText,mailingList). - Batch operations. Modify or delete many messages in one call, with graceful per-item fallback if a batch fails.
- RFC 2047 subject encoding for international characters.
- Typed input validation via Zod schemas on every tool.
- Error responses set
isError: trueso MCP clients can distinguish failures from successes. - Automated releases via semantic-release on every push to
main.
Installation
1. Clone and build
git clone https://github.com/simiancraft/Gmail-MCP-Server.git
cd Gmail-MCP-Server
bun install
bun run build
2. Create Google Cloud OAuth credentials
- Go to the Google Cloud Console and create (or select) a project.
- Enable the Gmail API for the project.
- Under APIs & Services → Credentials, click Create Credentials → OAuth client ID.
- Choose Desktop app or Web application. For Web application, add
http://localhost:3000/oauth2callbackto the authorized redirect URIs. - Download the JSON, rename it to
gcp-oauth.keys.json.
3. Authenticate
mkdir -p ~/.gmail-mcp
mv gcp-oauth.keys.json ~/.gmail-mcp/
bun dist/index.js auth
The auth command:
- Looks for
gcp-oauth.keys.jsonin the current directory or~/.gmail-mcp/. - Opens your browser to the Google consent screen.
- Saves the resulting token to
~/.gmail-mcp/credentials.json.
Credentials can be overridden with environment variables:
GMAIL_OAUTH_PATH— path to the client keys JSONGMAIL_CREDENTIALS_PATH— path to the stored user token JSON
4. Wire it into your MCP client
Example for Claude Desktop:
{
"mcpServers": {
"gmail": {
"command": "bun",
"args": ["/absolute/path/to/Gmail-MCP-Server/dist/index.js"]
}
}
}
With custom credential paths:
{
"mcpServers": {
"gmail": {
"command": "bun",
"args": ["/absolute/path/to/Gmail-MCP-Server/dist/index.js"],
"env": {
"GMAIL_OAUTH_PATH": "/path/to/gcp-oauth.keys.json",
"GMAIL_CREDENTIALS_PATH": "/path/to/credentials.json"
}
}
}
}
Structured responses
This is the most important behavioral difference worth calling out: read_email and search_emails return structured JSON rather than pre-formatted text blobs. The JSON is embedded in the MCP content[0].text field, so clients can JSON.parse it directly.
read_email response
{
"messageId": "182ab45cd67ef",
"threadId": "182ab45cd67ef",
"subject": "Project Files",
"from": "sender@example.com",
"to": "recipient@example.com",
"date": "Thu, 19 Jun 2025 10:30:00 -0400",
"body": {
"text": "Plain text version of the email...",
"html": "<html>Rich HTML version...</html>"
},
"attachments": [
{
"id": "ANGjdJ9fkTs...",
"filename": "document.pdf",
"mimeType": "application/pdf",
"size": 250880
}
]
}
body.text and body.html are independently null when the message does not contain that part. Most transactional/vendor email is HTML-only; some plain-text-only senders only populate text.
search_emails response
[
{
"id": "182ab45cd67ef",
"subject": "Project Files",
"from": "sender@example.com",
"date": "Thu, 19 Jun 2025 10:30:00 -0400"
}
]
All other tools return human-readable status text in content[0].text (e.g. "Email sent successfully with ID: ...").
Tool reference
All 19 tools accept validated JSON input. Required fields are called out explicitly; everything else is optional.
send_email
Sends a new email. Supports plain text, HTML, multipart, attachments, and threading.
{
"to": ["recipient@example.com"],
"subject": "Meeting Tomorrow",
"body": "Hi, reminder about our meeting.",
"mimeType": "text/plain",
"cc": ["cc@example.com"],
"bcc": ["bcc@example.com"],
"attachments": ["/path/to/file.pdf"],
"threadId": "182ab45cd67ef",
"inReplyTo": "<message-id@example.com>"
}
mimeTypedefaults totext/plain. Usetext/htmlfor HTML-only, ormultipart/alternativewith bothbodyandhtmlBodyfor clients that can render either.- Attachments are validated for existence before send.
draft_email
Same input as send_email but creates a draft instead of sending.
read_email
Retrieves full content of a message. See Structured responses.
{ "messageId": "182ab45cd67ef" }
search_emails
Searches using Gmail search syntax. Returns an array of metadata (see Structured responses).
{
"query": "from:sender@example.com after:2024/01/01 has:attachment",
"maxResults": 10
}
modify_email
Adds or removes labels on a single message. If both addLabelIds and labelIds are provided, addLabelIds takes precedence.
{
"messageId": "182ab45cd67ef",
"addLabelIds": ["IMPORTANT"],
"removeLabelIds": ["INBOX"]
}
delete_email
Permanently deletes an email. Irreversible.
{ "messageId": "182ab45cd67ef" }
download_attachment
Downloads an attachment to disk. Filenames are sanitized (path.basename) and the resolved output path is verified to stay inside savePath to prevent traversal.
{
"messageId": "182ab45cd67ef",
"attachmentId": "ANGjdJ9fkTs...",
"savePath": "/path/to/downloads",
"filename": "custom-name.pdf"
}
savePath defaults to the current working directory. filename defaults to the original filename from the message.
Batch
batch_modify_emails
Adds/removes labels on many messages at once. Falls back to per-item retry if a batch fails.
{
"messageIds": ["id1", "id2", "id3"],
"addLabelIds": ["IMPORTANT"],
"removeLabelIds": ["INBOX"],
"batchSize": 50
}
batch_delete_emails
Permanently deletes many messages in batches.
{
"messageIds": ["id1", "id2", "id3"],
"batchSize": 50
}
Labels
list_email_labels
Lists all labels, split into system and user groups.
{}
create_label
{
"name": "Important Projects",
"messageListVisibility": "show",
"labelListVisibility": "labelShow"
}
update_label
{
"id": "Label_1234567890",
"name": "Urgent Projects"
}
delete_label
Refuses to delete system labels.
{ "id": "Label_1234567890" }
get_or_create_label
Idempotent. Returns the existing label if one with that name exists; otherwise creates it. The response tells you which happened.
{
"name": "Project XYZ",
"messageListVisibility": "show",
"labelListVisibility": "labelShow"
}
Filters
create_filter
{
"criteria": {
"from": "newsletter@company.com",
"hasAttachment": false
},
"action": {
"addLabelIds": ["Label_Newsletter"],
"removeLabelIds": ["INBOX"]
}
}
list_filters, get_filter, delete_filter
{}
{ "filterId": "ANe1Bmj..." }
create_filter_from_template
Six templates: fromSender, withSubject, withAttachments, largeEmails, containingText, mailingList.
{
"template": "fromSender",
"parameters": {
"senderEmail": "notifications@github.com",
"labelIds": ["Label_GitHub"],
"archive": true
}
}
Gmail search syntax
search_emails accepts any standard Gmail search operator:
| Operator | Example | Description |
|---|---|---|
from: |
from:john@example.com |
Emails from a specific sender |
to: |
to:mary@example.com |
Emails to a specific recipient |
subject: |
subject:"meeting notes" |
Subject match |
has:attachment |
has:attachment |
Has attachments |
after: |
after:2024/01/01 |
After a date |
before: |
before:2024/02/01 |
Before a date |
is: |
is:unread |
Message state |
label: |
label:work |
Has a specific label |
Combine freely: from:john@example.com after:2024/01/01 has:attachment.
Security
- OAuth credentials are stored locally in
~/.gmail-mcp/(or the paths set in env vars). Never commit them. - Attachment downloads validate the resolved output path against
savePathto block path traversal via crafted filenames. - The server requests
gmail.modifyandgmail.settings.basicscopes — enough to read/send/label/filter, but not to change account settings beyond filters. - Offline access is used so the token refreshes without re-consent. Revoke in your Google Account → Security → Third-party access if you want to kill it.
- Error responses set
isError: trueso clients can distinguish failures from successes rather than pattern-matching on text.
Development
bun install # install dependencies
bun run build # compile TypeScript to dist/
bun test # run the test suite (53 tests)
bun run lint # biome check
bun run lint:fix # biome auto-fix
bun run format # biome format
Tests live alongside source as src/*.test.ts and use Bun's built-in test runner. Running bun test discovers them automatically — no extra config.
Commit style
This project uses Conventional Commits and semantic-release. Pushes to main with the right prefix automatically bump the version and publish a GitHub Release.
| Prefix | Bump | Example |
|---|---|---|
fix: |
patch | fix: handle missing attachment header |
feat: |
minor | feat: add batch label operations |
feat!: / BREAKING CHANGE: |
major | feat!: change auth flow |
chore:, docs:, ci:, refactor:, test: |
no release | chore: update biome config |
Troubleshooting
- OAuth keys not found. Make sure
gcp-oauth.keys.jsonis in~/.gmail-mcp/(or in your current working directory at auth time). - Invalid credentials format. The keys file must contain either a
weborinstalledblock. For web apps, verifyhttp://localhost:3000/oauth2callbackis in the authorized redirect URIs. - Port 3000 already in use. Free it before running
bun dist/index.js auth— the OAuth callback server listens there. - Batch operation failures. Batches automatically retry per-item; check the returned failure list. If you're hitting rate limits, lower
batchSize. - Attachment send fails. Verify paths exist and are readable. Gmail's per-message attachment cap is 25 MB.
License
MIT. See LICENSE.
History
This project started as a clone of gongrzhe/server-gmail-autoauth-mcp in 2025. It has since diverged substantially — rewritten build/runtime to Bun, added semantic-release, restructured source into separate schemas/label/filter modules, added a full test suite, changed read_email and search_emails to return structured JSON, and tightened security, error handling, and type safety. The LICENSE file preserves the original MIT copyright, per the license's requirements.
推荐服务器
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 模型以安全和受控的方式获取实时的网络信息。