Skip to main content
Glama

caldav-mcp

Give AI assistants full read/write access to any CalDAV calendar.

A self-hosted bridge between Model Context Protocol clients and your CalDAV infrastructure. Connect Claude, Codex, Cursor, VS Code, and other AI assistants to Nextcloud, Radicale, Baikal, and any RFC 4791 calendar server. Query events, create meetings, manage attendees, and move events between calendars — all through a single Docker container with no database and no external dependencies.

License: MIT Python 3.11+ Docker MCP Release M8ven Score

What it does

caldav-mcp gives your AI assistant direct access to your calendar. Instead of copy-pasting events or switching tabs, ask your assistant to do it:

Related MCP server: mcp-labrat

Why caldav-mcp

Self-hosted AI assistants

Keep your AI calendar access on your own infrastructure. No third-party SaaS, no data leaves your network.

Nextcloud / Radicale / Baikal integration

Works with any RFC 4791 CalDAV server. Radicale is integration-tested in CI, Nextcloud is used in development. Baikal, ownCloud, iCloud, and Fastmail are protocol-compatible.

Centralized MCP infrastructure

One server instance for your entire homelab or team. Multiple AI clients connect to the same endpoint.

Multiple CalDAV accounts

Credentials travel per-request in HTTP headers — a single server serves different CalDAV accounts without restarts or reconfiguration.

Docker / homelab deployment

One Docker image, one docker compose up. No database, no background workers, no sidecars. Runs anywhere Docker runs.

Full read/write access

14 focused tools covering calendar discovery, event queries, creation, updates, deletion, moves, and attendee management.

Security built in

Optional API-key authentication with constant-time comparison, per-IP rate limiting with exponential backoff, input sanitization, and structured audit logging.

How it works

flowchart LR
    subgraph "MCP Client"
        AI["AI / MCP Client\n(Claude, Cursor, VS Code, …)"]
    end

    subgraph "caldav-mcp"
        EP["/mcp\nStreamable HTTP"]
        AK["API Key Auth\n(optional)"]
        RL["Per-IP Rate\nLimiting"]
    end

    subgraph "CalDAV Providers"
        N["Nextcloud"]
        R["Radicale"]
        B["Baikal"]
    end

    AI -- "Streamable HTTP\n+ headers" --> EP
    EP --> AK
    EP --> RL
    EP -- "CalDAV protocol" --> N
    EP -- "CalDAV protocol" --> R
    EP -- "CalDAV protocol" --> B

Stateless, per-request architecture

The server maintains no session state between requests. CalDAV credentials travel per-request in HTTP headers (X-Caldav-Url, X-Caldav-Username, X-Caldav-Password), which means:

  • Different requests can target different CalDAV accounts — a single server instance serves multiple users or calendars.

  • Environment variables provide a simpler single-account fallback — set CALDAV_URL, CALDAV_USERNAME, CALDAV_PASSWORD and omit the headers.

  • No database, no persistent account state — the only in-memory state is a thread-safe LRU cache of CalDAV client connections.

Two authentication layers sit between the client and the CalDAV server:

  1. MCP endpoint auth — optional API key via Authorization: Bearer or X-Api-Key header. When CALDAV_MCP_API_KEY is unset, the endpoint is open. Protects the MCP endpoint itself.

  2. CalDAV credentials — HTTP headers (preferred) or environment variables (fallback). Authenticate against the actual CalDAV server.

Supported CalDAV servers

Provider / Server

Status

Radicale

Integration-tested (CI pipeline)

Nextcloud

Known to work (used in development)

Baikal

Protocol-compatible

ownCloud

Protocol-compatible

iCloud

Protocol-compatible

Fastmail

Protocol-compatible

Other RFC 4791 CalDAV servers

Protocol-compatible

Any server that implements the CalDAV standard (RFC 4791) should work. If it doesn't, open an issue.

MCP tools (14)

