Mailhub
Mailhub is a local, MCP-first personal-operations hub for AI assistants and their humans.
Features
- Mail — Gmail, Microsoft Graph (Outlook.com), IMAP/SMTP
- Calendar — Google Calendar, Microsoft Graph, CalDAV
- Contacts — Google People, Microsoft Graph, CardDAV
- Tasks — Microsoft To Do (Graph), CalDAV VTODO
- MCP-first — stdio MCP server for AI agents (read-only + full modes)
- REST API — localhost-only HTTP server for scripts/CLI
- CLI —
mailhub(admin) andmail(user) commands
Quick Start
# Install
uv tool install --editable .
# Initialize config & state
mailhub init
# Configure OAuth clients (one-time)
mailhub config setup-google # Opens Google Cloud Console, guides OAuth setup
mailhub config setup-microsoft # Opens Entra ID, guides app registration
# Add accounts
mailhub config add-account --alias me --provider gmail --capabilities mail --email me@gmail.com
mailhub config add-account --alias work --provider graph --capabilities mail --email me@outlook.com
# Authorize accounts (opens browser, paste redirect URL)
mailhub auth gmail me --code '<pasted-redirect-url>'
mailhub auth graph work --code '<pasted-redirect-url>'
# Verify health
mailhub doctor
# Start REST server (localhost only)
mailhub serve
# Or run MCP server (read-only by default)
mailhub mcp --mode ro
# Or full mode for mutations
mailhub mcp --mode full
Installation Options
Permanent Install (Recommended for Daily Use)
# Install via uv (fast, isolated)
uv tool install zc-mailhub
# Or via pip
pip install zc-mailhub
# Or via pipx
pipx install zc-mailhub
Ephemeral / One-off Use (uvx)
# Run without installing (downloads + caches automatically)
uvx --from zc-mailhub mailhub init
uvx --from zc-mailhub mailhub mcp --mode full
uvx --from zc-mailhub mailhub doctor
# Use specific version
uvx --from zc-mailhub@0.1.2 mailhub mcp --mode full
# Force update to latest
uvx --from zc-mailhub@latest mailhub mcp --mode full
Docker
# Pull and run
docker pull ghcr.io/zeroclue/zc-mailhub:v0.1.2
docker run -p 8787:8787 ghcr.io/zeroclue/zc-mailhub:v0.1.2 serve
# Or use docker-compose (see docker-compose.yml)
docker compose up -d
Development Install
git clone https://github.com/ZeroClue/mailhub
cd mailhub
uv sync --extra dev
uv run mailhub init
Using the mail CLI
The mail command is a thin HTTP client to the REST API (requires mailhub serve running):
# List accounts
mail accounts
# List folders
mail folders me
# Search messages
mail search me "from:github.com newer_than:7d"
mail search work "hasAttachments:true"
# Get message
mail get me <message-id>
# Send (requires --confirm)
mail send me --to someone@example.com --subject "Hello" --body "Test" --confirm
# Create draft
mail draft me --to someone@example.com --subject "Draft" --body "Content"
Systemd Service (Auto-start)
# Install service files
mkdir -p ~/.config/systemd/user
cp deploy/*.service deploy/*.timer ~/.config/systemd/user/
# Enable and start
systemctl --user daemon-reload
systemctl --user enable --now mailhub.service mailhub-doctor.timer
loginctl enable-linger # keeps user services alive after logout
Configuration
Config lives at ~/.config/mailhub/config.toml:
[server]
host = "127.0.0.1"
port = 8787
[auth]
ro_token = "..." # read-only token
full_token = "..." # full-access token
[send]
allowlist = ["*@example.com"] # glob patterns
# allow_anywhere = true # disable allowlist
[gmail]
client_id = "..."
client_secret = "..."
[graph]
client_id = "..."
[accounts.personal]
provider = "gmail"
capabilities = ["mail", "calendar", "contacts"]
[accounts.work]
provider = "graph"
capabilities = ["mail", "calendar", "contacts", "tasks"]
Security
- REST server binds only to 127.0.0.1 (never 0.0.0.0)
- No hard-delete — only trash/cancel
- Mutations require
confirm=true+ recipient allowlist - Audit log (
~/.local/state/mailhub/audit.jsonl) never records tokens/bodies - Credentials stored with 0600 permissions, atomic writes, fcntl lock
- OAuth PKCE with random state
- Message bodies treated as untrusted input
Development
uv sync --extra dev
uv run pytest
uv run mailhub init
uv run mailhub status
uv run mailhub mcp --mode ro
Architecture
mailhub/
├── config.py # TOML config, validation, provider capabilities
├── store.py # Credential store (atomic, 0600, fcntl lock)
├── oauth.py # PKCE OAuth for Google/Microsoft
├── core.py # Policy engine, allowlist, audit, retry, adapters
├── util.py # HTML→text, MIME, address parsing
├── gmail.py # Gmail API adapter
├── graph.py # Microsoft Graph adapter
├── gcal.py # Google Calendar adapter
├── gcal_graph.py # Graph Calendar adapter
├── caldav.py # CalDAV adapter (read)
├── people.py # Google People adapter
├── people_graph.py # Graph People adapter
├── carddav.py # CardDAV adapter (read)
├── tasks.py # Tasks domain
├── tasks_graph.py # Graph Tasks adapter
├── tasks_caldav.py # CalDAV VTODO adapter (read)
├── app.py # FastAPI REST server (127.0.0.1 only)
├── cli.py # mailhub admin CLI
├── mail_cli.py # mail user CLI (thin REST client)
├── mcp.py # MCP server (ro + full modes)
├── backup.py # Backup/restore utilities
└── deploy/ # systemd user units
License
MIT
Man Pages
Install man pages system-wide:
sudo cp docs/man/mailhub.1 /usr/local/share/man/man1/
sudo cp docs/man/mail.1 /usr/local/share/man/man1/
sudo mandb
Then view with:
man mailhub
man mail
Or view locally without installing:
man -l docs/man/mailhub.1
man -l docs/man/mail.1
Shell Completions
Generate completion scripts for your shell:
mailhub --generate-completion bash > ~/.local/share/bash-completion/completions/mailhub
mailhub --generate-completion zsh > ${fpath[1]}/_mailhub
mailhub --generate-completion fish > ~/.config/fish/completions/mailhub.fish
The completion script also covers the mail command (same stub).
Docker Usage
Mailhub provides official Docker images via GitHub Container Registry:
# Quick start with docker-compose
docker compose up -d
# View logs
docker compose logs -f mailhub
# Run MCP server only
docker compose run --rm mailhub-mcp
# Build locally
docker build -t zc-mailhub .
docker run -p 8787:8787 zc-mailhub serve
docker-compose.yml
The provided docker-compose.yml runs two services:
| Service | Description | Port |
|---|---|---|
mailhub |
REST API server | 8787 |
mailhub-mcp |
MCP server (full mode) | stdio |
Configuration and state are persisted via Docker volumes:
volumes:
mailhub-config:
mailhub-state:
Building Locally
# Build image
docker build -t zc-mailhub .
# Run REST API
docker run -p 8787:8787 zc-mailhub serve
# Run MCP server
docker run -it zc-mailhub mcp --mode full
Security
The Docker image follows security best practices:
- Multi-stage build — builder stage for dependencies, minimal runtime image
- Non-root user — runs as
mailhubuser (UID/GID 999) - No secrets in image — configuration and credentials mounted as volumes
- Read-only root filesystem (when deployed with
read_only: true) - Dropped capabilities —
cap_drop: ["ALL"],no-new-privileges:true
Volumes
Configuration and state are persisted via Docker volumes:
volumes:
mailhub-config:
mailhub-state:
These correspond to ~/.config/mailhub/ and ~/.local/state/mailhub/ on the host.
MCP Configuration
Mailhub's MCP server runs over stdio and works with all major AI clients. Configure it in your client's MCP settings:
Claude Desktop (~/Library/Application Support/Claude/claude_desktop_config.json on macOS, ~/.config/claude/claude_desktop_config.json on Linux)
{
"mcpServers": {
"mailhub": {
"command": "mailhub",
"args": ["mcp", "--mode", "full"],
"env": {
"MAILHUB_CONFIG": "/home/youruser/.config/mailhub/config.toml"
}
}
}
}
Cursor (.cursor/mcp.json in project root)
{
"mcpServers": {
"mailhub": {
"command": "mailhub",
"args": ["mcp", "--mode", "full"],
"env": {
"MAILHUB_CONFIG": "/home/youruser/.config/mailhub/config.toml"
}
}
}
}
VS Code / GitHub Copilot (.vscode/mcp.json in project root)
{
"mcpServers": {
"mailhub": {
"command": "mailhub",
"args": ["mcp", "--mode", "full"],
"env": {
"MAILHUB_CONFIG": "/home/youruser/.config/mailhub/config.toml"
}
}
}
}
Windsurf (~/.config/windsurf/mcp.json)
{
"mcpServers": {
"mailhub": {
"command": "mailhub",
"args": ["mcp", "--mode", "full"],
"env": {
"MAILHUB_CONFIG": "/home/youruser/.config/mailhub/config.toml"
}
}
}
}
Continue.dev (~/.continue/config.json)
{
"mcpServers": [
{
"name": "mailhub",
"command": "mailhub",
"args": ["mcp", "--mode", "full"],
"env": {
"MAILHUB_CONFIG": "/home/youruser/.config/mailhub/config.toml"
}
}
]
}
Zed (~/.config/zed/settings.json)
{
"mcp": {
"servers": {
"mailhub": {
"command": "mailhub",
"args": ["mcp", "--mode", "full"],
"env": {
"MAILHUB_CONFIG": "/home/youruser/.config/mailhub/config.toml"
}
}
}
}
}
Using uvx (No Install Required)
{
"mcpServers": {
"mailhub": {
"command": "uvx",
"args": ["--from", "zc-mailhub", "mailhub", "mcp", "--mode", "full"],
"env": {
"MAILHUB_CONFIG": "/home/youruser/.config/mailhub/config.toml"
}
}
}
}
Using pipx (Permanent Install)
{
"mcpServers": {
"mailhub": {
"command": "pipx",
"args": ["run", "--spec", "zc-mailhub", "mailhub", "mcp", "--mode", "full"],
"env": {
"MAILHUB_CONFIG": "/home/youruser/.config/mailhub/config.toml"
}
}
}
}
Using Docker
{
"mcpServers": {
"mailhub": {
"command": "docker",
"args": ["run", "-i", "--rm",
"-v", "mailhub-config:/home/mailhub/.config/mailhub",
"-v", "mailhub-state:/home/mailhub/.local/state/mailhub",
"ghcr.io/zeroclue/zc-mailhub:v0.1.2",
"mcp", "--mode", "full"],
"env": {}
}
}
}
Sourcegraph Cody (.cody/mcp.json in project root)
{
"mcpServers": {
"mailhub": {
"command": "mailhub",
"args": ["mcp", "--mode", "full"],
"env": {
"MAILHUB_CONFIG": "/home/youruser/.config/mailhub/config.toml"
}
}
}
}
Using Docker (Local)
{
"mcpServers": {
"mailhub": {
"command": "docker",
"args": ["run", "-i", "--rm", "zc-mailhub", "mcp", "--mode", "full"],
"env": {
"MAILHUB_CONFIG": "/home/mailhub/.config/mailhub/config.toml"
}
}
}
}
Setting Up Mailhub (MCP / Docker)
The MCP server and Docker container need configuration before they can access your mail/calendar. Run setup commands on the host (not inside MCP), then start the MCP/Docker.
1. Local Install Setup (then use MCP)
# Install
uv tool install zc-mailhub
# Initialize config & state
mailhub init
# Configure OAuth clients (one-time)
mailhub config setup-google # Opens Google Cloud Console
mailhub config setup-microsoft # Opens Microsoft Entra ID
# Add accounts
mailhub config add-account --alias me --provider gmail --capabilities mail --email me@gmail.com
mailhub config add-account --alias work --provider graph --capabilities mail --email me@outlook.com
# Authorize accounts (opens browser, paste redirect URL)
mailhub auth gmail me --code '<pasted-redirect-url>'
mailhub auth graph work --code '<pasted-redirect-url>'
# Verify health
mailhub doctor
# Now configure your AI client's MCP settings (see above)
# Then restart your AI client
2. Docker Setup
# Start container (creates volumes)
docker compose up -d
# Run setup INSIDE the container
docker compose exec mailhub mailhub init
docker compose exec mailhub mailhub config setup-google
docker compose exec mailhub mailhub config setup-microsoft
docker compose exec mailhub mailhub config add-account --alias me --provider gmail --capabilities mail --email me@gmail.com
docker compose exec mailhub mailhub auth gmail me --code '<pasted-redirect-url>'
# Verify
docker compose exec mailhub mailhub doctor
# Configure MCP client to use Docker (see "Using Docker" MCP config above)
3. uvx Setup (No Install)
# Run setup via uvx
uvx --from zc-mailhub mailhub init
uvx --from zc-mailhub mailhub config setup-google
uvx --from zc-mailhub mailhub config setup-microsoft
uvx --from zc-mailhub mailhub config add-account --alias me --provider gmail --capabilities mail --email me@gmail.com
uvx --from zc-mailhub mailhub auth gmail me --code '<pasted-redirect-url>'
uvx --from zc-mailhub mailhub doctor
# Configure MCP client to use uvx (see "Using uvx" MCP config above)
Key Points
| Method | Config Location | Notes |
|---|---|---|
| Local install | ~/.config/mailhub/ |
Standard XDG paths |
| Docker | Docker volumes (mailhub-config, mailhub-state) |
Persists across container restarts |
| uvx | ~/.config/mailhub/ |
Same as local install (uses real XDG) |
The OAuth flow opens a browser on the host - you'll copy the redirect URL from the browser and paste it back to the terminal running the auth command.
Remote/VPS Setup
# On remote server (with Docker)
ssh user@vps "docker compose up -d"
ssh user@vps "docker compose exec mailhub mailhub init"
ssh user@vps "docker compose exec mailhub mailhub config setup-google"
# ... etc
# Configure local AI client to use SSH tunnel:
{
"mcpServers": {
"mailhub": {
"command": "ssh",
"args": ["user@vps", "docker", "run", "-i", "--rm", "ghcr.io/zeroclue/zc-mailhub:v0.1.2", "mcp", "--mode", "full"]
}
}
}
Using SSH (Remote VPS)
{
"mcpServers": {
"mailhub": {
"command": "ssh",
"args": ["user@your-vps", "mailhub", "mcp", "--mode", "full"],
"env": {}
}
}
}
Using SSH + Docker (Remote VPS with Docker)
{
"mcpServers": {
"mailhub": {
"command": "ssh",
"args": ["user@your-vps", "docker", "run", "--rm", "-i", "zc-mailhub", "mcp", "--mode", "full"],
"env": {}
}
}
}
Read-Only Mode
For read-only access (no mutations), change --mode full to --mode ro:
{
"mcpServers": {
"mailhub": {
"command": "mailhub",
"args": ["mcp", "--mode", "ro"],
"env": {
"MAILHUB_CONFIG": "/home/youruser/.config/mailhub/config.toml"
}
}
}
}
MCP Server Capabilities
The MCP server exposes the following capabilities in its initialize response:
- Tools: All mail operations (search, get, send, draft, move, trash, folders) + status
- Resources: Not implemented (mail data accessed via tools)
- Prompts: Not implemented
- Logging: Supported
Server info returned on initialize:
server_name: "mailhub"server_version: "0.1.1" (current package version)instructions: Description of the server and usage hints
Metadata
Release files for zc-mailhub 0.1.8
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | Actions |
|---|---|---|---|---|
| zc_mailhub-0.1.8.tar.gz | 230.5 kB | With provenance | Details for zc_mailhub-0.1.8.tar.gz |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Actions Reset |
|---|---|---|---|---|
| zc_mailhub-0.1.8-py3-none-any.whl | Python 3 | none | any | Details for zc_mailhub-0.1.8-py3-none-any.whl |
Total release size: 300.7 kB
Release files / zc_mailhub-0.1.8.tar.gz
| Download URL | zc_mailhub-0.1.8.tar.gz |
|---|---|
| Size | 230.5 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
de28ca60094ac72865393802b7c6572ce1775f2e628e43b1018fa79649b140f0
|
|
BLAKE2b-256 checksum How to use checksums |
ae18c283d107d86b3906c9e5c4d42e9e205188fb343192ecb04845c35267a051
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Sep 27, 2026.
Transparency logRelease files / zc_mailhub-0.1.8-py3-none-any.whl
| Download URL | zc_mailhub-0.1.8-py3-none-any.whl |
|---|---|
| Size | 70.2 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
b3c07bbb4e0eda08c405ebe732ca03f1571d6b20b45f5d5f5b9ab388836665d0
|
|
BLAKE2b-256 checksum How to use checksums |
9038da600e796e76ef116fd9ca64c077cbb9504d8bfe5add74d4e2e400fda175
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Sep 27, 2026.
Transparency logSecurity for zc-mailhub 0.1.8
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
Full provenance
All 2 files in this release have been uploaded with provenance metadata.
Attestation for zc_mailhub-0.1.8.tar.gz
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Sep 27, 2026.
Transparency logA note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.
To learn more about protecting yourself from malicious actors, visit our package security documentation.