Files
dav-imap-mcp/README.md
Yigit Colakoglu 1543bc4174
All checks were successful
Build And Test / publish (push) Successful in 49s
Quote original message in reply drafts
2026-01-01 17:01:22 -08:00

5.6 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)

  1. Copy the environment template:

    cp .env.example .env
    
  2. Edit .env with your credentials.

  3. Start the server:

    docker compose up -d
    
  4. 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
Email 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. The draft includes reply headers and a quoted original message so webmail clients can preserve threading on send. Then send it with send_draft.

  • Provide in_reply_to_email_id (and optionally in_reply_to_mailbox, default INBOX).
  • reply_all=true includes original recipients; otherwise it replies to the sender/Reply-To.
  • If to/subject are omitted, they are derived from the original email; body is still required.
  • in_reply_to_email_id is the email UID from list_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 ps or curl http://localhost:8000/mcp
  • Auth errors: confirm MCP_API_KEY and client config
  • IMAP/SMTP failures: verify credentials and app-specific passwords
  • CalDAV/CardDAV failures: confirm base URL and username

License

MIT