iCloud CalDAV MCP Connector
iCloud MCPコネクタ
iCloudのアプリ固有パスワードを使用して、iCloudサービスをMCP対応クライアント(Claudeカスタムコネクタ、IDEなど)に公開するHTTP Model Context Protocol (MCP) サーバーです。
サポート対象: iCloudカレンダー (CalDAV) + iCloudメール (IMAP/SMTP)。
非公式です。このサービスはAppleのサーバーにiCloudのアプリ固有パスワードを転送するため、非公開にしてください。
なぜこれを作ったのか?
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生成 (summary/descriptionのエスケープ)、±3年間のウィンドウ内でのUIDマッチング
要件
Python 3.11+
Apple ID (メールアドレス形式、電話番号は不可)
iCloud アプリ固有パスワード (取り消し可能) — カレンダーとメールの両方で1つのパスワードを使用
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]
指定された期間内に少なくとも1つのイベントを含むカレンダーのみを返します。
引数
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読み取り専用モード
DR_PROFILE=1 を設定して、Deep Research用の読み取り専用ツールセットを実行します。以下のみが公開されます:
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