The server exposes 14 MCP tools across three categories.

Tool

Description

caldav_list_calendars

List all available calendars for the configured account

caldav_get_events

Get events in a date range

caldav_get_today_events

Get events for today

caldav_get_week_events

Get events for the next 7 days

caldav_get_event_by_uid

Get a specific event by UID, including attendees

caldav_search_events

Find events by text across summary, description, location, and categories

caldav_get_freebusy

Get free/busy information for a time range

Tool

Description

caldav_create_event

Create a new event — supports recurring rules, priority, categories, and attendees

caldav_update_event

Partially update an existing event by UID

caldav_delete_event

Delete an event by UID

caldav_move_event

Move an event between calendars

Tool

Description

caldav_add_attendee

Add an attendee to an event

caldav_remove_attendee

Remove an attendee from an event

caldav_list_attendees

List attendees of an event

Full API documentation: docs/api.md.

Quick start

1. Pull and run

Pull the latest image:

docker pull ghcr.io/gelse/caldav-mcp:latest

Then run it directly (replace the environment variables with your values):

docker run -d \
  -p 8600:8080 \
  -e CALDAV_URL=https://cloud.example.com/remote.php/dav/calendars/user/ \
  -e CALDAV_USERNAME=user \
  -e CALDAV_PASSWORD=app-password \
  -e CALDAV_MCP_API_KEY=your-secret-token \
  -e TZ=Europe/Vienna \
  ghcr.io/gelse/caldav-mcp:latest

Note: The CALDAV_URL, CALDAV_USERNAME, and CALDAV_PASSWORD environment variables are optional. If omitted, CalDAV credentials must be sent per-request via the X-Caldav-Url, X-Caldav-Username, and X-Caldav-Password HTTP headers — see MCP client configuration.

The server is now running at http://localhost:8600/mcp (Streamable HTTP).

2. Verify

curl -s http://localhost:8600/mcp \
  -H "Authorization: Bearer your-secret-token" \
  -H "X-Caldav-Url: https://cloud.example.com/remote.php/dav/calendars/user/" \
  -H "X-Caldav-Username: user" \
  -H "X-Caldav-Password: app-password" \
  -d '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2024-11-05","capabilities":{},"clientInfo":{"name":"test","version":"1"}}}'

MCP client configuration

Streamable HTTP only — the server does not support stdio transport. Any MCP client that supports Streamable HTTP can connect.

The standard configuration format with per-request CalDAV credentials:

{
  "mcpServers": {
    "caldav": {
      "type": "http",
      "url": "http://localhost:8600/mcp",
      "headers": {
        "Authorization": "Bearer YOUR_API_KEY",
        "X-Caldav-Url": "https://cloud.example.com/remote.php/dav/calendars/user/",
        "X-Caldav-Username": "user",
        "X-Caldav-Password": "app-password"
      }
    }
  }
}

Client-specific configuration

Claude Desktop

Config file location:

  • macOS: ~/Library/Application Support/Claude/claude_desktop_config.json

  • Linux: ~/.config/Claude/claude_desktop_config.json

Uses the mcpServers key. Custom Connectors added via the UI require a paid plan.

Claude Code

Config file locations:

  • Global: ~/.claude/settings.json

  • Project: .mcp.json (in project root)

Uses the mcpServers key. You can also add via CLI:

claude mcp add --transport http caldav http://localhost:8600/mcp

Note: The CLI does not support setting custom headers. Add the headers block manually in the JSON config after using the CLI command.

Cursor

Config file locations:

  • Project: .cursor/mcp.json

  • Global: ~/.cursor/mcp.json

Uses the mcpServers key.

VS Code

Config file location: .vscode/mcp.json

Uses the servers key, not mcpServers:

