Gmail + Google Docs MCP Server
Allows drafting and sending emails via Gmail, including attachments, and listing drafts.
Allows creating new Google Docs and appending text to existing documents, with conflict-safe concurrent editing.
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 + Google Docs MCP ServerDraft an email to Alex about the Q2 results."
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 + 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 |
| Creates a draft email (not sent) |
| Sends an email immediately |
| Sends a previously created draft by ID |
| Lists existing drafts |
| Creates a new Google Doc, optionally with initial text |
| Appends text to the end of an existing Google Doc (safe under concurrent edits — see below) |
| 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 }.
Related MCP server: Gmail MCP Server
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).
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 build3. Authorize (one-time)
npm run authorizeThis 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 startBy 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 Railway), 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 |
| (required) | OAuth client ID from Google Cloud Console |
| (required) | OAuth client secret from Google Cloud Console |
|
| Redirect URI used for the one-time |
|
| Where the access/refresh token is stored (encrypted at rest); relative paths resolve from the project root |
|
| Name this server reports over MCP |
|
| Structured (JSON) log verbosity written to stderr: |
Remote (HTTP) transport — only relevant when hosting this server remotely instead of running it locally via stdio (see deployment-plan.md):
Variable | Default | Purpose |
|
|
|
|
| Port the HTTP transport binds to ( |
| (required if | Shared secret remote clients must send as |
Optional:
Variable | Default | Purpose |
| (unset) | Path to a service-account key file. Not yet used — reserved for a future service-account auth mode; see the |
| (unset, i.e. any account) | Comma-separated email allowlist. Not yet enforced — see the |
|
| If |
|
| If |
Additional hardening (kept from the previous .env layout — see .env.example):
Variable | Default | Purpose |
|
| Max calls allowed per tool per rolling 60s window before returning a |
|
| Retry attempts for |
|
| Max size per email attachment |
| (auto-generated) | Base64, 32-byte key used to encrypt the token storage file at rest; if unset, a random key is generated once into |
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 Railway deployment instructions (Volumes for token persistence, environment variables, redirect URI updates, CI, etc.) are in deployment-plan.md.
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_TRANSPORTThis server cannot be installed
Maintenance
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
- Who's Calling? MCP Hosts Are an Identity Blind Spot (And the Spec Knows It)By Om-Shree-0709 on .mcpAgent IdentityOAuth 2.1
- 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/rohinigowdaiah1111/MCP-server'
If you have feedback or need assistance with the MCP directory API, please join our Discord server