icloud-mcp
iCloud Calendar HTTP MCP
Install / Use
claude mcp add localhost433 -- npx -y github:localhost433/icloud-mcpIf the server publishes to npm under a different name, use that package instead — check the repo README.
MCP Server
Model Context Protocol server
Quality Score
Category
Development & EngineeringSupported Platforms
Tags
Our assessment of icloud-mcp
icloud-mcp scores 70/100 on our quality scale, 3824th of 4,608 Development & Engineering skills we index.
Its MCP Server is 9.7 KB long, well organised into 22 sections with 4 code examples: a thorough specification that gives an agent plenty to work with.
It has 3 GitHub stars, so there is little community track record yet; judge it on its content.
Maintenance, license and trust
- The repository was last updated 2 days ago, so icloud-mcp is actively maintained.
- It is released under the MIT license, a permissive license that allows use, modification and commercial use with attribution.
- Its trust signals score 87/100, with 2 cautions from licensing, adoption, age or documentation. These come from repository metadata, not a code audit — read the skill file before letting an agent act on it.
icloud-mcp compared with similar skills
All 4 of these similar skills score higher than icloud-mcp; compare them before choosing.
| Skill | Score | Stars | Updated | Format |
|---|---|---|---|---|
| icloud-mcp (this skill)by localhost433 | 70 | 3 | 2d ago | MCP Server |
| Agent-Reachby Panniantong | 100 | 100.1k | today | CLAUDE.md |
| taste-skillby Leonxlnx | 100 | 99.7k | today | CLAUDE.md |
| headroomby headroomlabs-ai | 100 | 76.5k | today | CLAUDE.md |
| ai-job-searchby MadsLorentzen | 100 | 59.2k | today | CLAUDE.md |
Frequently asked questions
- How do I install icloud-mcp?
- Run
claude mcp add localhost433 -- npx -y github:localhost433/icloud-mcp. The install tabs above show the steps for each supported agent. - Which AI agents does icloud-mcp work with?
- It is written for Claude Code and Claude Desktop, as a MCP Server file. Other agents that read the same format can often use it too.
- Is icloud-mcp safe to use?
- It is MIT-licensed and scores 87/100 on trust signals. Skills are instructions an agent will follow, so read the file before installing it and do not approve commands you do not understand.
- Is icloud-mcp still maintained?
- The repository was last updated 2 days ago, so icloud-mcp is actively maintained.
Skill content
View source on GitHubiCloud CalDAV MCP Connector
An HTTP Model Context Protocol (MCP) server exposing iCloud Calendar (CalDAV) tools so MCP-aware clients (e.g., ChatGPT custom connectors, IDEs) can list calendars, read events, and create/update/delete events using an iCloud app-specific password.
Unofficial. Calendar only. Keep this service private; it forwards your iCloud app-specific password to Apple’s CalDAV endpoint.
Why did I build this?
I built this to use in ChatGPT Custom Connector, so I can change my iCloud Calendar compared to changing it manually. Came up with this idea on a Friday night before a TOP Pset was due, and this turned out to be a fun 1-day project.
Features
- HTTP MCP server (
/mcp) +GET /health - Tools (default write-capable profile):
list_calendars()list_calendars_with_events(start, end, expand_recurring=True)list_events(calendar_name_or_url, start, end, expand_recurring=True)create_event(calendar_name_or_url, summary, start, end, tzid?, description?, location?, recurrence?)update_event(calendar_name_or_url, uid, summary?, start?, end?, tzid?, description?, location?, recurrence?, clear_recurrence=False)delete_event(calendar_name_or_url, uid)
- Tools (Deep Research read-only profile):
search(query)→ basic text search over SUMMARY/DESCRIPTION in a time windowfetch(ids)→ fetch rawtext/calendarICS blobs for search results
- ISO datetime input (
YYYY-MM-DDTHH:MM:SS, with optionalZor timezone offset) - Minimal ICS generation (summary/description escaping), UID matching across a ±3-year window
Requirements
- Python 3.11+
- Apple ID (email identity, not phone number)
- iCloud app-specific password (revocable)
- Network access to
https://caldav.icloud.com
Environment
Create a .env next to server.py (auto-loaded):
APPLE_ID=you@example.com # Use your Apple ID email
ICLOUD_APP_PASSWORD=xxxx-xxxx-xxxx-xxxx # App-specific password
CALDAV_URL=https://caldav.icloud.com # optional, default shown
HOST=127.0.0.1 # optional
PORT=8000 # optional
TZID=America/New_York # default TZ for new/edited events
# Deep Research: read-only profile (optional)
DR_PROFILE=0 # Set to 1 to enable DR mode (default 0)
SCAN_DAYS=1095 # Time window (days) scanned by DR search/fetch (default ~3 years)
Required: APPLE_ID, ICLOUD_APP_PASSWORD.
Quick Start (local)
python -m venv .venv
source .venv/bin/activate
pip install -r requirements.txt
# Ensure .env exists (see above), then:
python server.py
# -> Listening on http://127.0.0.1:8000
curl http://127.0.0.1:8000/health # OK
MCP endpoint: http://127.0.0.1:8000/mcp
Tool Reference (functional details)
list_calendars() -> List[Calendar]
Returns:
name: str | nullurl: str(preferred identifier for other calls)id: str | null
list_calendars_with_events(start, end, expand_recurring=True) -> List[Calendar]
Returns only the calendars that contain at least one event in the given time window.
Args
start, end: str— ISO datetimes; search is [start, end)expand_recurring: bool— treat recurring series as concrete instances
Each returned calendar has the same shape as list_calendars().
list_events(calendar_name_or_url, start, end, expand_recurring=True) -> List[Event]
Args
calendar_name_or_url: str— display name or full CalDAV URLstart, end: str— ISO datetimes; search is [start, end)expand_recurring: bool— include concrete instances of recurring series
Returns each event with:
uid: strsummary: strstart: str(ISO)end: str | null(ISO)raw: str(original ICS text)
create_event(calendar_name_or_url, summary, start, end, tzid?, description?, location?, recurrence?) -> str
Creates a minimal VEVENT.
-
tziddefaults toTZIDenv if omitted; naive datetimes are assumed in that zone and stored as UTC. -
descriptionis optional; omit or passnullto skip it. -
locationis optional; omit or passnullto skip it. -
recurrence(optional) describes how the event should repeat, for example:{ "frequency": "weekly", // daily | weekly | monthly | yearly | custom "interval": 1, // optional, default 1 "by_weekday": ["MO", "WE"], // optional; for weekly/custom "by_monthday": [1, 15], // optional; for monthly/custom "end": { // optional end condition "type": "on_date", // or "after_occurrences" "date": "2025-12-31" // when type == "on_date" // or: "count": 10 // when type == "after_occurrences" } // for custom frequency you can pass a raw RRULE: // "frequency": "custom", // "rrule": "FREQ=MONTHLY;BYDAY=MO,TU;BYSETPOS=1" } -
Returns the generated
uid(random hex +@chatgpt-mcp).
update_event(calendar_name_or_url, uid, summary?, start?, end?, tzid?, description?, location?, recurrence?, clear_recurrence=False) -> bool
Updates the whole event identified by uid (for recurring events this updates the series VEVENT, not a single instance).
- Preserves any omitted fields from the original component.
location:- If omitted (
null/ not provided), keeps the existing location. - If provided as a non-empty string, updates the event’s location.
- If provided as an empty string, clears the event’s location.
- If omitted (
recurrence:- If provided, replaces any existing RRULE using the same shape as in
create_event.
- If provided, replaces any existing RRULE using the same shape as in
clear_recurrence:- If
True, removes any RRULE and converts the event back to a single non-recurring instance. - If
Trueandrecurrenceis also provided,clear_recurrencewins (no recurrence).
- If
- Returns
Trueon success,Falseifuidnot found in ±3-year window.
delete_event(calendar_name_or_url, uid) -> bool
Deletes the first matching uid in a ±3-year window.
- Returns
Trueif deleted,Falseif not found.
Date/Time Notes
- Accepts naive or
Z/offset datetimes (YYYY-MM-DDTHH:MM:SS, optionallyZor-04:00etc.) - New/edited events emit
DTSTART;TZID=...andDTEND;TZID=...using providedtzidorTZIDenv - Updates attempt to reuse the original TZID when present
LOCATIONis emitted whenlocationis provided and non-empty; passing an empty string when updating an event removes the existing location.
Deep Research read-only mode
Set DR_PROFILE=1 to run a read-only tool set for Deep Research. This exposes only:
- search(query) -> [{ id, title, snippet }]
- fetch(ids) -> [{ id, mimeType: 'text/calendar', content }]
Example:
DR_PROFILE=1 HOST=127.0.0.1 PORT=8000 python server.py
Notes:
- Write tools (list_events/create_event/update_event/delete_event) are disabled in this mode.
- SCAN_DAYS controls the search window around “now” (default: 1095 days ≈ 3 years).
- Keep this service private or add auth
Example (programmatic client)
import asyncio, json
from fastmcp import Client
MCP_URL = "http://127.0.0.1:8000/mcp"
CAL_URL = "<paste one of your calendar URLs>"
def unwrap(res):
sc = getattr(res, "structured_content", None)
if isinstance(sc, dict) and "result" in sc:
return sc["result"]
return json.loads(res.content[0].text)
async def main():
async with Client(MCP_URL) as c:
cals = unwrap(await c.call_tool("list_calendars", {"confirm": True}))
print("Calendars:", cals[:2])
evs = unwrap(await c.call_tool("list_events", {
"calendar_name_or_url": CAL_URL,
"start": "2025-09-01T00:00:00",
"end": "2025-10-01T00:00:00",
"expand_recurring": True
}))
print("Events:", len(evs))
uid = unwrap(await c.call_tool("create_event", {
"calendar_name_or_url": CAL_URL,
"summary":"Demo",
"start":"2025-09-29T15:00:00",
"end":"2025-09-29T15:30:00",
"tzid":"America/New_York",
"location": "Bobst Library"
}))
print("Created:", uid)
asyncio.run(main())
Deployment / Public HTTPS
To use this with ChatGPT Custom Connectors you need a public HTTPS endpoint that forwards to your local server.
See DEPLOY.md for:
- Cloudflare Tunnel (stable hostname, free)
- ngrok (quick test)
- VPS + Caddy/Nginx (permanent)
Security: add auth (Cloudflare Access, Basic Auth proxy, IP allowlist). Do NOT expose this unauthenticated; it holds live calendar write access.
You need a public HTTPS URL that forwards to your local http://127.0.0.1:8000.
Troubleshooting
| Symptom | Likely Cause / Fix |
| -------------------- | --------------------------------------------------------------------------------- |
| 401 Unauthorized | Wrong Apple ID or app-specific password; ensure .env uses email, not phone. |
| Empty event results | Wrong calendar URL or time window; remember end is exclusive. |
| Update/Delete no-ops | UID not in ±3-year scan window or different calendar than you’re querying. |
| Timezone drift | Pass tzid explicitly (e.g., America/New_York) or use UTC ...Z. |
Security
- Use app-specific passwords and rotate as needed
- Keep this server private (tunnel ACLs, IP allowlists, auth proxy)
- This project rewrites minimal VEVENTs; advanced fields (attendees, alarms, recurrence exceptions) are not preserved on update
License
MIT License.
Happy scheduling, I hope this helps!
Related Skills
Agent-Reach
100.1kGive your AI agent eyes to see the entire internet. Read & search Twitter, Reddit, YouTube, GitHub, Bilibili, XiaoHongShu — one CLI, zero API fees.
taste-skill
99.7kAgent skills that give coding agents taste: art direction, reference images and a browser check instead of generic AI pages
headroom
76.5kCompress tool outputs, logs, files, and RAG chunks before they reach the LLM. 20% fewer tokens for coding agents, 60-95% fewer tokens for JSON, same answers. Library, proxy, MCP server.
ai-job-search
59.2kThe job search that runs on your machine. AI job application framework built on Claude Code: evaluate postings, tailor CVs, write cover letters, prep interviews. Fork it and own it.
Languages
Trust signals
From repository metadata: license, adoption, age and documentation. Not a code audit — see the Safety scan above for what the skill file itself contains.
