iCloud CalDAV MCP Connector
iCloud MCP 커넥터
iCloud 앱 전용 암호를 사용하여 MCP 지원 클라이언트(예: Claude 커스텀 커넥터, IDE)에 iCloud 서비스를 노출하는 HTTP MCP(Model Context Protocol) 서버입니다.
지원 항목: iCloud 캘린더(CalDAV) + iCloud 메일(IMAP/SMTP).
비공식 서비스입니다. 이 서비스는 iCloud 앱 전용 암호를 Apple 서버로 전달하므로 비공개로 유지하십시오.
개발 동기
Claude 커스텀 커넥터에서 사용하기 위해 만들었습니다. 수동으로 변경하는 대신 iCloud 캘린더를 변경할 수 있도록 하기 위함입니다. TOP Pset 제출 전 금요일 밤에 이 아이디어를 떠올렸고, 재미있는 1일 프로젝트가 되었습니다.
기능
HTTP MCP 서버 (
/mcp) +GET /health캘린더 도구 (기본 쓰기 가능 프로필):
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)
캘린더 도구 (Deep Research 읽기 전용 프로필,
DR_PROFILE=1):search(query)→ 특정 시간 범위 내의 SUMMARY/DESCRIPTION에 대한 기본 텍스트 검색fetch(ids)→ 검색 결과에 대한 원시text/calendarICS 블롭 가져오기
메일 도구 (옵트인,
MAIL_ENABLED=1):list_mailboxes()— 모든 폴더 나열list_messages(mailbox, limit, unread_only)— 헤더와 함께 메시지 나열get_message(uid, mailbox)— 본문을 포함한 전체 메시지 가져오기search_messages(query, mailbox, limit)— IMAP TEXT 검색send_message(to, subject, body, cc?, bcc?)— SMTP를 통해 전송delete_message(uid, mailbox)— 휴지통으로 이동mark_message(uid, mailbox, read)— 읽음/안 읽음 표시
ISO 날짜/시간 입력 (
YYYY-MM-DDTHH:MM:SS, 선택적으로Z또는 시간대 오프셋 포함)최소한의 ICS 생성(요약/설명 이스케이프), ±3년 범위 내의 UID 매칭
요구 사항
Python 3.11+
Apple ID (이메일 식별자, 전화번호 아님)
iCloud 앱 전용 암호 (취소 가능) — 하나의 암호로 캘린더와 메일 모두 사용 가능
https://caldav.icloud.com,imap.mail.me.com,smtp.mail.me.com에 대한 네트워크 액세스
환경 설정
server.py 옆에 .env 파일을 생성하십시오(자동 로드됨):
APPLE_ID=you@example.com # Use your Apple ID email
ICLOUD_APP_PASSWORD=xxxx-xxxx-xxxx-xxxx # App-specific password (works for both calendar and mail)
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 calendar 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)
# Mail (IMAP / SMTP) — optional, disabled by default
MAIL_ENABLED=1 # Set to 1 to enable mail tools
IMAP_HOST=imap.mail.me.com # optional, default shown
IMAP_PORT=993 # optional, default shown
SMTP_HOST=smtp.mail.me.com # optional, default shown
SMTP_PORT=587 # optional, default shown
ICLOUD_TRASH_FOLDER=Deleted Messages # optional, iCloud trash folder name필수 항목: APPLE_ID, ICLOUD_APP_PASSWORD.
빠른 시작 (로컬)
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 # OKMCP 엔드포인트: http://127.0.0.1:8000/mcp
도구 참조 (기능 세부 정보)
list_calendars() -> List[Calendar]
반환값:
name: str | nullurl: str(다른 호출을 위한 기본 식별자)id: str | null
list_calendars_with_events(start, end, expand_recurring=True) -> List[Calendar]
주어진 시간 범위 내에 최소 하나의 이벤트가 포함된 캘린더만 반환합니다.
인자
start, end: str— ISO 날짜/시간; 검색 범위는 [start, end)입니다.expand_recurring: bool— 반복 시리즈를 구체적인 인스턴스로 처리합니다.
반환된 각 캘린더는 list_calendars()와 동일한 형태를 가집니다.
list_events(calendar_name_or_url, start, end, expand_recurring=True) -> List[Event]
인자
calendar_name_or_url: str— 표시 이름 또는 전체 CalDAV URLstart, end: str— ISO 날짜/시간; 검색 범위는 [start, end)입니다.expand_recurring: bool— 반복 시리즈의 구체적인 인스턴스를 포함합니다.
반환값 (각 이벤트):
uid: strsummary: strstart: str(ISO)end: str | null(ISO)raw: str(원시 ICS 텍스트)
create_event(calendar_name_or_url, summary, start, end, tzid?, description?, location?, recurrence?) -> str
최소한의 VEVENT를 생성합니다.
tzid가 생략되면TZID환경 변수가 기본값으로 사용됩니다. 네이티브 날짜/시간은 해당 시간대로 가정되며 UTC로 저장됩니다.description은 선택 사항입니다. 생략하거나null을 전달하여 건너뛸 수 있습니다.location은 선택 사항입니다. 생략하거나null을 전달하여 건너뛸 수 있습니다.recurrence(선택 사항)는 이벤트 반복 방식을 설명합니다. 예:{ "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" }생성된
uid(랜덤 16진수 +@claude-mcp)를 반환합니다.
update_event(calendar_name_or_url, uid, summary?, start?, end?, tzid?, description?, location?, recurrence?, clear_recurrence=False) -> bool
uid로 식별되는 전체 이벤트를 업데이트합니다(반복 이벤트의 경우 단일 인스턴스가 아닌 시리즈 VEVENT를 업데이트합니다).
원래 구성 요소에서 생략된 필드는 보존됩니다.
location:생략된 경우(
null/ 제공되지 않음): 기존 위치를 유지합니다.비어 있지 않은 문자열로 제공된 경우: 이벤트의 위치를 업데이트합니다.
빈 문자열로 제공된 경우: 이벤트의 위치를 지웁니다.
recurrence:제공된 경우:
create_event와 동일한 형식을 사용하여 기존 RRULE을 대체합니다.
clear_recurrence:True인 경우: RRULE을 제거하고 이벤트를 단일 비반복 인스턴스로 변환합니다.True이면서recurrence도 제공된 경우:clear_recurrence가 우선합니다(반복 없음).
성공 시
True, ±3년 범위 내에서uid를 찾을 수 없는 경우False를 반환합니다.
delete_event(calendar_name_or_url, uid) -> bool
±3년 범위 내에서 일치하는 첫 번째 uid를 삭제합니다.
삭제 성공 시
True, 찾을 수 없는 경우False를 반환합니다.
날짜/시간 참고 사항
네이티브 또는
Z/오프셋 날짜/시간(YYYY-MM-DDTHH:MM:SS, 선택적으로Z또는-04:00등)을 허용합니다.신규/편집된 이벤트는 제공된
tzid또는TZID환경 변수를 사용하여DTSTART;TZID=...및DTEND;TZID=...를 출력합니다.업데이트 시 원래 TZID가 있으면 재사용을 시도합니다.
LOCATION은location이 제공되고 비어 있지 않을 때 출력됩니다. 업데이트 시 빈 문자열을 전달하면 기존 위치가 제거됩니다.
메일 도구 참조
MAIL_ENABLED=1로 활성화하십시오. 캘린더와 동일한 APPLE_ID 및 ICLOUD_APP_PASSWORD를 사용합니다. 추가 종속성은 없으며 순수 Python 표준 라이브러리(imaplib, smtplib)를 사용합니다.
list_mailboxes() -> List[{name}]
모든 IMAP 폴더(INBOX, Sent, Drafts, Junk, Deleted Messages 등)를 반환합니다.
list_messages(mailbox="INBOX", limit=20, unread_only=False) -> List[Message]
최대 limit개의 메시지에 대해 최신순으로 헤더를 반환합니다. 각 항목:
uid: str,subject: str,from: str,date: str,read: bool
get_message(uid, mailbox="INBOX") -> Message
디코딩된 본문(text/plain 우선, 대체제로 HTML 제거)을 포함한 전체 메시지를 가져옵니다. 반환값:
uid, subject, from, to, cc, date, body, read
search_messages(query, mailbox="INBOX", limit=20) -> List[Message]
IMAP TEXT 검색 — 제목과 본문을 일치시킵니다. list_messages와 동일한 헤더 필드를 반환합니다.
send_message(to, subject, body, cc=None, bcc=None) -> bool
SMTP(포트 587에서 STARTTLS)를 통해 전송합니다. to와 cc는 쉼표로 구분할 수 있습니다. 성공 시 True를 반환합니다.
delete_message(uid, mailbox="INBOX") -> bool
휴지통(기본값 Deleted Messages, ICLOUD_TRASH_FOLDER로 재정의 가능)으로 복사한 후 삭제합니다. 성공 시 True를 반환합니다.
mark_message(uid, mailbox="INBOX", read=True) -> bool
\Seen 플래그를 설정하거나 해제합니다. 성공 시 True를 반환합니다.
Deep Research 읽기 전용 모드
Deep Research를 위한 읽기 전용 도구 세트를 실행하려면 DR_PROFILE=1로 설정하십시오. 다음만 노출됩니다:
search(query) -> [{ id, title, snippet }]
fetch(ids) -> [{ id, mimeType: 'text/calendar', content }]
예시:
DR_PROFILE=1 HOST=127.0.0.1 PORT=8000 python server.py참고:
이 모드에서는 쓰기 도구(list_events/create_event/update_event/delete_event)가 비활성화됩니다.
SCAN_DAYS는 "현재"를 기준으로 검색 범위를 제어합니다(기본값: 1095일 ≈ 3년).이 서비스를 비공개로 유지하거나 인증을 추가하십시오.
예시 (프로그래밍 방식 클라이언트)
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())배포 / 공개 HTTPS
Claude 커스텀 커넥터와 함께 사용하려면 로컬 서버로 전달되는 공개 HTTPS 엔드포인트가 필요합니다.
다음은 DEPLOY.md를 참조하십시오:
Cloudflare Tunnel (안정적인 호스트 이름, 무료)
ngrok (빠른 테스트)
VPS + Caddy/Nginx (영구적)
보안: 인증을 추가하십시오(Cloudflare Access, Basic Auth 프록시, IP 허용 목록). 인증되지 않은 상태로 노출하지 마십시오. 실시간 캘린더 쓰기 권한이 포함되어 있습니다.
로컬 http://127.0.0.1:8000으로 전달되는 공개 HTTPS URL이 필요합니다.
문제 해결
증상 | 가능한 원인 / 해결 방법 |
| Apple ID 또는 앱 전용 암호가 잘못되었습니다. |
빈 이벤트 결과 | 캘린더 URL 또는 시간 범위가 잘못되었습니다. |
업데이트/삭제 무반응 | UID가 ±3년 스캔 범위에 없거나 쿼리 중인 캘린더와 다릅니다. |
시간대 오차 |
|
보안
앱 전용 암호를 사용하고 필요에 따라 주기적으로 변경하십시오.
이 서버를 비공개로 유지하십시오(터널 ACL, IP 허용 목록, 인증 프록시).
이 프로젝트는 최소한의 VEVENT를 다시 작성합니다. 고급 필드(참석자, 알람, 반복 예외)는 업데이트 시 보존되지 않습니다.
라이선스
MIT 라이선스.
즐거운 일정 관리 되시길 바랍니다. 도움이 되었으면 좋겠습니다!
This server cannot be installed
Resources
Unclaimed servers have limited discoverability.
Looking for Admin?
If you are the server author, to access and configure the admin panel.
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/alexey-max-fedorov/icloud-mcp'
If you have feedback or need assistance with the MCP directory API, please join our Discord server