Skip to main content
Glama
stefanikostic

iCloud Reminders and Calendar MCP Server

iCloud Reminders and Calendar MCP Server

Lets Claude read and manage your iCloud Reminders and Calendar through a custom MCP connector — ask Claude "what's on my groceries list" or "add milk and eggs" or "What do I need to do today" from any Claude client, including the iPhone app.

Architecture

Claude (iPhone/desktop/web)
        |  HTTPS + API key
        v
Anthropic's cloud infrastructure
        |  HTTPS
        v
EC2 instance (Caddy: auto HTTPS via sslip.io + Let's Encrypt)
        |  localhost
        v
Python MCP server (this repo)
        |  CalDAV
        v
iCloud (your Reminders)

Custom MCP connectors are reached by Anthropic's cloud, not by your device directly — so the server has to be genuinely public, not just reachable on your home network. This deployment gets that for $0/month (within AWS free tier) using:

  • EC2 free tier (750 hrs/month for 12 months) instead of a paid VPS

  • sslip.io for a free "domain" (no purchase, no signup) instead of a real registered domain

  • Caddy for free automatic HTTPS via Let's Encrypt

  • CalDAV to talk to iCloud directly — no Mac, no AppleScript required

Related MCP server: reminders-mcp

Why CalDAV instead of AppleScript

Apple Reminders has no public REST API. AppleScript works but only runs locally on a Mac. CalDAV is an open standard Apple also supports for Calendar/Reminders sync, reachable from any OS or cloud server with just an Apple ID and an app-specific password — which is what makes a Mac-free, cloud-hosted version of this possible at all.


Setup

1. Generate an app-specific Apple ID password

  1. Go to https://appleid.apple.com and sign in

  2. Sign-In and Security → App-Specific Passwords → generate one

  3. Save it — you'll need it in step 4. This is not your normal Apple ID password, and can be revoked independently at any time.

2. Launch an EC2 instance

  1. AWS Console → EC2 → Launch instance

  2. Choose Ubuntu Server (latest LTS), instance type t3.micro or t2.micro (free tier eligible)

  3. Create or select a key pair (for SSH access) — download the .pem file

  4. Security group: allow inbound SSH (22) from your IP, and HTTP (80) + HTTPS (443) from anywhere (0.0.0.0/0) — port 80 is needed briefly for Let's Encrypt's verification, Caddy handles the rest

  5. Launch it

3. Attach an Elastic IP (so the address doesn't change on reboot)

  1. EC2 → Elastic IPs → Allocate Elastic IP address

  2. Actions → Associate → select your instance

  3. Note this IP — you'll need it for the sslip.io hostname

4. SSH in and set up the server

ssh -i your-key.pem ubuntu@YOUR-ELASTIC-IP

sudo apt update
sudo apt install -y python3-venv python3-pip git

git clone https://github.com/YOUR_USERNAME/reminders-mcp.git
cd reminders-mcp

python3 -m venv venv
source venv/bin/activate
pip install -r requirements.txt

cp .env.example .env
nano .env   # fill in APPLE_ID, APPLE_APP_PASSWORD, and a generated MCP_API_KEY

Generate a strong API key with:

python3 -c "import secrets; print(secrets.token_urlsafe(32))"

5. Install and configure Caddy

sudo apt install -y debian-keyring debian-archive-keyring apt-transport-https curl
curl -1sLf 'https://dl.cloudsmith.io/public/caddy/stable/gpg.key' | sudo gpg --dearmor -o /usr/share/keyrings/caddy-stable-archive-keyring.gpg
curl -1sLf 'https://dl.cloudsmith.io/public/caddy/stable/debian.deb.txt' | sudo tee /etc/apt/sources.list.d/caddy-stable.list
sudo apt update
sudo apt install -y caddy

Edit the Caddyfile to use your actual Elastic IP (dots replaced with dashes), e.g. if your IP is 54.123.45.67:

sudo nano /etc/caddy/Caddyfile
54-123-45-67.sslip.io {
    handle_path /REPLACE_WITH_YOUR_MCP_API_KEY/* {
      reverse_proxy localhost:8787 {
        header_up x-api-key "YOUR_MCP_API_KEY"
      }
    }
    respond 404
}
sudo systemctl restart caddy

Caddy will automatically get a free HTTPS certificate for that hostname the first time it starts — no manual steps needed.

6. Set up the systemd service (so it survives reboots)

sudo cp reminders-mcp.service /etc/systemd/system/
sudo systemctl daemon-reload
sudo systemctl enable reminders-mcp
sudo systemctl start reminders-mcp
sudo systemctl status reminders-mcp   # confirm it's running

7. Test it's reachable

curl -i https://54-123-45-67.sslip.io/{YOUR_MCP_API_KEY}/mcp

{YOUR_MCP_API_KEY} will be extracted from the path and Caddy will inject it as a request header. A response other than a connection error means it's working (a 406 here is expected and fine — it just means the request wasn't a full valid MCP handshake, which curl alone won't do).

8. Add the connector on claude.ai

Custom connectors can only be added via a browser or Claude Desktop — not from the iPhone app directly. Once added, it's usable everywhere, including mobile.

  1. Go to claude.ai → Customize → Connectors → "+" → Add custom connector

  2. Name: iCloud Reminders

  3. URL: https://54-123-45-67.sslip.io/{YOUR_MCP_API_KEY}/mcp

  4. Click Add, then enable it in a conversation

9. Use it from your iPhone

Open the Claude app, start a conversation, enable the connector from the "+" menu if it isn't already, and try:

  • "What's on my Groceries list?"

  • "Add milk and eggs to Groceries"

  • "Mark milk as done on Groceries"

  • "What do I need to do today?"


Tools this server exposes

Tool

What it does

list_reminder_lists()

Names of all your iCloud Reminders lists

get_pending_reminders(list_name)

Incomplete items in a given list

add_reminder(list_name, title)

Adds a new reminder to an existing list

complete_reminder(list_name, title)

Marks an item done

Note: add_reminder requires the list to already exist — create it once in the Reminders app on any Apple device and it syncs to iCloud automatically. Reliably creating brand-new top-level lists via CalDAV varies across iCloud accounts, so it's left as a one-time manual step.

Security

  • The server requires a valid YOUR_MCP_API_KEY header in every request path — without it, requests are rejected with 401. Treat this key like a password.

  • Your Apple app-specific password only grants limited access and can be revoked anytime from appleid.apple.com without touching your main password.

  • The EC2 security group only allows inbound 22/80/443 — the MCP server's actual port (8787) is only reachable via localhost, through Caddy.

  • Never commit your real .env file — .gitignore is already set up to exclude it. Only .env.example (placeholder values) should be in git.

Cost

Within the AWS Free Tier (first 12 months): $0/month.

  • EC2 t3.micro/t2.micro: free (750 hrs/month covers 24/7 use)

  • Elastic IP: free while attached to a running instance

  • sslip.io hostname: free, no signup

  • Let's Encrypt certificate via Caddy: free, auto-renewing

After free tier expires, EC2 alone runs roughly $7–9/month if left running continuously — stop the instance when not in use to avoid ongoing charges.

Troubleshooting

  • Caddy fails to get a certificate → confirm port 80 is open in your security group (Let's Encrypt needs it briefly for verification), and that the sslip.io hostname matches your Elastic IP exactly.

  • 401 Unauthorized from your own curl test → double-check the YOUR_MCP_API_KEY in URL https://54-123-45-67.sslip.io/{YOUR_MCP_API_KEY}/mcp matches MCP_API_KEY in .env exactly, no trailing whitespace.

  • Service won't start → sudo journalctl -u reminders-mcp -e to see the actual error; usually a missing/incorrect .env value.

  • Claude says the connector won't connect → confirm you can reach the HTTPS URL from a browser or curl first, before troubleshooting on the Claude side.

Next steps

  • Add a Calendar tool (get_todays_events) using the same CalDAV connection, so Claude can start combining "what's on my calendar" with "what's on my todo list" into a single daily briefing.

Related MCP Connectors

Related MCP Servers