A Model Context Protocol server that gives AI assistants full access to Apple iCloud Calendar through CalDAV. List calendars, search events, create, update, and delete events -- all from Claude or any MCP-compatible client.
Built with Go and the mcp-go SDK. Ships as a single static binary for Linux, macOS, and Windows.
- Features
- Quick Start
- Prerequisites
- Installation
- Configuration
- Usage with Claude Desktop
- Available Tools
- Development
- Architecture
- Security
- Troubleshooting
- Contributing
- License
Calendar Operations
- List all iCloud calendars with paths, names, descriptions, and colors
- Search events with date range filters and pagination
- Create events with title, time, description, location, and attendees
- Update individual fields on existing events (partial update with pointer fields)
- Delete events permanently
Recurring Events & Attendees
- Expand recurring events (RRULE) into individual occurrences within a date range
- Manage attendees with roles (CHAIR, REQ-PARTICIPANT, OPT-PARTICIPANT) and statuses
Multi-Account Support
- Manage multiple iCloud accounts from a single server instance
- Configure via
ACCOUNTS_FILEJSON or single-account environment variables - Per-account rate limiting and retry logic
Operational
- Structured JSON logging with UUID request correlation
- Configurable timeout middleware on every tool call (default 25s)
- Automatic retry with exponential backoff for transient failures
- Rate limiting per account to avoid iCloud throttling
- Health endpoint (
/healthz,/readyz) and Prometheus metrics (/metrics) - Audit logging for mutating operations (no PII)
- Input validation for all tool parameters
- MCP tool annotations (read-only, destructive, idempotent) for client-side safety
- Graceful shutdown on SIGTERM/SIGINT
- mTLS and custom CA support for enterprise deployments
file://credential loading for Docker/Kubernetes secrets- CI pipeline with tests, linting, and vulnerability scanning
# Install
go install github.com/rgabriel/mcp-icloud-calendar@latest
# Set credentials (app-specific password, not your main iCloud password)
export ICLOUD_EMAIL="you@icloud.com"
export ICLOUD_PASSWORD="xxxx-xxxx-xxxx-xxxx"
# Run
mcp-icloud-calendarOr download a prebuilt binary from the Releases page.
- Go 1.21+ -- install (only needed when building from source)
- iCloud account with two-factor authentication enabled
- App-specific password -- required for CalDAV access
- Go to appleid.apple.com and sign in
- Navigate to Sign-In and Security > App-Specific Passwords
- Click Generate an app-specific password
- Enter a label (e.g. "MCP Calendar Server") and click Create
- Copy the generated password (
xxxx-xxxx-xxxx-xxxx) and store it securely
Notes:
- Your Apple ID must have two-factor authentication enabled
- You can create up to 25 active app-specific passwords
- Changing your main Apple ID password revokes all app-specific passwords
- Never use your main iCloud password for CalDAV access
git clone https://github.com/rgabriel/mcp-icloud-calendar.git
cd mcp-icloud-calendar
make buildgo install github.com/rgabriel/mcp-icloud-calendar@latestdocker build -t mcp-icloud-calendar .
docker run \
-e ICLOUD_EMAIL="you@icloud.com" \
-e ICLOUD_PASSWORD="xxxx-xxxx-xxxx-xxxx" \
mcp-icloud-calendarThe Docker image uses a multi-stage build with a distroless base image and runs as a non-root user.
Download the binary for your platform from the Releases page. Binaries are available for:
| Platform | Architecture | Binary |
|---|---|---|
| Linux | x86_64 | mcp-icloud-calendar-linux-amd64 |
| Linux | ARM64 | mcp-icloud-calendar-linux-arm64 |
| macOS | Intel | mcp-icloud-calendar-macos-amd64 |
| macOS | Apple Silicon | mcp-icloud-calendar-macos-arm64 |
| Windows | x86_64 | mcp-icloud-calendar-windows-amd64.exe |
SHA256 checksums are provided alongside each binary.
The server requires two environment variables at minimum:
| Variable | Required | Default | Description |
|---|---|---|---|
ICLOUD_EMAIL |
Yes | Your iCloud email address (Apple ID) | |
ICLOUD_PASSWORD |
Yes | App-specific password from appleid.apple.com | |
ICLOUD_CALENDAR_ID |
No | Default calendar path (e.g., /1234567/calendars/home/) |
|
LOG_LEVEL |
No | INFO |
Logging verbosity: DEBUG, INFO, WARN, ERROR |
TOOL_TIMEOUT |
No | 25s |
Timeout per tool call (Go duration, e.g., 30s, 1m) |
MAX_RETRIES |
No | 3 |
Retry attempts for transient CalDAV failures |
RETRY_BASE_DELAY |
No | 1s |
Base delay for exponential backoff |
RATE_LIMIT_RPS |
No | 10 |
CalDAV requests per second per account |
RATE_LIMIT_BURST |
No | 20 |
Burst allowance for rate limiter |
MAX_CONNS_PER_HOST |
No | 10 |
Max HTTP connections to iCloud per account |
HEALTH_PORT |
No | Port for health/metrics HTTP server (e.g., 8080) |
|
TLS_CERT_FILE |
No | Client TLS certificate for mTLS | |
TLS_KEY_FILE |
No | Client TLS key for mTLS | |
TLS_CA_FILE |
No | Custom CA certificate |
You can set these as environment variables or place them in a .env file:
cp .env.example .env
# Edit .env with your credentialsCredentials support file:// prefixes for Docker/Kubernetes secrets (e.g., ICLOUD_PASSWORD=file:///run/secrets/password).
To manage multiple iCloud accounts, set the ACCOUNTS_FILE environment variable pointing to a JSON file:
{
"accounts": [
{
"name": "personal",
"email": "personal@icloud.com",
"password": "xxxx-xxxx-xxxx-xxxx",
"calendarId": "/1234567/calendars/home/"
},
{
"name": "work",
"email": "work@icloud.com",
"password": "yyyy-yyyy-yyyy-yyyy"
}
]
}Each tool accepts an optional account parameter. Omit it to use the default account.
Add the server to your Claude Desktop configuration file.
macOS -- ~/Library/Application Support/Claude/claude_desktop_config.json
Linux -- ~/.config/claude/claude_desktop_config.json
Windows -- %APPDATA%\Claude\claude_desktop_config.json
{
"mcpServers": {
"icloud-calendar": {
"command": "/path/to/mcp-icloud-calendar",
"env": {
"ICLOUD_EMAIL": "you@icloud.com",
"ICLOUD_PASSWORD": "xxxx-xxxx-xxxx-xxxx"
}
}
}
}Restart Claude Desktop after saving.
The server exposes 5 MCP tools. Each tool includes schema constraints and annotations indicating whether it is read-only, destructive, or idempotent.
List all available iCloud calendars. Returns each calendar's path, display name, description, and color. Call this first to discover valid calendarId values.
| Parameter | Type | Default | Description |
|---|---|---|---|
account |
string | Account name for multi-account setups |
Search for calendar events within a date range. Returns paginated results with event details including recurrence info and attendees.
| Parameter | Type | Default | Description |
|---|---|---|---|
account |
string | Account name for multi-account setups | |
calendarId |
string | (server default) | Calendar path from list_calendars |
startTime |
string | Start of date range (RFC 3339, e.g., 2025-03-01T00:00:00Z) |
|
endTime |
string | End of date range (RFC 3339) | |
limit |
number | 50 |
Max events to return (1-500) |
offset |
number | 0 |
Events to skip for pagination |
expandRecurrence |
boolean | false |
Expand recurring events into individual occurrences (requires both startTime and endTime) |
Create a new calendar event. Returns the created event's unique ID.
| Parameter | Type | Default | Description |
|---|---|---|---|
account |
string | Account name for multi-account setups | |
title |
string | (required) | Event title or summary |
startTime |
string | (required) | Start time (RFC 3339) |
endTime |
string | (required) | End time (RFC 3339) |
description |
string | Event description or notes | |
location |
string | Event location | |
calendarId |
string | (server default) | Calendar path to create the event in |
attendees |
string | JSON array of attendee objects (see below) |
Attendee format:
[
{"email": "alice@example.com", "name": "Alice", "role": "REQ-PARTICIPANT"},
{"email": "bob@example.com", "name": "Bob", "role": "OPT-PARTICIPANT", "status": "TENTATIVE"}
]Supported roles: CHAIR, REQ-PARTICIPANT, OPT-PARTICIPANT. Supported statuses: NEEDS-ACTION, ACCEPTED, DECLINED, TENTATIVE.
Update specific fields of an existing event. Only include the fields you want to change -- omitted fields remain unchanged.
| Parameter | Type | Default | Description |
|---|---|---|---|
account |
string | Account name for multi-account setups | |
eventId |
string | (required) | Event ID (UID) from search_events |
calendarId |
string | (server default) | Calendar path containing the event |
title |
string | Updated title | |
description |
string | Updated description | |
location |
string | Updated location | |
startTime |
string | Updated start time (RFC 3339) | |
endTime |
string | Updated end time (RFC 3339) |
Permanently delete a calendar event. This action cannot be undone.
| Parameter | Type | Default | Description |
|---|---|---|---|
account |
string | Account name for multi-account setups | |
eventId |
string | (required) | Event ID (UID) from search_events |
calendarId |
string | (required) | Calendar path containing the event |
make build # Build binary with version embedding
make test # Run tests with race detector
make lint # Run golangci-lint
make clean # Remove build artifacts
make docker # Build Docker image
make run # Build and runexport ICLOUD_EMAIL="you@icloud.com"
export ICLOUD_PASSWORD="xxxx-xxxx-xxxx-xxxx"
make runThe project includes 88 table-driven tests across 14 test files, covering all tool handlers, CalDAV client logic, input validation, retry/rate-limiting wrappers, recurrence expansion, attendee parsing, and error paths. Tests use mock implementations of the CalendarService interface -- no live CalDAV connection required.
make testUse the MCP Inspector to interactively test the server:
npx @modelcontextprotocol/inspector mcp-icloud-calendarEvery push to main or dev and every pull request runs:
go vetandgo test -race-- correctness and data race detectiongolangci-lint-- static analysis (errcheck, govet, staticcheck, gosec, gocritic, and more)govulncheck-- known vulnerability scanning
Tagged releases (v*.*.*) trigger automated cross-platform builds with SHA256 checksums.
mcp-icloud-calendar/
main.go Server setup, multi-account init, tool registration, middleware chain
config/
config.go Environment variable loading, validation, file:// credential support
accounts.go Multi-account JSON configuration
caldav/
interface.go CalendarService interface
client.go CalDAV client (caldav.icloud.com, TLS/mTLS)
retry.go Retry wrapper with exponential backoff
ratelimit.go Rate-limiting wrapper (token bucket)
recurrence.go RRULE expansion for recurring events
attendees.go Attendee parsing and serialization
validation.go Input validation for CalDAV parameters
tools/
accounts.go AccountClients multi-account resolver
list_calendars.go list_calendars handler
search_events.go search_events handler
create_event.go create_event handler
update_event.go update_event handler
delete_event.go delete_event handler
health/server.go Health check and readiness endpoints
metrics/ Prometheus metrics and tool call middleware
middleware/ Request ID middleware (UUID correlation)
logging/ Structured JSON logging (slog)
Client chain: Each account gets its own pipeline: realClient -> RateLimitedClient -> RetryClient
Middleware chain: Each tool call passes through RequestID -> Timeout -> Metrics -> handler. The request ID middleware assigns a UUID for log correlation. The timeout middleware enforces a configurable deadline. The metrics middleware records tool call duration and outcome.
Audit logging: Mutating operations (create_event, update_event, delete_event) are logged via a post-call hook with tool name, account, calendar ID, and status -- no PII (titles, descriptions, locations) is included.
| Package | Purpose |
|---|---|
| mcp-go | MCP SDK -- tool registration, stdio transport |
| go-webdav | CalDAV protocol client |
| go-ical | iCalendar (RFC 5545) parsing |
| rrule-go | Recurrence rule expansion |
| godotenv | .env file loading |
| uuid | Event UID and request ID generation |
| prometheus/client_golang | Prometheus metrics |
| x/time/rate | Token bucket rate limiter |
- App-specific passwords only -- never accepts or stores your main iCloud password
- TLS everywhere -- all CalDAV communication uses HTTPS with TLS verification
- mTLS support -- optional client certificate authentication for enterprise environments
- Input validation -- all tool parameters are validated for type, range, and format
- Size and format limits -- title length, time range, and parameter constraints enforced via MCP schema
- Credential file loading --
file://prefix for secure secret injection (Docker, Kubernetes) - Distroless Docker image -- minimal attack surface, runs as non-root
- No third-party data sharing -- the server runs locally and communicates only with iCloud servers
- Revocable access -- app-specific passwords can be revoked at any time from appleid.apple.com
- Audit trail -- mutating operations are logged without PII for compliance
Never commit your .env file to version control. The .gitignore already excludes it.
- Verify you are using an app-specific password, not your main iCloud password
- Check that two-factor authentication is enabled on your Apple ID
- Regenerate a new app-specific password at appleid.apple.com
- Confirm your email address matches your Apple ID
- Run
list_calendarsto see the exact calendar paths your account has - Calendar paths look like
/1234567/calendars/home/-- always start with/ - Make sure you are using the
pathvalue fromlist_calendars, not the display name
- Use RFC 3339 / ISO 8601:
2025-01-15T14:30:00Z - Include timezone offset if not UTC:
2025-01-15T14:30:00-05:00
- Check your internet connection
- Reduce the
limitparameter for large result sets - Use narrower date ranges with
startTime/endTime - Increase
TOOL_TIMEOUTif your network is slow (default: 25s)
- Set
expandRecurrencetotrueinsearch_events - Both
startTimeandendTimemust be provided for recurrence expansion - Expansion only works within the specified date range
- Verify the event ID matches a UID from
search_events - Ensure you are using the correct
calendarId - The event may have been deleted or moved since the ID was retrieved
Contributions are welcome. Please open an issue to discuss larger changes before submitting a pull request.
MIT License -- see LICENSE for details.