5.4 KiB
DAV/IMAP MCP Server
A self-hosted MCP server that connects IMAP/SMTP, CalDAV, and CardDAV to MCP-compatible clients. It exposes email, calendar, and contacts tools and can optionally send new email notifications to a Poke webhook.
Features
- Email tools over IMAP/SMTP (list, read, search, drafts, send drafts, flags, unsubscribe)
- Calendar tools over CalDAV (list, create, update, delete)
- Contacts tools over CardDAV (list, create, update, delete)
- Optional email notifications via webhook with IMAP IDLE or polling
- SQLite cache with Alembic migrations
- API key auth for the MCP endpoint
Quickstart (Docker Compose)
-
Copy the environment template:
cp .env.example .env -
Edit
.envwith your credentials. -
Start the server:
docker compose up -d -
Verify it is running:
curl http://localhost:8000/mcp
Local Run (Python)
python -m venv venv
source venv/bin/activate # Windows: venv\Scripts\activate
pip install -r requirements.txt
cp .env.example .env
# Edit .env
python src/server.py
Configuration
All settings are read from .env. Configure only the services you want to enable.
Minimal email-only setup
MCP_API_KEY=your-secret-key
PORT=8000
IMAP_HOST=imap.example.com
IMAP_USERNAME=you@example.com
IMAP_PASSWORD=your-password
SMTP_HOST=smtp.example.com
SMTP_USERNAME=you@example.com
SMTP_PASSWORD=your-password
SMTP_FROM_EMAIL=you@example.com
ENABLE_CALENDAR=false
ENABLE_CONTACTS=false
Full setup (email + calendar + contacts)
MCP_API_KEY=your-secret-key
PORT=8000
IMAP_HOST=imap.example.com
IMAP_PORT=993
IMAP_USERNAME=you@example.com
IMAP_PASSWORD=your-password
IMAP_USE_SSL=true
SMTP_HOST=smtp.example.com
SMTP_PORT=587
SMTP_USERNAME=you@example.com
SMTP_PASSWORD=your-password
SMTP_USE_TLS=true
SMTP_FROM_EMAIL=you@example.com
SMTP_FROM_NAME=Your Name
CALDAV_URL=https://caldav.example.com/dav
CALDAV_USERNAME=you@example.com
CALDAV_PASSWORD=your-password
ICS_CALENDARS=Team|https://example.com/team.ics,Family|https://example.com/family.ics
ICS_CALENDAR_TIMEOUT=20
ICS_CALENDARS=Team|https://example.com/team.ics,Family|https://example.com/family.ics
ICS_CALENDAR_TIMEOUT=20
CARDDAV_URL=https://carddav.example.com/dav/addressbooks/users/you@example.com/contacts/
CARDDAV_USERNAME=you@example.com
CARDDAV_PASSWORD=your-password
Contacts tools always use CARDDAV_URL as the full CardDAV address book URL. Listing address books is not exposed via MCP.
ICS calendars are optional and read-only. Set ICS_CALENDARS to a comma-separated list of entries, each as name|url or just url if you want the name inferred.
Email notifications (Poke webhook)
Enable notifications to send new-email alerts to Poke. The server will use IMAP IDLE when available and fall back to polling.
ENABLE_EMAIL_NOTIFICATIONS=true
NOTIFICATION_MAILBOXES=INBOX,Updates
NOTIFICATION_POLL_INTERVAL=60
NOTIFICATION_IDLE_TIMEOUT=1680
POKE_WEBHOOK_URL=https://poke.com/api/v1/inbound-sms/webhook
POKE_API_KEY=your-poke-api-key
POKE_WEBHOOK_TIMEOUT=30
POKE_WEBHOOK_MAX_RETRIES=3
MCP Client Setup
MCP Inspector
npx @modelcontextprotocol/inspector
- Transport: Streamable HTTP
- URL:
http://localhost:8000/mcp
Claude Desktop
{
"mcpServers": {
"pim": {
"transport": "http",
"url": "http://localhost:8000/mcp"
}
}
}
Poke
Add your MCP endpoint at https://poke.com/settings/connections.
Available Tools
| Category | Tools |
|---|---|
list_mailboxes, list_emails, list_drafts, read_email, search_emails, move_email, delete_email, delete_draft, save_draft, edit_draft, send_draft, set_email_flags, unsubscribe_maillist |
|
| Calendar | list_calendars, list_events, get_event, create_event, update_event, delete_event |
| Contacts | list_contacts, get_contact, create_contact, update_contact, delete_contact |
| System | get_server_info |
Sending email
Emails are sent only from drafts. Create or edit a draft with save_draft/edit_draft, then send it with send_draft using the returned draft ID.
Replying to an email
Use in_reply_to_email_id on save_draft or edit_draft to create a reply without a separate tool. Then send it with send_draft.
- Provide
in_reply_to_email_id(and optionallyin_reply_to_mailbox, defaultINBOX). reply_all=trueincludes original recipients; otherwise it replies to the sender/Reply-To.- If
to/subjectare omitted, they are derived from the original email;bodyis still required. in_reply_to_email_idis the email UID fromlist_emails/read_email, not the RFC Message-ID header.
Example (send a reply):
{
"tool": "save_draft",
"args": {
"in_reply_to_email_id": "12345",
"in_reply_to_mailbox": "INBOX",
"reply_all": true,
"body": "Thanks — sounds good to me."
}
}
Then send the draft by its returned ID:
{
"tool": "send_draft",
"args": {
"email_id": "67890"
}
}
Database and Migrations
The server uses SQLite (default: /data/cache.db) and Alembic.
alembic revision --autogenerate -m "Describe change"
alembic upgrade head
Troubleshooting
- Connection refused: check
docker compose psorcurl http://localhost:8000/mcp - Auth errors: confirm
MCP_API_KEYand client config - IMAP/SMTP failures: verify credentials and app-specific passwords
- CalDAV/CardDAV failures: confirm base URL and username
License
MIT