MCP Google Workspace Server
MCP Google Workspace Server
A Model Context Protocol (MCP) server that exposes Gmail and Google Docs capabilities as tools any MCP-compliant agent (Claude Desktop, Cursor, custom agents, etc.) can call.
It provides three tools:
Tool | Description |
| Send an email through Gmail on behalf of the authenticated account. |
| Create a Gmail draft without sending it. |
| Append text to the end of an existing Google Doc (never overwrites). |
Both stdio and streamable HTTP transports are supported.
Architecture
The codebase is modular so each layer is independently testable:
MCP layer —
src/server.ts,src/tools/*,src/transports/*(tool registration, validation, structured errors, transports).Google service layer —
src/google/gmailService.ts,src/google/docsService.ts,src/lib/mime.ts.Auth / config —
src/auth/*,src/config.ts.
send_gmail / draft_gmail / append_to_google_doc
│ (zod-validated input, JSON-Schema advertised)
▼
MCP server (stdio | streamable HTTP)
▼
Gmail / Docs services ──► Google APIs
▲
OAuth2 client (auto-refresh)Prerequisites
Node.js 18+ and npm.
A Google account (Gmail and/or Google Workspace).
A Google Cloud project.
1. Google Cloud Console setup
Go to the Google Cloud Console and create (or select) a project.
Enable APIs: APIs & Services → Library → enable Gmail API and Google Docs API.
OAuth consent screen: configure it (External is fine for testing). Add your Google account under Test users.
Add the required scopes (least privilege):
https://www.googleapis.com/auth/gmail.sendhttps://www.googleapis.com/auth/gmail.composehttps://www.googleapis.com/auth/documents
Create credentials: APIs & Services → Credentials → Create Credentials → OAuth client ID → Desktop app.
Add an authorized redirect URI that matches
GOOGLE_OAUTH_REDIRECT(defaulthttp://localhost:3000/oauth2callback).Copy the Client ID and Client secret.
2. Install & configure
npm install
cp .env.example .env # then edit .envFill in at least GOOGLE_CLIENT_ID and GOOGLE_CLIENT_SECRET. See .env.example for every variable.
Variable | Required | Purpose |
| yes | OAuth client ID |
| yes | OAuth client secret |
| no (default | Where tokens are stored (git-ignored) |
| no (default | Consent-flow redirect URI |
| no | Default From address |
| no (default |
|
| no | HTTP bind address (defaults |
| required for | Bearer token clients must present |
| no |
|
3. Authorize (one time)
npm run authThis opens the Google consent screen, captures the redirect, and stores a refresh token at GOOGLE_TOKEN_PATH. Tokens are refreshed transparently afterward. Re-run it if you change scopes or revoke access.
4. Build & run
npm run build # compile TypeScript to dist/
npm start # run the compiled server (uses MCP_TRANSPORT)
# or during development:
npm run devstdio transport (local/desktop agents)
Set MCP_TRANSPORT=stdio (default). The server communicates over stdin/stdout; all logs go to stderr.
streamable HTTP transport (remote agents)
MCP_TRANSPORT=http MCP_HTTP_AUTH_TOKEN=$(openssl rand -hex 32) npm startThe endpoint is http://<host>:<port>/mcp. Every request must include Authorization: Bearer <MCP_HTTP_AUTH_TOKEN>.
5. Connect an MCP client
See mcp-client-config.example.json. Example for a stdio client:
{
"mcpServers": {
"google-workspace": {
"command": "node",
"args": ["/absolute/path/to/dist/index.js"],
"env": {
"GOOGLE_CLIENT_ID": "your-client-id.apps.googleusercontent.com",
"GOOGLE_CLIENT_SECRET": "your-client-secret",
"GOOGLE_TOKEN_PATH": "/absolute/path/to/token.json",
"MCP_TRANSPORT": "stdio"
}
}
}
}You can also inspect the server with the MCP Inspector:
npx @modelcontextprotocol/inspector node dist/index.jsTool reference
send_gmail
Input: to: string[] (req), subject: string (req), body: string (req), body_type: "text"|"html" (default text), cc?: string[], bcc?: string[], reply_to?: string.
Output: { "message_id": string, "thread_id": string, "status": "sent" }
draft_gmail
Input: same as send_gmail.
Output: { "draft_id": string, "message_id": string, "status": "drafted" }
append_to_google_doc
Input: document_id: string (req; a raw ID or a full docs URL), content: string (req), add_newline_before: boolean (default true).
Output: { "document_id": string, "status": "appended" }
Authentication model & tradeoffs
This server uses OAuth2 user-delegated auth (Approach 1 in the problem statement):
Each user authorizes once via the standard consent flow; the server stores and auto-refreshes the refresh token.
Works for any Gmail/Workspace account with no admin setup — the best fit for an agent-agnostic server that may act for arbitrary users.
Alternative — service account with domain-wide delegation (not used here):
Suited to a single Workspace domain.
A plain service account cannot send Gmail as arbitrary users; it needs domain-wide delegation configured by a Workspace admin. For Docs, the target document must be shared with the service account's email.
To switch to a service account, replace getAuthorizedClient in src/auth/googleAuth.ts with a JWT client using domain-wide delegation (subject = impersonated user); the rest of the code is unchanged.
Error handling
Tools never crash the connection; they return structured MCP tool errors (isError: true with a machine-readable error.code). Codes include: INVALID_INPUT, DOCUMENT_NOT_FOUND, INSUFFICIENT_SCOPE, CREDENTIALS_MISSING, RATE_LIMITED, NETWORK_ERROR, GOOGLE_API_ERROR. Inputs are validated against the schema before any Google API call.
Security
Least-privilege scopes only.
Secrets loaded from env;
.envandtoken.jsonare git-ignored. Never hard-coded.The logger redacts recipients and never logs email bodies or document contents.
Email addresses and document IDs are validated/sanitized; MIME header injection is prevented.
The HTTP transport requires a bearer token and binds to
127.0.0.1by default.
Testing
npm testUnit tests mock the Google layer and cover each tool's success and error branches, MIME construction, and the Google→MCP error mapping.
Future extensions
Rich-text Docs formatting, email attachments/inline images, Gmail read/reply, and new tools (create Doc, insert at index, Sheets/Calendar) can be added as new modules under src/tools/ and src/google/ without changing existing tool contracts.
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/sunreddy1593-tech/MCP-1'
If you have feedback or need assistance with the MCP directory API, please join our Discord server