{
  "servers": {
    "caldav": {
      "type": "http",
      "url": "http://localhost:8600/mcp",
      "headers": {
        "Authorization": "Bearer YOUR_API_KEY",
        "X-Caldav-Url": "https://cloud.example.com/remote.php/dav/calendars/user/",
        "X-Caldav-Username": "user",
        "X-Caldav-Password": "app-password"
      }
    }
  }
}

OpenCode

Config file location: project root (e.g. opencode.json).

Uses the mcpServers key with the standard format shown above.

OpenWebUI

Configure via Admin Panel → Settings → Connections. Add the MCP server URL and headers through the UI.

Multiple CalDAV accounts

Because credentials travel per-request in HTTP headers, a single server instance can serve multiple CalDAV accounts. Configure each MCP client connection with different X-Caldav-* headers.

Deployment

Install from a release

# Clone at a specific version
git clone --branch v0.1.0 https://github.com/gelse/caldav-mcp.git
cd caldav-mcp
cp .env.example .env
# Edit .env with your CalDAV credentials
docker compose up -d

Using the public Docker image

Docker image releases are published to the GitHub Container Registry. Pull the latest image:

docker pull ghcr.io/gelse/caldav-mcp:latest

Then run it directly (replace the environment variables with your values):

docker run -d \
  -p 8600:8080 \
  -e CALDAV_URL=https://cloud.example.com/remote.php/dav/calendars/user/ \
  -e CALDAV_USERNAME=user \
  -e CALDAV_PASSWORD=app-password \
  -e CALDAV_MCP_API_KEY=your-secret-token \
  -e TZ=Europe/Vienna \
  ghcr.io/gelse/caldav-mcp:latest

Note: The CalDAV credentials (CALDAV_URL, CALDAV_USERNAME, CALDAV_PASSWORD) are optional. You can omit them and instead provide credentials per-request via the X-Caldav-Url, X-Caldav-Username, and X-Caldav-Password HTTP headers in your MCP client configuration — see MCP client configuration.

Local / private deployment

The simplest setup — AI client and caldav-mcp on the same machine:

AI Client → http://localhost:8600/mcp → CalDAV Server
docker compose up -d

The server listens on localhost:8600 and is not accessible from the network unless you explicitly publish the port.

Remote / shared deployment

For multi-user or remote access, put the server behind a TLS-terminating reverse proxy:

AI Client → HTTPS → reverse proxy → caldav-mcp → CalDAV Server
server {
    listen 443 ssl;
    server_name caldav-mcp.example.com;

    ssl_certificate     /etc/ssl/certs/caldav-mcp.pem;
    ssl_certificate_key /etc/ssl/private/caldav-mcp-key.pem;

    location /mcp {
        proxy_pass http://127.0.0.1:8600/mcp;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
    }
}

Built-in TLS

If you prefer not to use a reverse proxy, enable built-in TLS:

CALDAV_MCP_TLS_CERT=/path/to/cert.pem \
CALDAV_MCP_TLS_KEY=/path/to/key.pem \
docker compose up -d

⚠️ Do not expose the MCP endpoint publicly without both authentication and TLS. Without CALDAV_MCP_API_KEY set, the endpoint is open. Without TLS, all traffic — including API keys and CalDAV passwords — is transmitted in plaintext.

Authentication & security

Authentication

MCP endpoint auth (CALDAV_MCP_API_KEY):

  • Every request to /mcp must include Authorization: Bearer <token> or X-Api-Key: <token>.

  • Token comparison uses constant-time comparison to prevent timing attacks.

  • When CALDAV_MCP_API_KEY is unset, the endpoint is open. Do not expose it to the public internet without authentication.

CalDAV credentials are resolved per-request:

  1. HTTP headers (preferred): X-Caldav-Url, X-Caldav-Username, X-Caldav-Password

  2. Environment variables (fallback): CALDAV_URL, CALDAV_USERNAME, CALDAV_PASSWORD

TLS

  • Option A: Enable built-in TLS by setting CALDAV_MCP_TLS_CERT and CALDAV_MCP_TLS_KEY. The server listens on HTTPS directly.

  • Option B: Run behind a TLS-terminating reverse proxy (Traefik, Caddy, nginx).

