mail-cal-drive-mcp
Allows sending, reading, searching, and managing emails in Gmail accounts, including folder management and bulk actions.
Enables listing, creating, updating, deleting calendar events, finding free time, and checking scheduling conflicts across Google Calendar accounts.
Provides file management capabilities including listing, uploading, downloading, searching, sharing, and managing permissions on Google Drive.
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., "@mail-cal-drive-mcpshow me my next calendar event"
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.
mail-cal-drive-mcp
A self-hosted Model Context Protocol (MCP) server for multi-account email, calendar, and cloud storage. Connect Microsoft 365, Google Workspace, and IMAP accounts to any MCP client — Claude Desktop, Claude Code, VS Code, Cursor, and more.
Runs locally in Docker with Postgres. Credentials encrypted at rest. Survives reboots without re-authorization.
Provider | Calendar | Drive | |
Microsoft 365 | ✅ | ✅ | ✅ OneDrive |
Google Workspace | ✅ | ✅ | ✅ Google Drive |
IMAP | ✅ | ❌ | ❌ |
Prerequisites
Docker and Docker Compose
Node.js 20+ (for local development only)
Related MCP server: productivity-mcp
Quick Start
# 1. Clone
git clone https://github.com/rumbitopi/mail-cal-drive-mcp.git
cd mail-cal-drive-mcp
# 2. Configure
cp .env.example .env
# 3. Generate keys (run 3 times — one for each)
node -e "console.log(require('crypto').randomBytes(32).toString('hex'))"
# Paste into .env as POSTGRES_PASSWORD, CREDENTIAL_ENCRYPTION_KEY, and API_KEY
# Update DATABASE_URL to use the same POSTGRES_PASSWORD
# 4. Start (server runs with zero providers — configure them later)
docker compose up --build -d
# 5. Verify health
curl http://localhost:3100/health
# 6. Verify MCP
curl -X POST http://localhost:3100/mcp \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-H "Accept: application/json, text/event-stream" \
-d '{"jsonrpc":"2.0","method":"initialize","params":{"protocolVersion":"2024-11-05","capabilities":{},"clientInfo":{"name":"test","version":"1.0.0"}},"id":1}'Architecture
┌──────────────────────────────────────────────┐
│ Docker Compose │
│ ┌─────────────┐ ┌──────────────┐ │
│ │ workspace │◄──►│ postgres:5432│ │
│ │ -mcp :3100 │ │ (persistent) │ │
│ │ Streamable │ └──────────────┘ │
│ │ HTTP + Auth │ │
│ └─────────────┘ │
└──────────────────────────────────────────────┘Protocol: MCP over Streamable HTTP (2024-11-05), MCP SDK 1.25.3
What persists across reboots (in Postgres):
Credentials (AES-256-GCM encrypted)
MSAL token cache (silent Microsoft refresh — no re-auth)
Pending auth flows (survive restarts mid-auth)
What's ephemeral (in memory):
MCP sessions (clients auto-reconnect)
Client Configuration
Claude Desktop
Claude Desktop speaks stdio, not HTTP. supergateway bridges the two.
Add to ~/Library/Application Support/Claude/claude_desktop_config.json (macOS) or %APPDATA%\Claude\claude_desktop_config.json (Windows):
{
"mcpServers": {
"workspace": {
"command": "npx",
"args": [
"-y", "supergateway",
"--streamableHttp", "http://localhost:3100/mcp",
"--header", "Authorization: Bearer YOUR_API_KEY"
]
}
}
}Claude Code
Add to .claude/settings.local.json in your project:
{
"mcpServers": {
"workspace": {
"type": "streamable-http",
"url": "http://localhost:3100/mcp",
"headers": {
"Authorization": "Bearer YOUR_API_KEY"
}
}
}
}VS Code (GitHub Copilot)
Add to .vscode/settings.json:
{
"github.copilot.chat.mcpServers": {
"workspace": {
"type": "streamable-http",
"url": "http://localhost:3100/mcp",
"headers": {
"Authorization": "Bearer YOUR_API_KEY"
}
}
}
}Cursor
Add to .cursor/mcp.json:
{
"mcpServers": {
"workspace": {
"url": "http://localhost:3100/mcp",
"headers": {
"Authorization": "Bearer YOUR_API_KEY"
}
}
}
}Provider Setup
Microsoft 365
Uses device code flow — no redirect URI needed.
Azure App Registration
Go to Azure Portal → Azure Active Directory → App registrations → New registration
Name:
Workspace MCP(or anything)Supported account types: "Accounts in any organizational directory and personal Microsoft accounts"
Redirect URI: Leave blank
Click Register
Enable Public Client Flow
Go to your app → Authentication
Under Advanced settings, set "Allow public client flows" to Yes
Click Save
Add API Permissions
Go to API permissions → Add a permission → Microsoft Graph → Delegated permissions
Add:
Mail.ReadWrite,Mail.SendCalendars.ReadWrite,Calendars.Read.SharedFiles.ReadWrite,Files.ReadWrite.AllUser.Read,offline_access
Click "Grant admin consent" if you're an admin
Configure .env
MS_ENABLED=true
MS_CLIENT_ID=<Application (client) ID from Overview page>
MS_TENANT_ID=<Directory (tenant) ID, or "common" for multi-tenant>Connect
Call auth_start with provider: "microsoft". You'll get a URL and code. Visit the URL, enter the code, sign in. Then call auth_complete. One-time setup — refresh tokens persist across reboots.
Google Workspace
Uses OAuth callback flow — requires a redirect URI.
Google Cloud Setup
Go to Google Cloud Console → create or select a project
Enable APIs: Gmail API, Google Calendar API, Google Drive API
OAuth consent screen:
User Type: External (or Internal for Workspace)
Add scopes:
gmail.modify,gmail.labels,calendar,calendar.events,drive,drive.fileAdd yourself as a test user
Credentials → Create OAuth client ID:
Type: Web application
Redirect URI:
http://localhost:3100/auth/google/callback
Configure .env
GOOGLE_ENABLED=true
GOOGLE_CLIENT_ID=<Client ID>
GOOGLE_CLIENT_SECRET=<Client Secret>
GOOGLE_REDIRECT_URI=http://localhost:3100/auth/google/callbackConnect
Call auth_start with provider: "google". Open the URL, sign in, grant permissions. The callback saves your tokens. Then call auth_complete. One-time setup.
IMAP
No external setup needed — provide credentials directly.
Connect
Call auth_start with:
provider: "imap"
accountName: "Personal Email"
email: "user@example.com"
imapHost: "imap.example.com"
imapPort: 993
imapUsername: "user@example.com"
imapPassword: "your-app-password"
imapTls: trueNo auth_complete needed — IMAP accounts are ready immediately.
Common IMAP Servers
Provider | Host | Port | Notes |
Gmail |
| 993 | Requires App Password |
Outlook/Hotmail |
| 993 | Requires App Password |
Yahoo |
| 993 | Requires App Password |
Fastmail |
| 993 | Requires App Password |
MCP Tools (36 total)
Authentication (4)
Tool | Description |
| List all accounts with connection status |
| Start OAuth/device code flow or IMAP setup |
| Complete pending authentication |
| Remove an account |
Email (10)
Tool | Description |
| List all configured email accounts |
| Get folders/labels for an account |
| Messages with pagination |
| Full message with body and attachments |
| Download attachment (text decoded, binary as blob) |
| Search with filters (see parameters below) |
| Move to folder |
| Trash or permanent delete |
| Mark read/unread |
| Batch operations with dry-run support |
Calendar (8)
Tool | Description |
| All calendars for an account |
| Events in a date range |
| Full event details |
| Create with attendees, recurrence, conferencing |
| Modify existing event |
| Delete event |
| Find available time slots across accounts |
| Detect scheduling conflicts |
Drive (14)
Tool | Description |
| List files and folders |
| File metadata |
| Download file content |
| Search by name, content, or type |
| Upload new file (text or base64) |
| Create folder |
| Move file/folder |
| Copy file |
| Rename file/folder |
| Delete (trash or permanent) |
| View sharing permissions |
| Share with user or create link |
| Remove sharing |
| Storage usage info |
Tool Parameter Reference
get_attachment
Parameter | Type | Required | Description |
| string | Yes | Account ID |
| string | Yes | Message ID |
| string | Yes | Attachment ID from |
Returns text content for text-based files, or an MCP EmbeddedResource with blob for binary files (PDF, images, etc.). Max 5MB.
search_messages
Parameter | Type | Required | Description |
| string[] | Yes | Account IDs to search |
| string | No | Filter by sender email |
| string | No | Filter by recipient email |
| string | No | Filter by subject (partial match) |
| string | No | Search in message body |
| string | No | Limit to specific folder |
| boolean | No | Filter by attachment presence |
| boolean | No | Filter by read status |
| boolean | No | Filter by starred status |
| string | No | Messages after date (ISO 8601) |
| string | No | Messages before date (ISO 8601) |
| string[] | No | Filter by labels (Gmail) |
| number | No | Max results per account (default: 50) |
create_event
Parameter | Type | Required | Description |
| string | Yes | Account ID |
| string | Yes | Event title |
| string | Yes | Start time (ISO 8601) |
| string | Yes | End time (ISO 8601) |
| string | No | Calendar ID (default: primary) |
| string | No | Event description |
| string | No | Event location |
| boolean | No | All-day event |
| string | No | Time zone (default: UTC) |
| array | No |
|
| string | No |
|
| boolean | No | Add video conference link |
bulk_mail_action
Parameter | Type | Required | Description |
| string | Yes | Account ID |
| string | Yes |
|
| boolean | No | Preview without executing (default: false) |
| string | No | Destination folder (required for |
| string | No | Filter by sender |
| string | No | Filter by subject |
| string | No | Limit to folder |
| boolean | No | Filter by read status |
| string | No | After date (ISO 8601) |
| string | No | Before date (ISO 8601) |
| number | No | Max messages to process |
find_free_time
Parameter | Type | Required | Description |
| string[] | Yes | Account IDs to check |
| string | Yes | Start of range (ISO 8601) |
| string | Yes | End of range (ISO 8601) |
| number | Yes | Required duration in minutes |
| string[] | No | Calendars to check (default: primary) |
upload_file
Parameter | Type | Required | Description |
| string | Yes | Account ID |
| string | Yes | File name |
| string | Yes | File content (text or base64) |
| string | No | Parent folder ID (default: root) |
| string | No | MIME type |
| boolean | No | Content is base64 encoded |
share_file
Parameter | Type | Required | Description |
| string | Yes | Account ID |
| string | Yes | File ID |
| string | Yes |
|
| string | Yes |
|
| string | No | Email (required for user/group) |
| boolean | No | Send email notification |
| string | No | Notification message |
Environment Variables
Variable | Required | Default | Description |
| Yes | — | Postgres connection string |
| Yes | — | Postgres password (used by Docker Compose) |
| Yes | — | 64 hex chars (32 bytes) for AES-256-GCM |
| Yes | — | Min 32 chars, used as Bearer token |
| No |
|
|
| No |
| Server port |
| No |
|
|
| No |
| Enable Microsoft 365 |
| If MS | — | Azure App client ID |
| If MS |
| Azure tenant ID |
| No |
| Enable Google Workspace |
| If Google | — | OAuth client ID |
| If Google | — | OAuth client secret |
| If Google | — | OAuth callback URL |
| No |
| Enable IMAP provider |
Endpoints
Endpoint | Method | Auth | Description |
| GET | No | Health check |
| POST | Bearer | MCP JSON-RPC endpoint |
| GET | Bearer | SSE stream for notifications |
| DELETE | Bearer | Session cleanup |
| GET | No | Google OAuth redirect handler |
Security
This server is designed for localhost use only. Do not expose it to the internet without additional hardening.
Encryption at rest: All credentials, MSAL token cache, and pending auth flows encrypted with AES-256-GCM before storage in Postgres
Bearer auth: Timing-safe token comparison (
crypto.timingSafeEqual) on all protected endpointsSession limits: Max 100 concurrent sessions, 30-minute TTL with automatic eviction
Network: Binds to
127.0.0.1only — not accessible from other machinesBody size: 10MB request limit to prevent memory exhaustion
IMAP note: IMAP credentials are passwords (not OAuth tokens) — encrypted at rest, but inherently less secure than OAuth. Use app passwords where possible.
Logging: All logs to stderr (MCP requirement). No tokens, passwords, or keys logged.
Token rotation: To rotate the encryption key, generate a new one, re-run
auth_startfor each account, and update.env. There is no in-place re-encryption (accounts must be re-authorized).
See SECURITY.md for vulnerability reporting.
Troubleshooting
Container won't start
Check
docker compose logs workspace-mcpfor errorsVerify
DATABASE_URLmatches the Postgres container credentialsEnsure Postgres is healthy:
docker compose logs postgres
Microsoft device code expired
Device codes expire after 15 minutes. Call
auth_startagain for a fresh code.
Google OAuth: "Access blocked" or consent screen issues
Make sure your Google Cloud app is in "Testing" mode with your email added as a test user
Verify the redirect URI in Google Cloud Console matches
GOOGLE_REDIRECT_URIexactly
IMAP connection refused
Verify host, port, and TLS settings for your provider
Most providers require an App Password when 2FA is enabled (not your regular password)
Check if your provider blocks third-party IMAP access (Gmail: enable "Less secure apps" or use App Password)
"Invalid or expired session" on tool calls
MCP sessions expire after 30 minutes of inactivity and are lost on container restart. Your client should auto-reconnect. If not, restart the client.
Development
# Install dependencies
npm install
# Start with hot reload (requires local Postgres on DATABASE_URL)
npm run dev
# Build TypeScript
npm run build
# Production start
npm start
# Run tests
npm test
# Type check
npm run typecheck
# Migrate credentials from old file-based storage
DATABASE_URL=... CREDENTIAL_ENCRYPTION_KEY=... npm run migrateProject Structure
mail-cal-drive-mcp/
├── src/
│ ├── index.ts # Entry point
│ ├── server.ts # Express server
│ ├── config.ts # Environment config
│ ├── logger.ts # Structured console logger
│ ├── auth/
│ │ ├── types.ts # Credential interfaces
│ │ ├── storage.ts # Postgres credential storage
│ │ └── bearer.ts # Bearer token middleware
│ ├── storage/
│ │ ├── postgres.ts # Postgres client + encrypted CRUD
│ │ ├── msal-cache.ts # MSAL cache plugin (Postgres)
│ │ └── pending-flows.ts # Pending auth flow storage
│ ├── providers/
│ │ ├── base.ts # Abstract interfaces
│ │ ├── types.ts # Shared types
│ │ ├── microsoft/ # Graph API (mail, calendar, drive)
│ │ ├── google/ # Google APIs (Gmail, Calendar, Drive)
│ │ └── imap/ # ImapFlow (mail only)
│ ├── mcp/
│ │ ├── handler.ts # MCP request routing
│ │ ├── session.ts # Session management
│ │ └── tools/ # 35 MCP tools
│ └── utils/ # Timezone, recurrence, MIME helpers
├── db/migrations/ # Postgres schema
├── docker-compose.yml
├── Dockerfile
└── .env # Configuration (gitignored)License
This 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
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/rumbitopi/mail-cal-drive-mcp'
If you have feedback or need assistance with the MCP directory API, please join our Discord server