Getting Started
Get up and running with Gmail Secretary MCP in minutes.
What This Is
Secretary MCP is an AI-native Gmail client — not an IMAP library. It provides:
- Signals for intelligent reasoning (VIP, deadlines, questions)
- Staged mutations requiring user confirmation
- Time-boxed batches that never timeout
- Optional semantic search via pgvector
Prerequisites
- Docker and Docker Compose installed (Install Docker Desktop)
- Google Cloud Project with OAuth2 credentials (for IMAP/SMTP authentication and Calendar API)
- Claude Desktop or another MCP-compatible AI client
Quick Start
Step 1: Create Configuration
Create config.yaml:
# config.yaml - mount as read-only in Docker
bearer_auth:
enabled: true
token: "REPLACE-WITH-YOUR-UUID" # See Step 2
identity:
email: your-email@gmail.com
full_name: "Your Full Name"
aliases: [] # Empty list if no aliases
imap:
host: imap.gmail.com
port: 993
username: your-email@gmail.com
use_ssl: true
smtp:
host: smtp.gmail.com
port: 587
username: your-email@gmail.com
use_tls: true
timezone: Europe/Amsterdam # IANA format
working_hours:
start: "09:00"
end: "17:00"
workdays: [1, 2, 3, 4, 5] # Mon-Fri
vip_senders: [] # Add priority senders, or empty list
calendar:
enabled: true
# Database: sqlite (default) or postgres (for semantic search)
database:
backend: sqliteCritical Fields
aliases: []- Required even if emptyvip_senders: []- Required even if empty- OAuth tokens go in
token.json, NOT in config.yaml
Step 2: Generate Bearer Token
uuidgen# Install uuid-runtime if uuidgen not found
uuidgen
# Or use OpenSSL (always available)
openssl rand -hex 32[guid]::NewGuid().ToString()Add the generated token to your config.yaml under bearer_auth.token.
Step 3: Create Docker Compose
The container runs three processes via supervisord:
- Engine API (port 8001, internal) - IMAP sync, mutations, OAuth management
- MCP Server (port 8000, exposed) - Tool exposure for AI clients
- Web UI (port 8080, exposed) - Human web interface
# docker-compose.yml
services:
workspace-secretary:
image: ghcr.io/johnneerdael/gmail-secretary-map:latest
container_name: workspace-secretary
restart: always
ports:
- "8000:8000" # MCP server
- "8080:8080" # Web UI
volumes:
- ./config:/app/config
environment:
- LOG_LEVEL=INFO
- ENGINE_API_URL=http://127.0.0.1:8001Three-Process Architecture
The container internally runs three coordinated processes:
- Engine API: Maintains IMAP connection, handles all mutations
- MCP Server: Exposes tools to AI, proxies writes to Engine
- Web UI: Human interface, proxies writes to Engine
The Engine API runs on port 8001 (internal only, not exposed).
Step 4: Run OAuth Setup
Start the container first, then run auth setup inside it:
docker compose up -d
# Option 1: OAuth2 (recommended)
docker exec -it workspace-secretary uv run python -m workspace_secretary.auth_setup \
--client-id='YOUR_CLIENT_ID.apps.googleusercontent.com' \
--client-secret='YOUR_CLIENT_SECRET'
# Option 2: App Password (simpler, no Google Cloud project needed)
docker exec -it workspace-secretary uv run python -m workspace_secretary.app_passwordSimplified Setup (v4.2.2+)
- Token automatically saves to
/app/config/token.json - Config automatically at
/app/config/config.yaml - No
--configor--token-outputflags needed
Manual OAuth Flow:
- Open the printed authorization URL in your browser
- Login and approve access
- Copy the full redirect URL from your browser (even if page doesn't load)
- Paste when prompted
- Tokens saved automatically
Minimal credentials.json
If using --credentials-file instead of --client-id/--client-secret:
{
"installed": {
"client_id": "YOUR_CLIENT_ID.apps.googleusercontent.com",
"client_secret": "YOUR_CLIENT_SECRET"
}
}Step 5: Start and Verify
docker compose up -d
docker compose logs -f
# Test health endpoint
curl http://localhost:8000/healthGoogle Cloud Setup
Create OAuth2 Credentials
Go to Google Cloud Console
Create or select a project
Enable Google Calendar API:
- APIs & Services → Library → Search and enable it
- Note: Gmail uses IMAP/SMTP (no Gmail API needed)
Configure OAuth Consent Screen:
- APIs & Services → OAuth consent screen
- User type: External (or Internal for Workspace)
- Add your email as test user
- Add scopes:
https://mail.google.com/https://www.googleapis.com/auth/calendar
Create Credentials:
- APIs & Services → Credentials → Create Credentials → OAuth client ID
- Application type: Desktop app
- Download JSON as
credentials.json
The downloaded credentials.json looks like this:
{
"installed": {
"client_id": "123456789-abcdefg.apps.googleusercontent.com",
"project_id": "your-project-name",
"auth_uri": "https://accounts.google.com/o/oauth2/auth",
"token_uri": "https://oauth2.googleapis.com/token",
"auth_provider_x509_cert_url": "https://www.googleapis.com/oauth2/v1/certs",
"client_secret": "GOCSPX-your-client-secret",
"redirect_uris": ["http://localhost"]
}
}Only Two Fields Matter
The auth setup only uses client_id and client_secret from this file. You can alternatively pass them directly via --client-id and --client-secret flags.
Manual Flow Advantage
The manual OAuth flow works with any redirect URI, including http://localhost. Perfect for headless servers and containers. After auth setup completes, credentials.json is no longer needed - only token.json is used at runtime.
Production Deployment
With Traefik
services:
workspace-secretary:
image: ghcr.io/johnneerdael/gmail-secretary-map:latest
volumes:
- ./config:/app/config
labels:
- "traefik.enable=true"
- "traefik.http.routers.mcp.rule=Host(`mcp.yourdomain.com`)"
- "traefik.http.routers.mcp.tls.certresolver=letsencrypt"With Caddy
services:
caddy:
image: caddy:2-alpine
ports:
- "80:80"
- "443:443"
volumes:
- ./Caddyfile:/etc/caddy/Caddyfile:ro
- caddy_data:/data
workspace-secretary:
image: ghcr.io/johnneerdael/gmail-secretary-map:latest
volumes:
- ./config:/app/config
volumes:
caddy_data:Caddyfile:
mcp.yourdomain.com {
reverse_proxy workspace-secretary:8000
}Caddy Let's Encrypt Caveats
Automatic HTTPS requires:
- Ports 80/443 reachable from internet
- DNS A/AAAA record pointing to your server
- No CDN/proxy interference
Common failures:
- ISP blocks port 80
- Behind NAT without port forwarding
- IPv6 AAAA exists but routing broken
For wildcards or complex setups: Use DNS challenge
With PostgreSQL (Semantic Search)
For AI-powered search by meaning:
cat > .env << 'EOF'
POSTGRES_PASSWORD=your-secure-password
GEMINI_API_KEY=your-gemini-api-key
EOF
docker compose -f docker-compose.postgres.yml up -dUpdate config.yaml:
database:
backend: postgres
postgres:
host: postgres
port: 5432
database: secretary
user: secretary
password: ${POSTGRES_PASSWORD}
embeddings:
enabled: true
provider: gemini
gemini_api_key: ${GEMINI_API_KEY}
gemini_model: text-embedding-004
dimensions: 3072
batch_size: 100
task_type: RETRIEVAL_DOCUMENTSee Semantic Search Guide and Embeddings Guide for details.
Connecting AI Clients
MCP Server endpoint: http://localhost:8000/mcp
Web UI: http://localhost:8080
Claude Desktop
macOS: ~/Library/Application Support/Claude/claude_desktop_config.json
{
"mcpServers": {
"workspace-secretary": {
"url": "http://localhost:8000/mcp",
"headers": {
"Authorization": "Bearer YOUR_TOKEN"
}
}
}
}Claude Code (CLI)
claude mcp add --transport http workspace-secretary http://localhost:8000/mcp \
--header "Authorization: Bearer YOUR_TOKEN"Troubleshooting
OAuth "App Not Verified"
Click Advanced → Go to [App Name] (unsafe). Normal for development apps.
Token Refresh Fails
Re-run auth setup:
uv run python -m workspace_secretary.auth_setup \
--credentials-file credentials.json \
--config config.yaml \
--token-output token.jsonContainer Won't Start
docker compose logs workspace-secretary
# Common issues:
# - config.yaml not found: check volume mount paths
# - Invalid timezone: use IANA format (Europe/Amsterdam, not CET)
# - Missing aliases: [] or vip_senders: []Permission Denied
- Ensure
token.jsonexists:touch token.json - Verify APIs enabled in Google Cloud Console
- Check OAuth scopes include Gmail and Calendar
Next Steps
- Configuration Guide - All settings explained
- MCP Tools Reference - Available tools
- Semantic Search - AI-powered email search
- Agent Patterns - Building intelligent workflows
Need help? Open an issue on GitHub