Without TLS, all traffic — including API keys and CalDAV passwords — is transmitted in plaintext.

Rate limiting

Failed authentication attempts are tracked per client IP using a sliding-window rate limiter with exponential backoff. Defaults: 10 failures per 60-second window. Configurable via CALDAV_MCP_RATE_LIMIT_MAX_FAILURES and CALDAV_MCP_RATE_LIMIT_WINDOW_SECONDS.

Audit logging

All authentication attempts and tool operations are logged. Set CALDAV_MCP_LOG_FORMAT=json for structured JSON output suitable for log aggregation systems.

Deployment recommendations

  • Bind to 127.0.0.1 or a private network unless you need remote access.

  • Restrict access at the network/firewall layer to trusted hosts or a VPN.

  • Never commit CalDAV app passwords to version control.

  • Use a reverse proxy for TLS termination in production.

Configuration reference

All configuration is via environment variables, validated at startup with Pydantic.

Variable

Default

Description

CALDAV_MCP_PORT

8080

Listen port (inside container)

CALDAV_MCP_PATH

/mcp

Streamable HTTP endpoint path

CALDAV_MCP_API_KEY

"" (disabled)

Shared secret for MCP endpoint auth

TZ

"" (UTC)

IANA timezone (e.g. Europe/Vienna) for today/week boundaries

Variable

Default

Description

CALDAV_URL

""

CalDAV server URL (fallback for X-Caldav-Url header)

CALDAV_USERNAME

""

CalDAV username (fallback for X-Caldav-Username header)

CALDAV_PASSWORD

""

CalDAV password (fallback for X-Caldav-Password header)

CALDAV_MCP_CALDAV_VERIFY_SSL

true

Verify TLS certs on CalDAV connections. Set false only for testing with self-signed certs.

Variable

Default

Description

CALDAV_MCP_TLS_CERT

""

Path to TLS certificate PEM file

CALDAV_MCP_TLS_KEY

""

Path to TLS private key PEM file

CALDAV_MCP_TLS_CA_BUNDLE

""

Optional CA bundle for custom certificate authorities

Variable

Default

Description

CALDAV_MCP_RATE_LIMIT_MAX_FAILURES

10

Max failed auth attempts per IP within the sliding window

CALDAV_MCP_RATE_LIMIT_WINDOW_SECONDS

60

Sliding window duration in seconds

Variable

Default

Description

CALDAV_MCP_LOG_FORMAT

text

Audit log format: text or json

Compatibility / limitations

  • Streamable HTTP only — the server uses MCP Streamable HTTP transport. There is no stdio transport. To use stdio, modify server.py to call mcp.run() instead of mcp.run_http_async().

  • Search is client-sidecaldav_search_events fetches all events and filters locally. This works well for small to medium calendars. Very large calendars may experience slower search.

  • Move is non-atomiccaldav_move_event copies the event to the target calendar with a new UID, then deletes the original. A failure after copy leaves a duplicate (the safer failure mode).

  • Published Docker image — prebuilt images are available from GitHub Container Registry. Pull with docker pull ghcr.io/gelse/caldav-mcp:latest or build locally with docker build -t caldav-mcp . / docker compose up --build.

  • No GitHub releases yet — the project is at version 0.1.0.

Setup

python3 -m venv .venv
source .venv/bin/activate
pip install -r requirements.txt
pip install -e ".[dev]"
cp .env.example .env  # configure your CalDAV credentials

Commands

make test              # Run unit tests
make test-integration  # Run integration tests (requires docker-compose.test.yaml)
make test-performance  # Run performance benchmarks
make lint              # Lint with ruff (check + format)
make typecheck         # Type check with mypy
make check             # All checks: lint + typecheck + deps-check + test
make deps-check        # Verify pyproject.toml and requirements.txt are in sync
make build             # Build Docker image

