内容
# apple-calendar-mcp
> 一个快速、原生的macOS日历工具。单个二进制文件既是`ical`终端命令,又是MCP服务器——因此任何兼容MCP的应用程序都可以读取和更改您的日历,本地或从私人网络中的另一台机器。
它直接通过EventKit(约100毫秒每次查询)与macOS日历数据库交互,因此Calendar.app永远不需要打开,并且没有任何数据离开您的机器。
- 🗓️ **即时查询** — 今天、本周、特定日历、接下来的N天。
- ✏️ **创建、更新和删除事件** — 从CLI或任何MCP客户端。
- 🔌 **两种连接方式** — 本地`stdio`服务器,或其他机器可以访问的HTTP服务器。
- 🔐 **每个客户端的令牌** — 每个机器都有自己的可撤销凭证;如果没有令牌,服务器拒绝启动。
- 🍺 **一键安装** — Homebrew构建、代码签名,并(可选)为您运行。
---
## 您需要哪种模式?
- **同一台Mac**(AI应用程序运行在日历所在的位置)→ **本地(`stdio`)**。一个JSON块,无令牌,无网络。跳转到[本地设置](#模式1--本地-stdio)。
- **另一台机器**(笔记本电脑、服务器、手机、远程Claude Code)→ **网络(HTTP)**。一个命令即可设置:`ical serve setup`。跳转到[网络设置](#模式2--网络-http)。
---
## 目录
- [您需要哪种模式?](#您需要哪种模式?)
- [要求](#要求)
- [安装](#安装)
- [使用`ical`命令](#使用ical命令)
- [使用MCP服务器](#使用mcp服务器)
- [从另一台机器连接](#从另一台机器连接)
- [在VPN上安全运行](#在vpn上安全运行)
- [手动/高级设置](#手动高级设置)
- [配置参考](#配置参考)
- [安全模型](#安全模型)
- [从源代码构建](#从源代码构建)
- [工作原理](#工作原理)
- [许可证](#许可证)
---
## 要求
- **macOS 14(Sonoma)或更高版本** — 它使用EventKit的完全访问API。
- **Homebrew**,以及Xcode **命令行工具** (`xcode-select --install`)。不需要完整的Xcode。
---
## 安装
```bash
brew install hunterbrewer04/tap/apple-calendar
```
这会从源代码构建二进制文件,将其安装为`ical`,并**使用稳定标识符签名**,以便macOS日历权限在升级过程中保持有效。
**第一次**该工具实际读取您的日历时,macOS会显示一个一次性 **“允许完全访问”** 对话框 — 点击它。(安装本身期间不会提示任何内容。)
> **注意:** 如果您已经在`PATH`中安装了另一个`ical`,Homebrew会告诉您它被“遮蔽”。请删除旧的,或者通过其完整路径调用此构建(`$(brew --prefix)/bin/ical`)。
---
## 使用`ical`命令
```
$ ical today
Jun 19 9:00 AM – 9:15 AM Standup
📅 Work
Jun 19 12:30 PM – 1:30 PM Lunch with Alex
📍 Cafe Roma
📅 Personal
Jun 19 all day Q3 Planning Offsite
📅 Work
```
| 命令 | 显示 |
|---|---|
| `ical` / `ical today` | 今天(默认) |
| `ical tomorrow` | 明天 |
| `ical week` | 接下来的7天 |
| `ical month` | 接下来的30天 |
| `ical next 14` | 接下来的*N*天 |
| `ical calendars` | 所有日历的名称 |
| `ical cal "Work" 14` | 按名称查找日历(可选天数,默认7) |
| `ical detail week` | 任意时间段,包含备注+网址 |
| `ical debug today` | 原始的、按管道分隔的输出(用于脚本) |
在任何命令中添加 **`-x`** (或 `--detail`),以包含事件备注和网址(以及事件 **id**,`edit` / `rm` 需要)。
### 创建和更改事件
```bash
# 添加定时事件
ical add --title "Standup" --start 2026-07-01T09:00 --end 2026-07-01T09:15 --cal "Work"
# 添加全天事件
ical add --title "Offsite" --start 2026-07-01 --all-day --cal "Work"
# 查找事件的id,然后编辑或删除它
ical detail today # 注意 🆔 行
ical edit <id> --title "Standup (moved)" --start 2026-07-01T09:30 --end 2026-07-01T09:45
ical rm <id>
```
| 命令 | 执行操作 |
|---|---|
| `ical add --title T --start ISO [--end ISO] [--all-day] [--cal NAME] [--location L] [--notes N] [--url U]` | 创建事件 |
| `ical edit ID [--title …] [--start …] [--end …] [--all-day] [--cal …] [--location …] [--notes …] [--url …]` | 更改给定字段 |
| `ical rm ID` | 删除事件 |
日期为ISO-8601格式(`2026-07-01T14:30`);对于 `--all-day` 事件,请传递纯日期(`2026-07-01`)。`edit` 仅更改您传递的字段,而不影响其他字段。
> 循环事件会扩展为单个事件,一个多日事件如果与窗口重叠,则会显示(带有其原始开始日期)。
---
## 使用MCP服务器
同一个二进制文件支持 **模型上下文协议(MCP)**,因此AI助手或任何MCP兼容应用程序都可以将其作为工具调用。有两种模式。
### 模式1 — 本地(`stdio`)
适用于在 **同一台Mac** 上运行应用程序。应用程序启动二进制文件,并通过其标准输入/输出与其通信 — 无需网络,无需令牌。将以下内容添加到您的MCP客户端配置文件中:
```json
{
"mcpServers": {
"apple-calendar": { "type": "stdio", "command": "ical", "args": ["mcp"] }
}
}
```
### 模式2 — 网络(HTTP)
适用于运行在您私人网络中的另一台机器上的应用程序(参见 [VPN 部分](#在vpn上安全运行) — 绝不能暴露在开放的互联网上)。一个命令即可完成所有操作:
```bash
brew install hunterbrewer04/tap/apple-calendar
ical serve setup --tailscale # 或:--host <your-private-ip> (裸露 = 仅环回)
```
`serve setup` 生成一个令牌(保存到 `~/.config/apple-calendar/token`,chmod 600),
安装一个绑定到您选择的地址的后台LaunchAgent,启动它,并打印出可以在另一台机器上粘贴的确切客户端配置。
它 **在重启和 `brew upgrade` 后仍然存活** — 无需 `launchctl setenv`,无需重新执行任何操作。
管理它随时:
| 命令 | 执行操作 |
|---|---|
| `ical serve status` | 是否启动?(期望 `401` = 启动 + 强制身份验证)+ 重新打印客户端配置 |
| `ical serve token` | 打印 **默认** 令牌(`ical serve token \| pbcopy`) |
| `ical serve token add <client> [--force]` | 为命名客户端生成令牌(如果已存在则错误,除非 `--force`) |
| `ical serve token show <client>` | 打印客户端的令牌(`ical serve token show brewserver \| pbcopy`) |
| `ical serve token revoke <client>` | 删除客户端的令牌 — 该机器在 ~5 秒内失去访问权限 |
| `ical serve token list` | 列出每个客户端 + `sha256:` 指纹(从不打印原始令牌) |
| `ical serve connect <ssh-host>` | 一键设置远程机器的Claude Code |
| `ical serve uninstall` | 停止并删除后台服务器(`--purge` 也会删除所有令牌) |
> HTTP服务器是 **关闭失败的**:如果没有可解析的令牌,它会拒绝启动。(您可以传递 `--no-auth` 来覆盖,但在完全受信任的隔离接口上只能这样做。)
日志记录到 `~/Library/Logs/apple-calendar.log`。
### 可用工具
> HTTP服务器处理 **多个并发客户端和重新连接** — 每个MCP会话都是隔离的 — 因此多个应用程序(和重复的连接/断开循环)可以同时指向它。
| 工具 | 执行操作 |
|---|---|
| `list_calendars` | 所有日历的名称 |
| `get_today` / `get_tomorrow` | 单日 |
| `get_week` / `get_month` | 接下来的 7 / 30 天 |
| `get_next_days(days)` | 接下来的 *N* 天 |
| `get_calendar_events(calendar_name, days)` | 按名称查找日历 |
| `create_event(title, start, end, …)` | 创建新事件 |
| `update_event(event_id, …)` | 更改现有事件的字段 |
| `delete_event(event_id)` | 删除事件 |
读取工具接受 `details: true` 以包含备注、网址和每个事件的 **id**。写入工具采用ISO-8601日期(`2026-07-01T14:30`;纯日期如 `2026-07-01` 表示 `all_day` 事件);
`update_event` / `delete_event` 通过 `id` 引用事件,该 `id` 在 `details: true` 列表中返回。
`create_event` 和 `update_event` 还接受 `calendar_name` 、 `location` 、 `notes` 和 `url`。
---
## 从另一台机器连接
您可以从笔记本电脑、服务器或手机读取Mac的日历 — 只要两台设备都在同一个 **私人网络** 上(参见下面的VPN部分;绝不能暴露在开放的互联网上)。
有两个部分需要设置。
### 在Mac(服务器)上
运行一个命令 — 它生成令牌,绑定到您选择的私有网络地址,并启动后台服务器(参见 [模式2](#模式2--网络-http)):
```bash
ical serve setup --tailscale # 或 --host <your-private-ip> (使用VPN时,这是您的VPN IP)
```
它打印令牌和可以直接粘贴的客户端配置。使用 `ical serve token` 再次获取令牌。
### 在另一台机器(客户端)上
如果Claude Code运行客户端,并且您有 **基于密钥的ssh**,请在 **从Mac** 上用一个命令连接 — 无需在另一侧编辑配置:
```bash
ical serve connect brewserver # 任何ssh主机或~/.ssh/config别名
```
它生成一个 **针对该ssh主机的令牌** — 而不是共享的默认令牌 — 然后通过ssh在该主机的Claude Code中安装apple-calendar MCP服务器,并从该主机探测服务器以确认服务器可从该机器访问并且强制执行身份验证。令牌仅通过ssh通道传输。在没有基于密钥的ssh的主机上,运行 `ical serve connect <host> --print` 以打印 `claude mcp add …` 命令,在那里粘贴 — 它使用相同的每个主机令牌构建。
由于每个机器都有自己的凭证,您可以切断一个而无需触及其余部分:
`ical serve token revoke <host>` 删除只是该主机的令牌,并且运行中的服务器在 ~5 秒内拒绝它 — 所有其他连接的客户端继续工作。
否则 — 没有ssh,或客户端不是Claude Code — 通过 `ical serve token add <client>`(参见上面的 `ical serve` 表)为客户端生成其自己的令牌,然后将MCP客户端指向Mac的地址,并将令牌作为承载头包含:
```json
{
"mcpServers": {
"apple-calendar": {
"type": "http",
"url": "http://YOUR-MAC-IP:3456/mcp",
"headers": { "Authorization": "Bearer YOUR-TOKEN" }
}
}
}
```
可以在 [`examples/mcp-config.json`](examples/mcp-config.json) 中找到可编辑的副本。
**从另一台机器快速测试:**
```bash
# 无令牌 → 401(正常:已锁定)
curl -s -o /dev/null -w "%{http_code}\n" -X POST http://YOUR-MAC-IP:3456/mcp \
-H 'Accept: application/json, text/event-stream' -d '{}'
# 有令牌 → 初始化响应
curl -s -X POST http://YOUR-MAC-IP:3456/mcp \
-H 'Authorization: Bearer YOUR-TOKEN' \
-H 'Accept: application/json, text/event-stream' \
-d '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2025-06-18","capabilities":{},"clientInfo":{"name":"t","version":"0"}}}'
```
---
## 在VPN上安全运行
HTTP服务器使用 **纯HTTP,没有传输加密**,并且它授予对您整个日历的读写访问权限。 **不要将端口3456暴露在公共互联网上。** 相反,将两台机器放在私人VPN上,并将服务器绑定到VPN接口。两个好选择:
### 选项A — Tailscale(推荐)
[Tailscale](https://tailscale.com) 为每个设备提供一个稳定的私有IP地址,范围为 `100.x.y.z`,并为您处理所有网络。
1. **在Mac和客户端机器上安装Tailscale**,并将两者登录到同一个账户(“tailnet”)。
2. **查找Mac的Tailscale IP:**
```bash
tailscale ip -4 # 例如 100.101.102.103
```
3. **将其绑定到Tailscale IP:**
```bash
ical serve setup --tailscale # 为您读取 `tailscale ip -4`
```
4. 在客户端,使用上面的配置中的 `http://100.101.102.103:3456/mcp`。
5. **(可选)使用Tailscale ACL锁定**,仅允许 **您的** 设备访问Mac上的日历端口。在Tailscale管理控制台策略文件中:
```jsonc
{
"acls": [
// 仅允许您的设备访问Mac上的日历端口
{ "action": "accept", "src": ["autogroup:member"], "dst": ["YOUR-MAC:3456"] }
]
}
```
### 选项B — WireGuard
如果您运行自己的 [WireGuard](https://www.wireguard.com) 网络,Mac有一个WireGuard接口地址(例如 `10.0.0.1`):
1. **将服务器绑定到WireGuard接口地址:**
```bash
ical serve setup --host 10.0.0.1
```
2. 在每个对等体上,将 **AllowedIPs** 限定为仅需要路由的路由 — 例如,在对等体的WireGuard配置中:
```ini
[Peer]
# ... Mac 的公钥 + 端点 ...
AllowedIPs = 10.0.0.1/32
```
3. 从对等体,使用上面的配置中的 `http://10.0.0.1:3456/mcp`。
> 无论您使用哪种VPN,仍然需要承载令牌 — VPN控制 **谁可以访问端口**,而令牌控制 **谁可以使用服务器**。
---
## 手动/高级设置
`ical serve setup` 会自动完成所有这些操作。如果您想手动运行服务器,可以按照以下步骤:
<details>
<summary>手动运行HTTP服务器(环境令牌)</summary>
手动启动它来试用:
```bash
export CALENDAR_MCP_TOKEN="$(openssl rand -hex 16)" # 生成令牌
ical mcp --http # 监听 127.0.0.1:3456
```
添加 `--host <ip>` / `--port <n>` (或环境变量 [配置参考](#配置参考))以绑定到其他位置。
检查它是否启动(请求 **不** 带令牌应该被拒绝,返回 `401`):
```bash
curl -s -o /dev/null -w "%{http_code}\n" -X POST http://127.0.0.1:3456/mcp \
-H 'Accept: application/json, text/event-stream' -d '{}' # → 401
```
> **没有 `brew services` 集成**:brew 管理的服务 plist 在升级时会被重新生成,这会清除任何通过 `launchctl setenv` 注入的环境变量,并使服务器关闭。 对于任何持久化操作,请使用 `ical serve setup` — 它的令牌文件和用户拥有的 LaunchAgent 在重启和 `brew upgrade` 后仍然存活;这是受支持的路径。
</details>
---
## 配置参考
| 设置 | 环境变量 | 默认值 | 备注 |
|---|---|---|---|
| 令牌 | `CALENDAR_MCP_TOKEN` | — | HTTP模式需要来自 **某些** 来源的令牌 — 此环境变量、下面的令牌文件或客户端令牌;比较时间恒定 |
| 令牌文件 | `CALENDAR_MCP_TOKEN_FILE` | `~/.config/apple-calendar/token` | 旧的共享/默认令牌;覆盖默认路径;`serve setup`写入它 |
| 客户端令牌 | — | `~/.config/apple-calendar/tokens/<client>` | 每个客户端一个文件,由 `ical serve token` / `serve connect` 管理;与上面的源联合 |
| 绑定地址 | `CALENDAR_MCP_HOST` | `127.0.0.1` | 设置为您的VPN IP以允许远程客户端 |
| 端口 | `CALENDAR_MCP_PORT` | `3456` | |
所有令牌源都是 **联合** 的 — 环境令牌、默认令牌文件和 `tokens/` 中的每个命名客户端文件同时有效;任何一个都可以授权请求,并且每个都使用相同的恒定时间比较。 命令行参数: `--host` 、 `--port` 和 `--no-auth` (在没有令牌的情况下运行 — 参见下面的安全模型)。
---
## 安全模型
- **读写**。该工具可以查询事件,也可以创建、更新和删除事件。 没有只读模式 — 任何可以调用服务器的人都可以更改您的日历,因此请相应地对待对其(及其令牌)的访问。
- **需要令牌,关闭失败**。HTTP服务器不会在没有可解析的令牌的情况下启动,并且拒绝任何 `Authorization: Bearer …` 头部不与其匹配的请求(恒定时间比较)。 由于HTTP服务器还公开了写入工具,因此保持令牌保密非常重要。
- **每个客户端的令牌,联合**。 环境令牌、默认令牌文件和 `~/.config/apple-calendar/tokens/` 中的每个命名文件同时有效 — 任何一个都可以授权请求,并且每个都使用相同的恒定时间比较。 每个会话都记录了与其匹配的客户端(`~/Library/Logs/apple-calendar.log` 中的 `session <id> client=<name>`),并且撤销令牌(`ical serve token revoke <client>`)在运行中的服务器上立即生效 — 无需重启。 如果所有令牌都被撤销,服务器拒绝所有请求。
- **内置加密**。 HTTP模式是纯HTTP — 仅在受信任的私有网络(环回或VPN)上运行,绝不能在公共互联网上运行。
- **macOS权限,固定到二进制文件**。 日历访问由macOS按代码标识授予。 二进制文件使用稳定的标识符签名(`com.apple-calendar-mcp.cli`),因此该授予在重新编译之间保持有效 — 您只需批准一次。
---
## 从源代码构建
```bash
git clone https://github.com/hunterbrewer04/apple-calendar-mcp.git
cd apple-calendar-mcp
swift build -c release
codesign -s - --identifier com.apple-calendar-mcp.cli --force .build/release/apple-calendar
```
每次 **重新编译** 后请重新运行 `codesign` 步骤 — 稳定标识符正是保持日历权限在重新编译之间有效的原因。(Homebrew会自动为您执行此操作。)
> 完整的Swift工具链运行测试套件(`swift test`)。 命令行工具可以构建和运行二进制文件,但不能运行测试;它们在每次推送时在CI上运行。
---
## 工作原理
```
ical <subcommand> → CLI ───────┐
ical mcp → stdio MCP ──┼─→ 共享 EventKit 存储 → macOS 日历数据库 (~100 毫秒)
ical mcp --http → HTTP MCP ───┘ (读写)
▲
└─ 承载令牌,关闭失败
```
单个EventKit存储为两个前端提供服务。MCP层使用官方 [Swift MCP SDK](https://github.com/modelcontextprotocol/swift-sdk) 进行协议和 `stdio` 传输,并使用[Hummingbird](https://github.com/hummingbird-project/hummingbird)支持的传输进行令牌门控HTTP服务器。
---
## 许可证
[MIT](LICENSE)。
MCP 配置
以下是此 MCP Server 的配置信息,可直接复制到 Cursor 或其他 MCP 客户端中使用。
mcp.json
连接信息
猜你喜欢
暂无推荐服务器