Skip to main content
Glama
sunreddy1593-tech

MCP Google Workspace Server

README.md
# MCP Google Workspace Server

A [Model Context Protocol](https://modelcontextprotocol.io) (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_gmail` | Send an email through Gmail on behalf of the authenticated account. |
| `draft_gmail` | Create a Gmail draft without sending it. |
| `append_to_google_doc` | 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

1. Go to the [Google Cloud Console](https://console.cloud.google.com/) and create (or select) a project.
2. **Enable APIs**: APIs & Services → Library → enable **Gmail API** and **Google Docs API**.
3. **OAuth consent screen**: configure it (External is fine for testing). Add your Google account under **Test users**.
4. Add the required **scopes** (least privilege):
   - `https://www.googleapis.com/auth/gmail.send`
   - `https://www.googleapis.com/auth/gmail.compose`
   - `https://www.googleapis.com/auth/documents`
5. **Create credentials**: APIs & Services → Credentials → **Create Credentials → OAuth client ID → Desktop app**.
6. Add an authorized redirect URI that matches `GOOGLE_OAUTH_REDIRECT` (default `http://localhost:3000/oauth2callback`).
7. Copy the **Client ID** and **Client secret**.

---

## 2. Install & configure

```bash
npm install
cp .env.example .env   # then edit .env
```

Fill in at least `GOOGLE_CLIENT_ID` and `GOOGLE_CLIENT_SECRET`. See [`.env.example`](./.env.example) for every variable.

| Variable | Required | Purpose |
|----------|----------|---------|
| `GOOGLE_CLIENT_ID` | yes | OAuth client ID |
| `GOOGLE_CLIENT_SECRET` | yes | OAuth client secret |
| `GOOGLE_TOKEN_PATH` | no (default `./token.json`) | Where tokens are stored (git-ignored) |
| `GOOGLE_OAUTH_REDIRECT` | no (default `http://localhost:3000/oauth2callback`) | Consent-flow redirect URI |
| `DEFAULT_SEND_AS` | no | Default From address |
| `MCP_TRANSPORT` | no (default `stdio`) | `stdio` or `http` |
| `MCP_HTTP_HOST` / `MCP_HTTP_PORT` | no | HTTP bind address (defaults `127.0.0.1:3333`) |
| `MCP_HTTP_AUTH_TOKEN` | required for `http` | Bearer token clients must present |
| `LOG_LEVEL` | no | `debug`/`info`/`warn`/`error` |

---

## 3. Authorize (one time)

```bash
npm run auth
```

This 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

```bash
npm run build      # compile TypeScript to dist/
npm start          # run the compiled server (uses MCP_TRANSPORT)
# or during development:
npm run dev
```

### stdio 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)

```bash
MCP_TRANSPORT=http MCP_HTTP_AUTH_TOKEN=$(openssl rand -hex 32) npm start
```

The 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`](./mcp-client-config.example.json). Example for a stdio client:

```json
{
  "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](https://github.com/modelcontextprotocol/inspector):

```bash
npx @modelcontextprotocol/inspector node dist/index.js
```

---

## Tool 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; `.env` and `token.json` are 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.1` by default.

---

## Testing

```bash
npm test
```

Unit 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.

TDQS

B3.4/5.0

Scored across 3 tools

Disambiguation5/5

Each tool has a unique purpose: appending to a Google Doc, drafting a Gmail, and sending a Gmail. Drafting and sending are distinct actions, so no overlap.

Naming Consistency5/5

All tool names follow a consistent verb_noun pattern in snake_case: append_to_google_doc, draft_gmail, send_gmail.

Tool Count2/5

With only 3 tools, the server is too sparse for a Google Workspace domain. It covers only two features (Docs append and Gmail draft/send) but lacks many essential operations.

Completeness2/5

The server covers only a tiny fraction of Google Workspace capabilities. Missing tools for creating/reading docs, managing labels, searching emails, etc. Significant gaps.

Maintenance

ActivityInactive
ResponsivenessNo issues