Full contributing guide: docs/contributing.md.

caldav-mcp/
├── server.py                 # Thin entrypoint, launches FastMCP HTTP server
├── caldav_mcp/               # Core package
│   ├── tools/                # MCP tool handlers
│   │   ├── queries.py        #   Read-only tools (7)
│   │   ├── mutations.py      #   Write tools (4)
│   │   └── attendees.py      #   Attendee management (3)
│   ├── auth.py               # Two-layer auth (API key + CalDAV creds)
│   ├── calendar.py           # CalDAV calendar selection & serialization
│   ├── client_cache.py       # Thread-safe LRU cache for DAVClient
│   ├── config.py             # Env var parsing, header constants
│   ├── config_schema.py      # Pydantic startup validation
│   ├── datetime_utils.py     # Date/time parsing, timezone helpers
│   ├── errors.py             # Typed exceptions, ToolResult dataclass
│   ├── event_builder.py      # Pure iCalendar VEVENT construction
│   ├── sanitizers.py         # Input sanitization, field length limits
│   ├── rate_limit.py         # Sliding-window rate limiter
│   ├── audit.py              # Structured JSON audit logging
│   ├── constants.py          # Shared string constants
│   └── types.py              # CalDAVClient Protocol definition
├── tests/                    # Unit, integration, performance
├── docs/                     # Architecture, API, contributing docs
├── Dockerfile                # Multi-stage Docker build
├── docker-compose.yaml       # Production compose
├── docker-compose.test.yaml  # Test compose with Radicale
├── requirements.txt          # Runtime dependencies (pinned)
├── pyproject.toml            # Dev config and dependencies
└── Makefile                  # Build/test shortcuts

Dependencies

Package

Version

Purpose

fastmcp

3.4.7

MCP server framework, Streamable HTTP transport

caldav

3.2.1

CalDAV client library

icalendar

7.2.2

iCalendar RFC 5545 parsing/generation

requests

>=2.28.0

HTTP transport layer

Troubleshooting

Symptom

Cause

Fix

Connection refused

CalDAV server unreachable

Verify CALDAV_URL is correct and the server is running

SSL: CERTIFICATE_VERIFY_FAILED

Self-signed or invalid TLS cert

Import the server's CA into the system trust store, or set CALDAV_MCP_CALDAV_VERIFY_SSL=false for testing

ERROR:[auth] unauthorized

Missing or invalid API token

Set CALDAV_MCP_API_KEY and include Authorization: Bearer <token> in your request

Missing CalDAV credentials

No CalDAV headers or env vars

Provide X-Caldav-* headers or set CALDAV_URL/CALDAV_USERNAME/CALDAV_PASSWORD

Calendar 'X' not found

Typo or wrong calendar name

Run caldav_list_calendars to see available names — they are case-sensitive

Events show wrong time

Server timezone not set

Set the TZ env var to your IANA timezone (e.g. Europe/Vienna)

Contributing

See docs/contributing.md for development setup, code style, and architecture rules.

License

MIT

Tool Schema Changelog

Recent tool additions, removals, and schema changes observed during successful MCP inspections. Dates show when Glama detected each change.

No tool schema history has been recorded yet.

Maintenance

ActivityMaintained
ResponsivenessNo issues

Related MCP Connectors

Related MCP Servers

  • F
    license
    Not graded
    quality
    D
    maintenance
    An MCP server that enables interaction with CalDAV calendars to manage events and check availability through natural language or voice commands. It provides specific tools for listing, searching, and creating calendar entries using an OpenAI-compatible interface.
    -
  • A
    license
    Not graded
    quality
    F
    maintenance
    Provider-agnostic CalDAV calendar MCP server that connects any CalDAV calendar to AI assistants, enabling calendar operations like listing, creating, updating, and deleting events.
    AGPL 3.0

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/gelse/caldav-mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server