
1. 为什么我要给 AI 装上一本真实的日历AI 能写代码、能查资料、能读本地文件但每次让它「帮我看看明天下午有没有空」的时候它只能干瞪眼——因为日历数据在 iCloud 里而大模型本身碰不到。这个缺口就是 MCPModel Context Protocol要补的位置它给模型一套标准化的工具调用协议让模型能通过你写好的 Server 去操作外部系统。我做的这个 iCloud Calendar MCP就是用 TypeScript 写的一个 MCP Server底层走 CalDAV 协议直连 Apple iCloud Calendar。接进支持 MCP 的客户端之后你可以直接用自然语言说「查一下我明天的日程」「明天上午十点建一个 30 分钟的项目讨论」「检查周五下午有没有冲突」模型会把这些话翻译成对日历的真实读写操作。它适合谁如果你是用 Codex、Claude Desktop、Cursor 这类客户端、又希望 AI 助手能真正管理日程的开发者这套东西就是给你准备的。它不依赖 macOS、不依赖 AppleScript、不依赖 Calendar.app只要有 Node.js 环境npx一条命令就能跑起来。下面我把服务端配置骨架、TaoToken 统一 Key 的接入方式以及用 CalDAV 验证增删改查的完整步骤拆开讲。2. TaoToken 前置一把 Key 打通模型通道MCP Server 负责「操作日历」但模型本身得有个稳定的调用通道。我这边统一用 TaoToken 来做模型接入层原因是它把多家模型的 Key 收敛成一套 API 通道MCP 客户端里不用为每个模型单独配环境变量换模型只改一个 model 字段。官网入口在这里https://taotoken.net/?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_content API 基址是 https://taotoken.net/api 这个地址不加 UTM 参数直接填进客户端即可。你需要先拿到一把 API Key。进控制台创建https://taotoken.net/console?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_content 然后在 API Keys 页面生成https://taotoken.net/api-keys?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_content 。生成的 Key 形如sk-开头的一串字符复制下来后面配置里会用到。注意TaoToken 的 Key 和 iCloud 的应用专用密码是两回事。前者是模型通道凭证后者是日历访问凭证两个都要配但用途完全不同别混在一个变量里。如果你打算长期跑编码类或 Agent 类任务可以看下 Coding Planhttps://taotoken.net/coding-plan?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_content 它更适合高频调用的场景。只是想先验证模型能不能正常对话用模型对话页试一下就行https://taotoken.net/models?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_content 。3. 可复制配置MCP 服务端骨架与客户端接入3.1 准备 iCloud 应用专用密码这一步是整条链路里最容易卡住的地方。iCloud 不接受你的 Apple 账户主密码做第三方 CalDAV 登录必须生成「应用专用密码」。打开 Apple ID 账户管理页面找到「登录与安全」里的「应用专用密码」生成一个。系统会给你一串形如xxxx-xxxx-xxxx-xxxx的密码只显示一次立刻记下来。用户名填你的完整 Apple 账户邮箱比如youexample.com。3.2 MCP 客户端配置在支持 MCP 的客户端里加上这段配置。以通用mcpServers结构为例{ mcpServers: { icloud-calendar: { command: npx, args: [-y, icloud-calendar-mcp], env: { ICLOUD_USERNAME: youexample.com, ICLOUD_APP_PASSWORD: xxxx-xxxx-xxxx-xxxx } } } }command用npxargs里-y表示自动确认安装icloud-calendar-mcp就是 npm 上的包名。两个环境变量分别对应 Apple 账户邮箱和应用专用密码。保存后重启客户端MCP Server 会以 stdio 模式启动。3.3 模型通道配置MCP 客户端本身如果支持自定义模型端点把 TaoToken 的 API 基址和 Key 填进去{ model: claude-sonnet-4-20250514, apiKey: sk-你的TaoToken密钥, baseURL: https://taotoken.net/api }baseURL指向 TaoToken 的 API 通道apiKey填刚才在控制台生成的 Key。这样模型调用和日历操作就各走各的通道互不干扰。想换模型只改model字段Key 不用动。3.4 项目本身的关键设计这个 Server 不是简单包一层 CalDAV。日历写入最容易踩的坑是重复创建和并发覆盖所以项目里做了几件事用request_id和稳定 UID 降低重复创建风险用If-None-Match、If-Match和 ETag 做并发控制本地请求日志和可跨进程复用的事件 handle超时、指数退避、Retry-After和写后读取对密码、Authorization、敏感 URL 和事件内容做脱敏。时区、夏令时、全天事件的语义也单独处理过。对于无法安全完成的重复事件修改它会返回明确错误而不是静默把整个系列改错——这点在 RRULE 场景里特别重要。4. 验证请求用 CalDAV 跑通日历增删改查配置好之后先别急着让 AI 操作用命令行直接验证 CalDAV 通道是否通。下面用curl走一遍。4.1 查询日历列表curl -X PROPFIND https://caldav.icloud.com/ \ -u youexample.com:xxxx-xxxx-xxxx-xxxx \ -H Depth: 1 \ -H Content-Type: application/xml \ --data ?xml version1.0? d:propfind xmlns:dDAV: d:prop d:displayname/ d:resourcetype/ /d:prop /d:propfind返回的 XML 里会列出你账户下的所有日历集合每个日历有一个 URL形如https://caldav.icloud.com/12345678/calendars/home/。记下你要操作的那个。4.2 创建一个事件curl -X PUT https://caldav.icloud.com/12345678/calendars/home/test-event.ics \ -u youexample.com:xxxx-xxxx-xxxx-xxxx \ -H Content-Type: text/calendar; charsetutf-8 \ -H If-None-Match: * \ --data BEGIN:VCALENDAR VERSION:2.0 PRODID:-//Test//Test//EN BEGIN:VEVENT UID:test-event-001 DTSTAMP:20250601T000000Z DTSTART:20250602T020000Z DTEND:20250602T023000Z SUMMARY:项目讨论 END:VEVENT END:VCALENDARIf-None-Match: *是关键它保证只有资源不存在时才创建避免重复写入。返回201 Created就说明事件建好了。4.3 查询事件curl -X REPORT https://caldav.icloud.com/12345678/calendars/home/ \ -u youexample.com:xxxx-xxxx-xxxx-xxxx \ -H Depth: 1 \ -H Content-Type: application/xml \ --data ?xml version1.0? c:calendar-query xmlns:dDAV: xmlns:curn:ietf:params:xml:ns:caldav d:prop d:getetag/ c:calendar-data/ /d:prop c:filter c:comp-filter nameVCALENDAR c:comp-filter nameVEVENT/ /c:comp-filter /c:filter /c:calendar-query返回里能看到刚建的事件同时带一个 ETag。这个 ETag 后面更新和删除都要用。4.4 更新与删除更新时带上If-Match和上一步拿到的 ETagcurl -X PUT https://caldav.icloud.com/12345678/calendars/home/test-event.ics \ -u youexample.com:xxxx-xxxx-xxxx-xxxx \ -H Content-Type: text/calendar; charsetutf-8 \ -H If-Match: \上一步的ETag值\ \ --data ...更新后的ICS内容...删除则用 DELETE同样带If-Matchcurl -X DELETE https://caldav.icloud.com/12345678/calendars/home/test-event.ics \ -u youexample.com:xxxx-xxxx-xxxx-xxxx \ -H If-Match: \上一步的ETag值\返回204 No Content就删掉了。这四步跑通说明 CalDAV 通道完全可用MCP Server 里的逻辑就是把这套流程封装成工具调用。4.5 让 AI 真正操作回到 MCP 客户端重启后直接说「查一下我明天的日程」。模型会调用 Server 暴露的查询工具走上面 REPORT 那套逻辑把结果整理成自然语言返回。再说「明天上午十点创建一个 30 分钟的项目讨论」它会走 PUT 逻辑建事件。整个过程你不需要碰 ICS 格式模型帮你翻译。5. 本篇常见错排查401 Unauthorized九成是密码问题。确认用的是应用专用密码而不是 Apple 账户主密码且用户名是完整邮箱。应用专用密码生成后如果没记下来只能重新生成一个。403 Forbidden通常是日历 URL 不对。iCloud 的日历集合 URL 里带一串数字 ID必须从 PROPFIND 返回结果里取不能自己拼。412 Precondition FailedETag 不匹配。说明你手里的 ETag 过期了资源在别处被改过。重新 REPORT 一次拿最新 ETag 再操作。重复创建事件检查 PUT 时有没有带If-None-Match: *。不带的话同一个 UID 可能被写多次。时区错乱ICS 里的时间用 UTCZ结尾本地时间要自己换算。全天事件用VALUEDATE格式别混用。MCP 客户端连不上 Server先确认npx -y icloud-calendar-mcp能单独跑起来再检查客户端配置里的env有没有正确传入。stdio 模式下 Server 的日志会打到 stderr客户端一般能看到。模型通道报错检查 TaoToken 的 Key 和baseURL是否配对baseURL结尾不要多加斜杠。接入文档在这里https://taotoken.net/doc?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_content 。6. 把日历交给 AI 之后跑通这套之后我日常的用法是早上让 AI 汇总当天日程开会前让它检查冲突临时改期直接说一句话。它不会替你决定日程但把「查、建、改、删」这些机械操作接过去了。如果你在排障或接入阶段卡住先去 API Keys 页面确认 Key 状态https://taotoken.net/api-keys?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_content 再对照接入文档核对参数https://taotoken.net/doc?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_content 。想先验证模型通道是否正常用模型对话页发一条消息试试https://taotoken.net/models?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_content 。长期跑编码或 Agent 任务的话Coding Plan 更划算https://taotoken.net/coding-plan?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_content 。项目已经发布到 npm 和 Official MCP Registrynpx -y icloud-calendar-mcp就能用。日历这种每天都要碰的东西交给 AI 管起来之后确实省心不少。