Skip to main content
Glama
nikhil1102-ai

MCP-Style Server — Google Docs & Gmail Integration

MCP-Style Server — Google Docs, Drive & Gmail Integration

A lightweight Python server that exposes Google Docs, Drive and Gmail operations as MCP-style tool endpoints via FastAPI. Every action requires explicit human approval in the terminal before execution.


Features

  • Append to Google Doc — Append text content to any Google Doc you have access to.

  • Upload to Google Drive — Upload a file (e.g. the weekly pulse PDF) and get a shareable link.

  • Create Gmail Draft — Create a draft email in your Gmail account.

  • Human-in-the-Loop — Every action is printed to the terminal and requires y/n approval.

  • OAuth 2.0 — Secure authentication via Google's OAuth 2.0 flow.


Related MCP server: Google MCP Server

Prerequisites

  • Python 3.9+

  • Google Cloud Project with the following APIs enabled:

    • Google Docs API

    • Gmail API

  • OAuth 2.0 Client Credentials (credentials.json) downloaded from the Google Cloud Console.


Setup

1. Clone the repository

git clone <repo-url>
cd MCP_server
python -m venv venv
# Windows
venv\Scripts\activate
# macOS/Linux
source venv/bin/activate

3. Install dependencies

pip install -r requirements.txt

4. Add your credentials

Place your Google OAuth client secrets file in the project root. The server is pre-configured to look for the credentials file by its downloaded name. If you renamed it, update the CREDENTIALS_FILE path in auth.py.

5. Run the server

python server.py

On the first run, a browser window will open asking you to sign in and grant permissions. After approval, a token.json file is created and subsequent runs will not require browser login.


Usage

Append to a Google Doc

curl -X POST http://127.0.0.1:8000/append_to_doc \
  -H "Content-Type: application/json" \
  -d '{"doc_id": "YOUR_DOC_ID", "content": "Hello from MCP server!"}'

Check the terminal running the server — you'll see:

============================================================
  ACTION: append_to_doc
============================================================
{
  "doc_id": "YOUR_DOC_ID",
  "content": "Hello from MCP server!"
}
============================================================
Approve? (y/n):

Type y to approve and execute, or n to reject.

Create a Gmail Draft

curl -X POST http://127.0.0.1:8000/create_email_draft \
  -H "Content-Type: application/json" \
  -d '{"to": "recipient@example.com", "subject": "Test", "body": "Hello!"}'

API Reference

POST /append_to_doc

Field

Type

Description

doc_id

string

Google Doc ID (from the URL)

content

string

Text content to append

Response (200):

{ "status": "success", "message": "Content appended to document.", "doc_id": "..." }

POST /upload_to_drive

Field

Type

Description

filename

string

Destination filename, e.g. pulse_2026-09-07.pdf

content_b64

string

File contents, base64-encoded

mime_type

string

Optional. Defaults to application/pdf

folder_id

string

Optional. Overrides the DRIVE_FOLDER_ID env var

Response (200):

{
  "status": "success",
  "message": "File uploaded to Drive.",
  "file_id": "...",
  "file_url": "https://drive.google.com/file/d/.../view",
  "shared": true,
  "folder_id": "..."
}

Set DRIVE_FOLDER_ID to keep every upload in one folder instead of the Drive root. The ID is the last path segment of the folder's URL:

https://drive.google.com/drive/folders/1V4-pvlYj-pQPLC3rgSsnAcEH9jirQhy6
                                       ^--------- this is DRIVE_FOLDER_ID

A folder created by hand in the Drive UI works fine with the drive.file scope, as long as the authenticated account can access it.

Uploaded files are given "anyone with the link can view" access so that email recipients can open them without requesting permission. Set LINK_SHARING_ENABLED = False in drive_tool.py to keep uploads private.

The base64 body is summarised (not dumped) in the terminal approval prompt.

POST /create_email_draft

Field

Type

Description

to

string

Recipient email address

subject

string

Email subject line

body

string

Email body text (plain-text)

body_html

string

Optional rich HTML body

Response (200):

{ "status": "success", "message": "Draft created successfully.", "draft_id": "..." }

Error Responses

Status

Meaning

400

Malformed payload (e.g. invalid base64)

403

Action rejected by operator

401

Authentication error

422

Invalid request payload

502

