mailcow-dav-mcp
This server does not currently have a deployable release on Glama. Follow the repository's setup instructions to install and configure it for your MCP client, including any required credentials and local dependencies.
Works with
๐ฌ๐ง English ยท ๐ฎ๐น Italiano
An MCP server that gives AI assistants access to the ๐ address book (CardDAV),
๐
calendar (CalDAV) and โ
tasks of a mailcow mailbox. It uses the SOGo DAV endpoint
that mailcow already publishes at /SOGo/dav/<mailbox>/, so you don't need to change anything on the mailcow side.
If this project saves you time, give it a โญ on GitHub: it helps other mailcow users find it.
๐ Contents
๐ Connect your client: Claude Code ยท Claude Desktop ยท Codex ยท ChatGPT ยท other HTTP clients
Related MCP server: caldav-mcp
โจ Features
๐ Contacts | ๐ Calendar | โ Tasks |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
|
| |
|
|
๐ฉบ dav_status checks the credentials and lists your address books and calendars.
Fields: prefix, given/middle/family name, suffix, nickname, typed emails and phones (work:, cell:โฆ), company and
department, job title and role, notes, birthday, anniversary, spouse, assistant, IM (telegram:@x, skype:idโฆ),
time zone, URLs, postal addresses (type:street;city;region;zip;country), tags and photo.
Groups vs tags: groups are Apple-style group vCards (what Apple Contacts and BusyContacts show).
Tags are the CATEGORIES of each contact (what SOGo shows). Both are supported.
๐ Requirements
๐ A mailcow mailbox (any recent mailcow with SOGo enabled).
โก uv (recommended), or Python 3.11+ with
pip.๐ป macOS, Linux or Windows.
๐ Quick start
1๏ธโฃ Create a mailcow app password
Use an app password, not your mailbox password: you can limit it to DAV and revoke it at any time.
Log in to the mailcow UI as the mailbox user.
Open Mailbox โ Edit โ App passwords and add a new one.
Under Allow for, select only DAV/SOGo.
Copy the password. You need it in the next step.
2๏ธโฃ Store the credentials
Keep them in a dedicated file that only your user can read, so the password never ends up in a client's config:
mkdir -p ~/.config/mailcow-dav-mcp
cat > ~/.config/mailcow-dav-mcp/.env <<'EOF'
MAILCOW_DAV_URL=https://mail.example.com
MAILCOW_DAV_USER=you@example.com
MAILCOW_DAV_PASSWORD=your-app-password
MAILCOW_DAV_TIMEZONE=Europe/Rome
EOF
chmod 600 ~/.config/mailcow-dav-mcp/.envEvery client then needs only MAILCOW_DAV_ENV_FILE pointing to this file. A template is in .env.example.
In client configs, write the path in full (/Users/you/... or /home/you/...). Clients don't expand ~.
3๏ธโฃ Install and test
Option A: no clone (recommended). uvx downloads and runs the server on demand:
MAILCOW_DAV_ENV_FILE=$HOME/.config/mailcow-dav-mcp/.env \
uvx --from git+https://github.com/OakNet-io/mailcow-dav-mcp@v0.2.1 mailcow-dav-mcp --checkOption B: from a clone (for development or to pin a version):
git clone https://github.com/OakNet-io/mailcow-dav-mcp.git
cd mailcow-dav-mcp
uv sync # or: python3 -m venv .venv && .venv/bin/pip install -e .
cp .env.example .env && chmod 600 .env # then edit .env
uv run mailcow-dav-mcp --checkโ
Expected output: OK server=... user=... followed by your address books and calendars.
Updating. The commands pin a release tag (@v0.2.1): you always run a reviewed version, and a new
commit on main never reaches you by surprise. uvx caches it in ~/.cache/uv, so later starts are instant.
To update, change the tag in the command and in your client config to the latest
release.
To always follow main instead, drop the tag and add --refresh to fetch new commits:
uvx --refresh --from git+https://github.com/OakNet-io/mailcow-dav-mcp mailcow-dav-mcp --check.
uv cache clean mailcow-dav-mcp removes the cached copy.
With a clone, replaceuvx --from git+โฆ in the client configs below with
uv --directory /path/to/mailcow-dav-mcp run mailcow-dav-mcp. A clone also reads .env from the project folder.
๐ Connect your client
claude mcp add mailcow-dav --scope user \
-e MAILCOW_DAV_ENV_FILE=$HOME/.config/mailcow-dav-mcp/.env \
-- uvx --from git+https://github.com/OakNet-io/mailcow-dav-mcp@v0.2.1 mailcow-dav-mcpCheck with claude mcp list, or type /mcp inside Claude Code.
--scope user makes the server available in every project. Leave it out to add it to the current project only.
Open Settings โ Developer โ Edit Config, or edit the file directly:
๐ macOS:
~/Library/Application Support/Claude/claude_desktop_config.json๐ช Windows:
%APPDATA%\Claude\claude_desktop_config.json
{
"mcpServers": {
"mailcow-dav": {
"command": "uvx",
"args": ["--from", "git+https://github.com/OakNet-io/mailcow-dav-mcp@v0.2.1", "mailcow-dav-mcp"],
"env": {
"MAILCOW_DAV_ENV_FILE": "/Users/you/.config/mailcow-dav-mcp/.env"
}
}
}
}Quit Claude Desktop completely and start it again. The tools appear under the tools icon in the chat box. A copy of this snippet is in claude_desktop_config.example.json.
Claude Desktop starts servers with a minimalPATH. If it reports spawn uvx ENOENT, put the full path of uvx
in command (find it with which uvx, e.g. /opt/homebrew/bin/uvx or ~/.local/bin/uvx).
codex mcp add mailcow-dav \
--env MAILCOW_DAV_ENV_FILE=$HOME/.config/mailcow-dav-mcp/.env \
-- uvx --from git+https://github.com/OakNet-io/mailcow-dav-mcp@v0.2.1 mailcow-dav-mcpOr add it by hand to ~/.codex/config.toml. The Codex CLI and the IDE extension share this file:
[mcp_servers.mailcow-dav]
command = "uvx"
args = ["--from", "git+https://github.com/OakNet-io/mailcow-dav-mcp@v0.2.1", "mailcow-dav-mcp"]
env = { MAILCOW_DAV_ENV_FILE = "/Users/you/.config/mailcow-dav-mcp/.env" }Check with codex mcp list, or /mcp inside a Codex session.
ChatGPT connects only to remote MCP servers over HTTPS: it can't start a local command. Custom MCP servers are available on ChatGPT web for Business and Enterprise/Edu workspaces, subject to the workspace permissions (Create plugins with MCPs). See OpenAI's guide: Connecting custom MCP servers in ChatGPT.
1. Start the server in HTTP mode on your machine or on a server you control. It listens on 127.0.0.1 only:
MAILCOW_DAV_ENV_FILE=$HOME/.config/mailcow-dav-mcp/.env \
uvx --from git+https://github.com/OakNet-io/mailcow-dav-mcp@v0.2.1 mailcow-dav-mcp --transport http --port 8000The MCP endpoint is http://127.0.0.1:8000/mcp.
2. Make it reachable by ChatGPT without exposing it. Choose one:
๐ก๏ธ OpenAI Secure MCP Tunnel (recommended). The server stays private. Follow the tunnel setup in the guide above and point the tunnel at
http://127.0.0.1:8000/mcp.๐ A reverse proxy that adds OAuth, for example an identity-aware proxy in front of the server.
If the tunnel or proxy forwards a public host name, allow it with MAILCOW_DAV_MCP_ALLOWED_HOSTS=mcp.example.com.
Otherwise the server rejects the requests with 421 (DNS-rebinding protection).
3. Add it in ChatGPT.
Open Plugins, select +, then Add custom MCP server.
Enter a name. Under Connection, enter the HTTPS URL, or select Tunnel and choose your tunnel.
Configure authentication, then check I understand and want to continue.
Select Create as a plugin and review the discovered tools.
Install the plugin, start a new chat, type
@and select it.
In the legacy flow (Settings โ Apps โ Create), enter the endpoint, select Scan Tools, then Create.
Never publish the endpoint without authentication. Anyone with the URL could read and change your contacts and calendar. ChatGPT supports OAuth or no authentication, so use the tunnel or an OAuth proxy.
Any client that supports Streamable HTTP can use the HTTP mode. To listen on an address other than loopback you must set a bearer token, otherwise the server refuses to start. Generate the token once and keep it in a private file:
mkdir -p ~/.config/mailcow-dav-mcp
(umask 077; openssl rand -hex 32 > ~/.config/mailcow-dav-mcp/token)
export MAILCOW_DAV_MCP_TOKEN="$(cat ~/.config/mailcow-dav-mcp/token)"
MAILCOW_DAV_MCP_ALLOWED_HOSTS=mcp.example.com \
mailcow-dav-mcp --transport http --host 127.0.0.1 --port 8000Publish it through a reverse proxy with TLS on the same machine, forwarding https://mcp.example.com/mcp to
http://127.0.0.1:8000/mcp. Use --host 0.0.0.0 only on a private network, and never without TLS in front: the token
travels in a header. MAILCOW_DAV_MCP_ALLOWED_HOSTS is the public host name the clients use. Without it the server
answers 421.
On the client machine, read the same token:
Claude Code:
claude mcp add --transport http mailcow-dav https://mcp.example.com/mcp -H "Authorization: Bearer $(cat ~/.config/mailcow-dav-mcp/token)"Codex:
export MAILCOW_DAV_MCP_TOKEN="$(cat ~/.config/mailcow-dav-mcp/token)"in the shell that starts Codex, thencodex mcp add mailcow-dav --url https://mcp.example.com/mcp --bearer-token-env-var MAILCOW_DAV_MCP_TOKEN
โ๏ธ Configuration
Variable | Default | Description |
๐ | required | Base URL of mailcow, e.g. |
๐ค | required | Full mailbox address |
๐ | required | App password with DAV/SOGo access |
๐ | โ |
|
๐ |
| IANA time zone for dates without an offset |
๐งญ |
| Override if the DAV root is elsewhere |
๐ |
|
|
โฑ๏ธ |
| HTTP timeout to the DAV server, in seconds |
๐ฆ |
| Default export folder, and the folder for relative paths. Import, export and local photos may only use files in this folder or in |
๐ | โ | Extra folders the tools may read or write, separated by |
๐๏ธ |
|
|
๐ |
| Largest DAV response accepted, after decompression |
๐ฅ |
| Largest import file and number of cards per import |
๐ |
|
|
๐ |
| HTTP listen address and endpoint path |
๐๏ธ | โ | Bearer token for every HTTP request. Mandatory when the host is not loopback. |
๐ท๏ธ | โ | Extra |
Command line: mailcow-dav-mcp [--check] [--transport stdio|http] [--host H] [--port P] [--path /mcp]
๐ Security
๐ Credentials | Use an app password limited to DAV/SOGo. Keep it in a |
๐ Concurrent edits | Every update and delete is conditional on the ETag ( |
๐ผ๏ธ Photos from URLs | Downloads go only to public IP addresses. Each redirect is checked, the connection is pinned to the verified IP (no DNS rebinding), and the download stops at 2 MB. Photo references stored on the server are never read from the local disk. |
๐ Paths | Address book and calendar IDs must be a single path segment. Resource names never come from a UID found in an imported file. Exports never overwrite a file unless |
๐ HTTP mode | Loopback by default, token required on any other address, |
๐๏ธ Local files | In every mode, import, export and local photos are limited to |
๐งท DAV server answers | Only paths on the configured host and inside the requested collection are accepted, so the credentials never go elsewhere. Responses and imports have size limits. Recurrence rules that never end are not expanded. |
๐๏ธ Deletes and prompt injection | Notes, titles and invitations come from other people and can contain instructions. Tools carry MCP annotations ( |
Found a vulnerability? Open aprivate security advisory instead of a public issue.
๐ก Usage notes
๐๏ธ Dates are ISO 8601. Without an offset,
MAILCOW_DAV_TIMEZONEis used. Explicit offsets (+02:00) are converted to that time zone, keeping the same instant. For all-day events,endis exclusive.๐ In updates, lists (
emails,phones,addresses,attendees,urls,categories) replace the existing ones.โณ Changing only text fields of an event leaves its dates untouched. Moving
startkeeps the duration. Turning an all-day event into a timed one requiresstartwith a time.๐ Recurrences are expanded on the client. To change or delete one occurrence, pass the
recurrence_idfromlist_eventsasoccurrence. It keeps working after the occurrence has been moved. On a non-recurring eventoccurrenceis an error: the tool never falls back to the whole event.rrule=""turns a series into a single event and drops its exceptions. Each series is expanded up to 2000 occurrences per request.โฐ New task reminders are relative to the due date, or to the start date when there is no due date. Existing reminders keep their anchor, unless that date is removed.
โ๏ธ
respond_to_invitationsets your PARTSTAT. SOGo sends the reply to the organizer. SOGo also sends the invitations when you create an event with attendees.๐
search_contactsscans the whole address book on the client, because SOGo's server-side query matches only some fields. The first search takes about 2โ3 seconds for ~700 contacts. Later searches download only the cards whose ETag changed.๐ค
export_contactsasvcfis an exact copy of every resource, groups included.csvcontains contacts only. Resources the server lists but doesn't return are reported inmissing. In CSV, list items are separated by|(\|is a literal bar), and cells that start with=,+,-or@get a leading'so spreadsheets don't run them as formulas.import_contactsreverses both.๐ฅ
import_contactsimports contacts first, then groups, with members rewritten to the UIDs actually used. Members that match no contact are listed inunresolved_members, in the dry run too.
SOGo re-serializes every vCard. If a line is exactly 75 characters long (or 149, 223โฆ), SOGo folds it with an empty
continuation line. Apple Contacts and BusyContacts then merge it with the next field (e.g. country IT +
CATEGORIES:โฆ โ ITCATEGORIES:โฆ). A client can't fix this: create_contact/update_contact return a warning, and
check_folding lists the affected contacts. Fix: change the field length (e.g. Italia instead of IT).
๐ฉบ Troubleshooting
Symptom | Fix |
๐ซ | Check user and app password. The app password must allow DAV/SOGo. |
๐ | Check |
โ | The client doesn't pass the variables: check |
๐งฉ | Use the full path of |
๐ | The item changed after you read it. Read it again and retry. |
๐ HTTP | Add the public host name to |
๐๏ธ HTTP | Send |
Server messages are in English.
๐ ๏ธ Development
uv sync --extra dev
uv run --extra dev pytestFor manual integration tests, run Radicale locally. The server expects the SOGo layout
<principal>/Contacts/<id>/ and <principal>/Calendar/<id>/, so give the test user write access to nested
collections and create them once:
printf 'tester:secret\n' > users
cat > rights <<'EOF2'
[root]
user: .+
collection:
permissions: R
[owner]
user: .+
collection: {user}(/.*)?
permissions: RrWw
EOF2
cat > config <<EOF2
[server]
hosts = 127.0.0.1:5232
[auth]
type = htpasswd
htpasswd_filename = $PWD/users
htpasswd_encryption = plain
[rights]
type = from_file
file = $PWD/rights
[storage]
filesystem_folder = $PWD/data
EOF2
uvx radicale --config ./config &
sleep 3
B=http://127.0.0.1:5232; U=tester:secret
curl -u "$U" -X PROPFIND -H "Depth: 0" "$B/tester/" -o /dev/null # creates the principal
curl -u "$U" -X MKCOL "$B/tester/Contacts/"
curl -u "$U" -X MKCOL "$B/tester/Calendar/"
curl -u "$U" -X MKCOL -H "Content-Type: application/xml" "$B/tester/Contacts/personal/" --data \
'<D:mkcol xmlns:D="DAV:" xmlns:C="urn:ietf:params:xml:ns:carddav"><D:set><D:prop><D:resourcetype><D:collection/><C:addressbook/></D:resourcetype></D:prop></D:set></D:mkcol>'
curl -u "$U" -X MKCALENDAR "$B/tester/Calendar/personal/"Then set MAILCOW_DAV_URL=http://127.0.0.1:5232, MAILCOW_DAV_USER=tester, MAILCOW_DAV_PRINCIPAL_PATH=/tester/.
Module | Role |
| Minimal WebDAV client (PROPFIND, REPORT, GET, conditional PUT/DELETE) |
| Contacts, groups, photos, client-side search |
| Events, occurrences, invitations, tasks |
| vCard/CSV import and export |
| VTIMEZONE generation |
| Path, URL, file and ETag checks |
| MCP tools, stdio and HTTP transports |
Contributions are welcome: open an issue to discuss bigger changes first, and add a test for every fix.
๐ Support
๐ Documentation: this README ยท Italiano
๐ Issues: GitHub Issues
๐ฌ Discussions: GitHub Discussions
๐ Security: private advisory
๐ Acknowledgements
Model Context Protocol and its Python SDK
๐ License
Released under the MIT License.
โญ Star the repo if it helps you ยท ๐ Made for the mailcow community
This server cannot be deployed
Maintenance
Related MCP Connectors
Connect Apple iCloud and Microsoft 365 mail, calendars, contacts and tasks to AI.
Calendar API for AI agents: events, availability, Google/Microsoft setup, scheduling, and iCal.
Email, calendar and contacts for AI agents: Gmail, Microsoft 365 and IMAP accounts behind one API.
Email inboxes and calendars for AI agents: send, receive, search, draft and schedule.
Related MCP Servers
- AlicenseNot gradedqualityAmaintenanceEnables AI assistants to connect to CalDAV servers to list calendars and events, create, update, and delete calendar events, and manage tasks (VTODOs) including listing, creating, updating, completing, and deleting them.3,018 npmMIT
- AlicenseNot gradedqualityAmaintenanceEnables AI assistants to connect to CalDAV servers and manage calendars through natural language, including listing calendars and browsing, creating, updating, and deleting events and tasks (VTODOs). Supports recurring events, all-day events, due-date filtering, status changes, and pagination of long task lists.3,018 npmMIT
- AlicenseNot gradedqualityAmaintenanceEnables AI assistants to connect to CalDAV servers and manage calendars, events, and tasks through tools for listing, creating, updating, completing, and deleting items.3,018 npmMIT
- AlicenseNot gradedqualityAmaintenanceEnables AI assistants to connect to CalDAV servers and perform calendar and task operations, including listing calendars/events, creating, updating, and deleting events, and managing VTODO tasks with scheduling, recurrence, status, and pagination support.3,018 npmMIT