Gmail + Google Docs MCP Server
An MCP server that provides tools for drafting and sending Gmail emails, as well as creating and appending to Google Docs, using OAuth 2.0 authentication.
README
Gmail + Google Docs MCP Server
An MCP server that gives an AI agent tools to:
- Draft and send Gmail emails
- Create Google Docs and append content to them
Built with the official @modelcontextprotocol/sdk and Google's googleapis client, using a one-time OAuth 2.0 browser login (no Google Workspace admin setup required — works with a normal personal or Workspace Gmail account).
Tools exposed
| Tool | Description |
|---|---|
gmail_create_draft |
Creates a draft email (not sent) |
gmail_send_email |
Sends an email immediately |
gmail_send_draft |
Sends a previously created draft by ID |
gmail_list_drafts |
Lists existing drafts |
docs_create |
Creates a new Google Doc, optionally with initial text |
docs_append_text |
Appends text to the end of an existing Google Doc (safe under concurrent edits — see below) |
server_metrics |
Returns per-tool call counts, error rates, and latency for this running server |
gmail_create_draft and gmail_send_email also accept an optional attachments array (up to 10 files, 20MB each): { filename, contentBase64, mimeType }.
1. Set up Google Cloud credentials
- Go to the Google Cloud Console and create (or select) a project.
- Enable these two APIs (APIs & Services > Library):
- Gmail API
- Google Docs API
- Configure the OAuth consent screen (APIs & Services > OAuth consent screen):
- User type: "External" is fine for personal use (add yourself as a test user), or "Internal" if using a Workspace account.
- Create credentials (APIs & Services > Credentials > Create Credentials > OAuth client ID):
- Application type: Desktop app (accepts any loopback redirect URI, so
GOOGLE_REDIRECT_URIbelow doesn't need to be pre-registered).
- Application type: Desktop app (accepts any loopback redirect URI, so
- Copy
.env.exampleto.envand fill inGOOGLE_CLIENT_IDandGOOGLE_CLIENT_SECRETfrom that OAuth client (adjustGOOGLE_REDIRECT_URItoo if you want a different local port/path).
2. Install dependencies and build
npm install
npm run build
3. Authorize (one-time)
npm run authorize
This opens your browser to sign in to Google and grant access to Gmail (compose/send) and Docs. After you approve, tokens are saved to the path in GOOGLE_TOKEN_STORAGE_PATH (default tokens.json in the project root). Re-run this if you ever revoke access or delete that file.
.env and the token storage file contain secrets — they're already excluded via .gitignore and should never be committed.
4. Run the server
npm start
By default (MCP_TRANSPORT=stdio) the server communicates over stdio, so normally you won't run it directly — instead point your MCP client (e.g. Cursor) at it. To host it remotely instead (e.g. on Render), see deployment-plan.md and the "Remote (HTTP) hosting" section below.
5. Connect it to Cursor
Add this to your mcp.json (Cursor Settings > MCP, or .cursor/mcp.json in a project):
{
"mcpServers": {
"gmail-docs": {
"command": "node",
"args": ["C:\\Users\\thriv\\OneDrive\\Documents\\BuildMCP\\dist\\index.js"]
}
}
}
Restart Cursor (or reload MCP servers) and the tools above will become available to the agent.
Configuration
Required (see .env.example):
| Variable | Default | Purpose |
|---|---|---|
GOOGLE_CLIENT_ID |
(required) | OAuth client ID from Google Cloud Console |
GOOGLE_CLIENT_SECRET |
(required) | OAuth client secret from Google Cloud Console |
GOOGLE_REDIRECT_URI |
http://localhost:3000/oauth2callback |
Redirect URI used for the one-time npm run authorize flow; npm run authorize listens on whatever host/port/path this specifies |
GOOGLE_TOKEN_STORAGE_PATH |
./tokens.json |
Where the access/refresh token is stored (encrypted at rest); relative paths resolve from the project root |
MCP_SERVER_NAME |
google-workspace-mcp-server |
Name this server reports over MCP |
LOG_LEVEL |
info |
Structured (JSON) log verbosity written to stderr: trace|debug|info|warn|error|fatal |
Remote (HTTP) transport — only relevant when hosting this server remotely instead of running it locally via stdio (see deployment-plan.md):
| Variable | Default | Purpose |
|---|---|---|
MCP_TRANSPORT |
stdio |
"stdio" for local Cursor usage, or "http" to bind an HTTP server instead (for remote hosting) |
PORT |
8080 |
Port the HTTP transport binds to (0.0.0.0). Render (default 10000) or Railway injects this automatically — don't set it yourself there. |
MCP_AUTH_TOKEN |
(required if MCP_TRANSPORT=http) |
Shared secret remote clients must send as Authorization: Bearer <token> to /mcp, and ?token=<token> to /authorize. The server refuses to start in http mode without it. |
Optional:
| Variable | Default | Purpose |
|---|---|---|
GOOGLE_APPLICATION_CREDENTIALS |
(unset) | Path to a service-account key file. Not yet used — reserved for a future service-account auth mode; see the NOTE comment in src/auth.ts. |
ALLOWED_GOOGLE_ACCOUNTS |
(unset, i.e. any account) | Comma-separated email allowlist. Not yet enforced — see the NOTE comment in src/auth.ts for what's needed to wire it up (requires the openid/userinfo.email scopes). |
ENABLE_HTML_EMAIL |
true |
If false, gmail_create_draft/gmail_send_email reject isHtml: true with an INVALID_INPUT error |
ENABLE_DOCS_APPEND |
true |
If false, the docs_append_text tool is not registered at all |
Additional hardening (kept from the previous .env layout — see .env.example):
| Variable | Default | Purpose |
|---|---|---|
RATE_LIMIT_PER_MINUTE |
60 |
Max calls allowed per tool per rolling 60s window before returning a RATE_LIMITED error |
MAX_CONFLICT_RETRIES |
3 |
Retry attempts for docs_append_text when a concurrent edit is detected |
MAX_ATTACHMENT_BYTES |
20971520 (20MB) |
Max size per email attachment |
TOKEN_ENCRYPTION_KEY |
(auto-generated) | Base64, 32-byte key used to encrypt the token storage file at rest; if unset, a random key is generated once into .token.key |
Notes & safety
gmail_send_emailsends immediately with no human review — if you want a safety net, have the agent usegmail_create_draftand then a human sends it from Gmail (or callgmail_send_draftexplicitly).- The Gmail scope used is
gmail.compose, which allows creating/sending drafts and sending messages, but does not grant read access to the mailbox. - The Docs scope used is
documents, which allows creating and editing docs but does not touch other Drive files. - Access tokens refresh automatically and are persisted back to the token storage file, which is encrypted at rest (AES-256-GCM). Existing plaintext token files are migrated transparently on first read.
Reliability & observability (Phase 5 hardening)
- Structured errors: every tool failure is normalized to
Error [CODE]: message, whereCODEis one ofINVALID_INPUT,REAUTH_REQUIRED,RATE_LIMITED,UPSTREAM_ERROR,CONFLICT, orINTERNAL_ERROR, plus aretryablehint and arequestIdfor correlating with logs. - Rate limiting: each tool is capped at
RATE_LIMIT_PER_MINUTEcalls/minute (in-process, sliding window) to protect your Google API quota from a runaway agent loop. - Conflict-safe Docs writes:
docs_append_textreads the document's current revision and writes withwriteControl.requiredRevisionId; if another edit races it, the request is retried with exponential backoff (MAX_CONFLICT_RETRIESattempts) instead of silently overwriting. - Structured logs: JSON logs go to stderr (never stdout, which is reserved for the MCP protocol channel) with a
requestIdper tool call. Only non-sensitive metadata is logged (e.g. recipient counts, string lengths) — email bodies, document text, attachment bytes, and tokens are never written to logs. - Metrics: call the
server_metricstool at any time to see call counts, error rates, and average latency per tool for the running process.
Remote (HTTP) hosting
Setting MCP_TRANSPORT=http switches the entrypoint to bind an HTTP server (instead of stdio) exposing:
GET /health— unauthenticated; returns200 { status: "ok" }for platform health checks.POST/GET/DELETE /mcp— the actual MCP endpoint (Streamable HTTP transport), gated byAuthorization: Bearer <MCP_AUTH_TOKEN>.GET /authorize— starts the Google consent flow remotely (gated by?token=<MCP_AUTH_TOKEN>since it's opened in a browser). Needed because a headless remote server has no browser fornpm run authorizeto open.GET <path from GOOGLE_REDIRECT_URI>— Google's OAuth callback; exchanges the code and persists the token, no restart required.
This mode is stateless: each /mcp request gets its own short-lived MCP server + transport pair (per the SDK's guidance for stateless HTTP hosting), so a single process can serve concurrent requests without cross-talk. The in-memory rate limiter and metrics are still per-process, so run a single instance (see deployment-plan.md, Phase 11).
Full step-by-step Render deployment instructions (persistent Disk for token storage, environment variables, redirect URI updates, CI, etc.) are in deployment-plan.md. render.yaml at the repo root declares the service, disk, and env vars for a one-click Render Blueprint deploy (a railway.json is also kept in the repo if you'd rather deploy to Railway instead).
Project structure
src/
config.ts - env-driven settings: OAuth client, token path, server name, scopes, transport, feature toggles, hardening limits
errors.ts - AppError type + Google API error -> standardized error code mapping
logging.ts - structured pino logger (stderr-only) + per-request correlation IDs
metrics.ts - in-memory per-tool call/error/latency counters
rateLimiter.ts - per-tool sliding-window rate limiting
tokenCrypto.ts - AES-256-GCM encryption at rest for token.json
auth.ts - OAuth2 client creation + encrypted token load/save (stdio: eager/fail-fast; HTTP: lazy/non-throwing)
authorize.ts - one-time interactive `npm run authorize` script (local/stdio setup)
gmail.ts - GmailClient: createDraft, sendEmail (with attachments), sendDraft, listDrafts
docs.ts - DocsClient: createDocument, appendText (conflict-safe with retries)
mcpServer.ts - createMcpServer(): tool registry + shared logging/rate-limit/error middleware (used by both transports)
httpServer.ts - Streamable HTTP transport: /health, /mcp (bearer-token gated), /authorize, OAuth callback
index.ts - entrypoint; picks stdio or HTTP transport based on MCP_TRANSPORT
render.yaml - Render Blueprint (web service, persistent disk, env vars) — see deployment-plan.md
railway.json - equivalent config for Railway, kept for reference
推荐服务器
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 模型以安全和受控的方式获取实时的网络信息。