Google API error

500

Internal server error


Project Structure

MCP_server/
├── server.py          → FastAPI app with tool endpoints
├── auth.py            → Google OAuth 2.0 authentication
├── docs_tool.py       → Google Docs tool (append content)
├── drive_tool.py      → Google Drive tool (upload file, share link)
├── gmail_tool.py      → Gmail tool (create draft)
├── requirements.txt   → Python dependencies
├── Procfile           → Railway deployment config
├── README.md          → This file
├── .gitignore         → Git ignore rules
└── docs/
    ├── problemStatement.md
    └── architecture.md

Deploy to Railway

Prerequisites

  • A Railway account (free tier available)

  • The project pushed to GitHub (see Setup above)

  • Google OAuth token.json generated locally (run the server once locally first)

Step 1: Create a New Project on Railway

  1. Go to railway.app and log in.

  2. Click "New Project" → "Deploy from GitHub Repo".

  3. Select your repository: nikhil1102-ai/custom_mcp_server.

  4. Railway will auto-detect the Procfile and configure the start command.

Step 2: Configure Environment Variables

In your Railway project dashboard, go to Variables and add:

Variable

Value

Description

AUTO_APPROVE

true

Auto-approve actions (no terminal in cloud)

HOST

0.0.0.0

Bind to all interfaces (required by Railway)

GOOGLE_TOKEN_JSON

(contents of token.json)

OAuth token for Google APIs (see Step 3)

Note: Railway automatically sets the PORT variable — you do not need to add it manually.

Step 3: Set Up Google OAuth Token

Since Railway has no browser for the OAuth consent flow, you must generate token.json locally first:

  1. Run the server locally once:

    python server.py
  2. Make any API call (e.g., append to doc) — this triggers the OAuth browser flow.

  3. Complete the consent in your browser — token.json is created.

  4. Copy the token contents:

    # Windows (PowerShell)
    Get-Content token.json
    
    # macOS/Linux
    cat token.json
  5. Paste the entire JSON into the GOOGLE_TOKEN_JSON Railway variable.

Alternative: You can also upload token.json and credentials.json as Railway volume mounts if your plan supports it.

Step 4: Handle Credentials in Production

auth.py already handles this automatically — on startup it checks for two environment variables and writes the files to disk if they don't exist:

Variable

Value

GOOGLE_CREDENTIALS_JSON

Entire contents of credentials.json

GOOGLE_TOKEN_JSON

Entire contents of token.json

Set both in your Railway Variables tab. No manual code changes needed.

Step 5: Deploy

  1. Push your code to GitHub:

    git add .
    git commit -m "Add Railway deployment support"
    git push origin main
  2. Railway will auto-deploy from the main branch.

  3. Once deployed, Railway provides a public URL like:

    https://your-app-name.up.railway.app

Step 6: Test the Deployed API

# Replace with your Railway URL
curl -X POST https://your-app-name.up.railway.app/append_to_doc \
  -H "Content-Type: application/json" \
  -d '{"doc_id": "YOUR_DOC_ID", "content": "Hello from Railway!"}'

curl -X POST https://your-app-name.up.railway.app/create_email_draft \
  -H "Content-Type: application/json" \
  -d '{"to": "test@example.com", "subject": "Test", "body": "Deployed!"}'

Railway Environment Summary

Setting

Value

Start Command

uvicorn server:app --host 0.0.0.0 --port $PORT (from Procfile)

Build Command

pip install -r requirements.txt (auto-detected)

Port

Auto-assigned by Railway via $PORT

Health Check

GET /docs (FastAPI Swagger UI)


Security Notes

  • credentials.json and token.json are excluded from version control via .gitignore.

  • The server binds to 127.0.0.1 (localhost) by default — it is not exposed to the network.

  • On Railway, set AUTO_APPROVE=true since there is no terminal for manual approval.

  • Only documents, gmail.compose and drive.file OAuth scopes are requested. drive.file grants access solely to files this app creates — never the user's existing Drive contents.

  • Changing SCOPES invalidates an existing token.json. Delete it, re-run the consent flow locally, then update GOOGLE_TOKEN_JSON on Railway. A stale token fails with 403 insufficient authentication scopes.

  • For production, consider adding API key authentication to protect your endpoints.

Related MCP Connectors

Related MCP Servers