内容
# CalDAV MCP 服务器
[](https://www.typescriptlang.org/)
[](https://github.com/lukegskw/caldav-mcp/actions/workflows/container.yml)
[](https://github.com/lukegskw/caldav-mcp/pkgs/container/caldav-mcp)
[](LICENSE)
**CalDAV MCP 服务器** 是一个用于管理 iCloud 日历事件的模型上下文协议(MCP)服务器,包括对单个事件多个 `VALARM` 提醒的原生支持。
iCloud 日历是唯一官方支持和手动验证的提供商。服务器适用于任何支持 `stdio` 或可流式传输的 HTTP 的 MCP 客户端。
本独立项目与 Apple Inc. 无关,也未获得其授权、赞助或批准。Apple 和 iCloud 是其各自所有者的商标。
## 导航
- [关于](#关于)
- [功能](#功能)
- [MCP 工具](#mcp-工具)
- [技术栈](#技术栈)
- [安装](#安装)
- [配置](#配置)
- [MCP 客户端设置](#mcp-客户端设置)
- [验证](#验证)
- [限制](#限制)
- [贡献](#贡献)
## 关于
服务器通过 CalDAV 连接一个配置的账户到 iCloud。它发现账户的日历并通过 MCP 提供规范化的读写操作。
更新保留完整的 iCalendar 资源,包括未知属性、Apple 扩展、`VTIMEZONE`、重复异常和修补程序中省略的警报。写入使用不透明的资源标识符和 ETags,而不是假设 CalDAV 文件名与事件 UID 匹配。
日历资源在内存中处理。服务器没有遥测数据和应用程序数据库,只有在显式请求时才会返回原始 iCalendar。
## 功能
- 发现配置的 iCloud 账户可用的日历。
- 在半开放时间范围内列出事件并扩展重复事件。
- 创建定时、全天和重复事件。
- 支持每个事件零个、一个或多个显示警报。
- 发出 iCloud 日历预期的 Apple 警报扩展。
- 通过不透明的资源 ID 或日历 ID 和 UID 读取事件。
- 应用部分更新,同时保留省略和未知的 iCalendar 数据。
- 对更新和删除使用 ETags 进行乐观并发控制。
- 拒绝隔离的重复突变,而不是更改完整的系列。
- 从日志和错误中删除凭证、原始日历内容和 CalDAV 路径。
- 以非根容器的只读根文件系统配置运行。
- 支持 `stdio` 和可流式传输的 HTTP MCP 传输。
## MCP 工具
### `list_calendars`
列出为配置的账户发现的日历。每个结果包括不透明的 `calendar_id`、显示名称、描述、时区和最佳写状态。
### `list_events`
在半开放区间内列出事件并扩展重复事件。最大范围为 366 天,默认页面大小为 100,最大页面大小为 500。
示例输入:
```json
{
"calendar_id": "不透明的日历 ID",
"start": "2026-09-01T00:00:00Z",
"end": "2026-10-01T00:00:00Z",
"timezone": "Europe/Berlin",
"limit": 100
}
```
### `get_event`
通过 `resource_id` 或 `calendar_id` 和 UID 对读取事件。默认情况下,原始 iCalendar 被排除,可以通过 `include_raw_ical: true` 请求进行受控诊断。
### `create_event`
创建事件并读取服务器存储的表示。
定时事件,附带两个警报:
```json
{
"calendar_id": "不透明的日历 ID",
"summary": "购买新干线票",
"start": {
"date_time": "2026-09-06T03:00:00+02:00",
"timezone": "Europe/Berlin"
},
"end": {
"date_time": "2026-09-06T03:30:00+02:00",
"timezone": "Europe/Berlin"
},
"description": "Smart-EX",
"location": null,
"alarms": [
{ "minutes_before": 1440, "action": "DISPLAY" },
{ "minutes_before": 0, "action": "DISPLAY" }
],
"rrule": null
}
```
全天事件,附带独家结束日期:
```json
{
"calendar_id": "不透明的日历 ID",
"summary": "旅行",
"start": { "date": "2026-09-06" },
"end": { "date": "2026-09-08" },
"alarms": []
}
```
重复事件接受 RFC 5545 规则,不带 `RRULE:` 前缀:
```json
{
"calendar_id": "不透明的日历 ID",
"summary": "每周规划",
"start": {
"date_time": "2026-09-07T09:00:00+02:00",
"timezone": "Europe/Berlin"
},
"end": {
"date_time": "2026-09-07T09:30:00+02:00",
"timezone": "Europe/Berlin"
},
"rrule": "FREQ=WEEKLY;BYDAY=MO;COUNT=10"
}
```
### `update_event`
修补事件或完整的重复系列。省略的字段被保留,`null` 删除可为空的字段,`alarms: []` 删除所有警报。可选的 `expected_etag` 防止覆盖更新的服务器版本。
### `delete_event`
删除事件或完整的重复系列,可选地需要观察到的 ETag。当前版本不支持删除单个扩展事件。
## 技术栈
- [Node.js 24+](https://nodejs.org/)
- [TypeScript](https://www.typescriptlang.org/) 具有严格的项目规则
- [模型上下文协议 TypeScript SDK](https://github.com/modelcontextprotocol/typescript-sdk)
- [tsdav](https://github.com/natelindev/tsdav)
- [ical.js](https://github.com/kewisch/ical.js)
- [Zod](https://zod.dev/)
- [Vitest](https://vitest.dev/)
- [pnpm](https://pnpm.io/)
- [Docker](https://www.docker.com/)
## 安装
### 先决条件
- 启用日历功能的 iCloud 账户。
- 为 Apple 账户启用两步验证。
- [应用特定密码](https://support.apple.com/en-us/102654)。
- 用于容器部署的 Docker 和 Docker Compose,或用于本地安装的 Node.js 24+ 和 pnpm。
### Docker Compose
推荐的安装使用发布的 multi-architecture 镜像:
```text
ghcr.io/lukegskw/caldav-mcp:latest
```
下载 Compose 示例:
```sh
curl -O https://raw.githubusercontent.com/lukegskw/caldav-mcp/main/compose.example.yaml
```
提供 Apple 账户电子邮件和应用特定密码,然后启动服务:
```sh
export CALDAV_USERNAME='user@example.com'
export CALDAV_PASSWORD='xxxx-xxxx-xxxx-xxxx'
docker compose -f compose.example.yaml up -d
```
要发布不同的主机端口,请设置:
```sh
export CALDAV_MCP_PUBLISHED_PORT=18100
docker compose -f compose.example.yaml up -d
```
`latest` 标签遵循默认分支的最新成功构建。对于受控部署或回滚,请在 Compose 文件中将其替换为发布的版本或不可变的 `sha-*` 标签。
流式 HTTP 端点将在以下位置可用:
```text
http://<host>:8100/mcp
```
主机端口可以更改,而容器内的端口 `8100` 不变。不需要持久卷;日历数据保留在 iCloud 中。
### Docker 运行
可以以相同的安全容器配置启动相同的容器:
```sh
docker run -d \
--name caldav-mcp \
--restart unless-stopped \
--read-only \
--user 10001:10001 \
--cap-drop ALL \
--security-opt no-new-privileges:true \
--tmpfs /tmp:size=16m,mode=1777 \
-e CALDAV_PROVIDER=icloud \
-e CALDAV_USERNAME \
-e CALDAV_PASSWORD \
-e CALDAV_MCP_TRANSPORT=streamable-http \
-e CALDAV_MCP_HOST=0.0.0.0 \
-p 8100:8100 \
ghcr.io/lukegskw/caldav-mcp:latest
```
### 从源代码构建容器
本地构建是可选的。除非您需要修改或审核容器构建,否则请优先使用发布的镜像:
```sh
git clone https://github.com/lukegskw/caldav-mcp.git
cd caldav-mcp
docker buildx build --load -t caldav-mcp:local .
```
### 本地 Node.js 安装
```sh
git clone https://github.com/lukegskw/caldav-mcp.git
cd caldav-mcp
pnpm install --frozen-lockfile
cp .env.example .env
pnpm build
pnpm start -- --transport stdio
```
在 `stdio` 模式下,stdout 专门用于 MCP 消息。要在本地运行流式 HTTP:
```sh
CALDAV_MCP_TRANSPORT=streamable-http pnpm start
```
## 配置
所有设置都使用 `CALDAV_` 或 `CALDAV_MCP_` 前缀。
| 变量 | 必需 | 默认值 | 描述 |
| ------------------------------- | -------- | --------------------------- | ------------------------------------------------- |
| `CALDAV_PROVIDER` | 否 | `icloud` | 提供商策略。iCloud 是支持的配置文件。 |
| `CALDAV_URL` | 否 | `https://caldav.icloud.com` | CalDAV 发现 URL。 |
| `CALDAV_USERNAME` | 是 | 无 | Apple 账户电子邮件。 |
| `CALDAV_PASSWORD` | 是 | 无 | 应用特定密码,而不是账户密码。 |
| `CALDAV_MCP_TRANSPORT` | 否 | `stdio` | `stdio` 或 `streamable-http`。 |
| `CALDAV_MCP_HOST` | 否 | `0.0.0.0` | HTTP 绑定地址。 |
| `CALDAV_MCP_PORT` | 否 | `8100` | HTTP 监听端口。 |
| `CALDAV_MCP_LOG_LEVEL` | 否 | `INFO` | 应用程序日志级别。 |
| `CALDAV_MCP_REQUEST_TIMEOUT_MS` | 否 | `30000` | CalDAV 请求超时。 |
核心保留了一个实验性的 `generic` 提供商策略和可配置的 URL,以将 Apple 扩展与共享的 iCalendar 实现隔离。目前不支持其他提供商。
机密必须通过部署平台或环境提供。永远不要提交 `.env`,通过 MCP 工具参数传递凭证,或在诊断报告中包含它们。
## MCP 客户端设置
对于任何接受流式 HTTP 服务器定义的 MCP 客户端,请配置 URL:
```yaml
mcp_servers:
caldav:
url: http://<host>:8100/mcp
```
如果客户端与 Compose 网络共享,请使用服务名称和内部端口:
```yaml
mcp_servers:
caldav:
url: http://caldav-mcp:8100/mcp
```
对于启动本地 `stdio` 服务器的客户端,请配置命令以运行 `node /absolute/path/to/caldav-mcp/dist/main.js`,并带有必需的环境变量。
配置格式因 MCP 客户端而异。请咨询客户端文档以获取其确切的模式,并在更改服务器定义后重新加载或重启客户端。
## 验证
检查容器状态和日志:
```sh
docker compose -f compose.example.yaml ps
docker compose -f compose.example.yaml logs caldav-mcp
```
容器应报告 `healthy`。TCP 运行状况检查验证服务器进程,而不是 iCloud 凭证。
运行存储库验证套件:
```sh
pnpm install --frozen-lockfile
pnpm format:check
pnpm lint
pnpm typecheck
pnpm test:unit
pnpm test:integration
pnpm build
```
最后,使用 MCP 客户端连接并确认列出了所有六种工具。在发布之前,请针对测试日历运行专用的 [iCloud 手动验证](docs/icloud-manual-test.md)。
## 限制
- 一个 iCloud 账户每个服务器进程或容器配置。
- 流式 HTTP 端点在当前版本中没有身份验证。限制它到受信任的 LAN、VPN 或私有容器网络;不要直接将其暴露在互联网上。
- 单个重复事件是只读的。支持更新或删除完整的系列。
- 仅创建 `ACTION:DISPLAY` 警报。
- 单个 iCalendar 资源限制为 5 MiB。
- 事件列表范围限制为 366 天,页面限制为 500 个结果。
- 事件最多可包含 20 个警报。
- 当前范围外不支持与会者调度。
- 官方不支持 iCloud 以外的提供商。
有关发现、身份验证、ETag 和 Apple 扩展的指导,请参阅 [故障排除](docs/troubleshooting.md)。在报告安全问题或附加诊断之前,请查看 [SECURITY.md](SECURITY.md)。
## 贡献
欢迎贡献。在打开拉取请求之前:
```sh
pnpm install --frozen-lockfile
pnpm format:check
pnpm lint
pnpm typecheck
pnpm test
pnpm build
docker buildx build --load -t caldav-mcp:test .
```
对 CalDAV 写操作或 iCalendar 序列化的更改必须保留 ETag 检查、不透明的资源边界、未知属性、重复异常和修补程序中省略的警报。TypeScript 更改必须继续满足 [.codex/rules/typescript.md](.codex/rules/typescript.md) 中的规则。
## 许可证
MIT。参见 [LICENSE](LICENSE)。