关于 deltat
deltat 是供 Agent 使用的日历预订 MCP 服务,适合先提出时间、再等待人工确认的场景。它通过查找、暂存和提交流程临时保留时段,并以原子操作提交预订,也支持多个日历。
主要功能
- 01跨日历查找空闲时段
- 02确认前暂存时段
- 03提交或释放暂存
- 04原子预订多个日历
- 05冲突时返回备选时间
- 06查看和取消预订
适用场景
- 01等待确认期间暂留一个时段
- 02同时预订房间和设备
- 03时段被占用时提供备选
- 04管理预约和取消事项
MCP 相关
该服务通过 MCP 客户端提供日历工具,并由 deltat 数据库支持。预订流程为 find_slots、hold_slot、commit_hold;当前软件包提供 stdio 服务。
配置
本地服务的配置在各客户端中通用,复制后粘贴到客户端的 MCP 配置文件即可。
npx -y @open-deltat/mcpDELTAT_PASSWORD密钥Password for the deltat server. Required; there is no default.DELTAT_HOSTHostname of the deltat server.DELTAT_PORTPort deltat listens on.DELTAT_DATABASETenant to connect to. Each database name is an isolated tenant.DELTAT_USERUsername presented at connect time.DELTAT_TLSon to encrypt the connection and verify deltat's certificate. Unset means off unless DELTAT_TLS_CA is set; without TLS the password is sent in the clear, so use it for any deltat that is not on this machine.DELTAT_TLS_CAPath to a PEM certificate to trust instead of the system's authorities, for a deltat with a self-signed certificate. Implies DELTAT_TLS=on.本地服务需要在你的电脑上安装运行环境(如 Node.js 18+)。各客户端的配置文件位置不同,请参考客户端文档。
工具
9收录的工具列表 · 更新于 2 周前
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
| name | string | 是 | |
| hours | array | 否 | 每周开放时段;为空表示不可用 |
| timezone | string | 否 | IANA 时区,例如 Europe/Berlin |
| slot_minutes | integer | 否 |
详细介绍
@open-deltat/mcp
为 Agent 提供一个真正可预约、绝不会出现重复预约的日历。
npx @open-deltat/mcp
大多数排程 API 只给 Agent 一个动词:预约。这对提交表单来说没问题,但对 Agent 来说行不通,因为 Agent 需要几秒钟才能做出决定。它看到 14:30 有空,于是说“14:30 怎么样?”,然后等人类回复;等到回复时,那个时段可能已被别人占用。查看和预约是两个独立的时刻,中间的每一刻都存在竞争。
这个服务没有一步完成预约的动词。预约流程是:
find_slots → hold_slot → commit_hold
关键在于暂留。它会将时段保留几分钟,因此从提出时间到确认期间,该时段属于你,其他人无法占用。暂留会自动过期,因此被放弃的对话会自行清理,暂留也不会产生任何成本。
底层是 deltat,这是一个数据库,其唯一不变量是两项预约绝不会占用同一时段。拒绝由存储引擎发出,而非由先行检查、寄希望于不会出错的应用代码发出。
工具
| 工具 | 使用时机 |
|---|---|
find_slots | 向任何人提供时间之前。列出你指定的所有日历上真正空闲的时段。 |
hold_slot | 即将向人类报出某个具体时间时。返回 hold_ids。 |
commit_hold | 预约已确认时。以原子方式将每个暂留时段转为预约。 |
release_hold | 确定不需要已暂留的时段时。立即释放该时段。 |
create_calendar | 需要新的可预约日历时。返回 calendar_id。 |
set_availability | 开放时间有变,或 find_slots 没有返回任何结果时。替换整周的安排。 |
list_bookings | 有人询问已安排的事项,或你需要 booking_id 来取消预约时。 |
cancel_booking | 人类要求取消已确认的预约时。 |
book_slot | 永不使用。此工具仅用于拒绝请求,并引导你改用 hold_slot + commit_hold。 |
最后一行是刻意为之。其他所有日历 API 都有一步完成预约的动词,因此模型会尝试使用这里的同类动词,却发现无此功能。与其让它产生幻觉式工具调用或放弃对话,不如注册这个动词并在响应中解释实际的两个步骤。它绝不会预约任何内容。
时间以带必需偏移量的 RFC 3339 字符串传递,绝不会使用裸 epoch 毫秒数。没有时区的时间戳会被拒绝,而不会被默默解释为进程运行所在时区的时间。错误会带有可用于分支处理的类型前缀:CONFLICT(选择其他时段)、EXPIRED(重新暂留)、INVALID、NOT_FOUND。
拒绝时告知可用时间
当时段已被占用或时间不在开放时间内时,拒绝响应会附带原本可用的时间,这样 Agent 就能在同一轮中提出替代方案,而不必从头开始:
{
"error": "CONFLICT",
"booked": false, "held": false, "reserved": false,
"retry_same_time": true,
"alternatives": [
{ "start": "2026-06-01T12:00:00.000Z", "end": "2026-06-01T13:00:00.000Z",
"start_local": "1 Jun 2026, 2:00 pm" }
],
"next": "These times were free a moment ago but are NOT reserved. Offer one, then call hold_slot on it before you tell anyone it is theirs."
}
在实时通话中,这能省去整个模型轮次,否则就只能陷入沉默。
reserved 始终为 false。之所以明确声明,而不是让人自行推断,是因为只扫一眼 alternatives 的模型不能因此认定某个时段已被留出。替代时间是曾经空闲的时间;要使用其中任一时间,仍需调用 hold_slot。
如果没有可提供的替代时间,拒绝响应仍采用简单的 CODE: message 格式,因此禁用了替代方案的内核(DELTAT_COUNTER_OFFER=0)或早于此功能的内核,行为都与以前完全相同。
同时预约多个日历
相机机身、镜头和工作人员。外科医生和手术室。预约以及前往预约地点的车程。将所有日历传给同一组工具:
find_slots({ calendar_ids: [body, lens, crew], from, to }) times when ALL are free
hold_slot({ calendar_ids: [body, lens, crew], start, end }) holds every one, or none
commit_hold({ hold_ids, label }) books every one, or none
只有一个日历时,也使用相同的调用方式,只需传入一个元素的列表。提交操作在 deltat 中是一条记录,因此无论是其他预约者抢先预约,还是发生崩溃,都不会导致机身已预约而镜头仍空闲。
当一组设备的预约被拒绝时,替代时间是所有日历都能在相同长度的时段内空闲的时间,绝不会是刚被拒绝的时间,格式与上文相同。
配置
你需要一个 deltat 服务,版本为 0.4.0 或更高。使用 Docker 自托管:
docker run -p 5433:5433 -e DELTAT_PASSWORD=<your-password> ghcr.io/open-deltat/deltat
然后添加 MCP 服务。在 Claude Code 中:
claude mcp add deltat \
--env DELTAT_HOST=localhost \
--env DELTAT_PORT=5433 \
--env DELTAT_DATABASE=public \
--env DELTAT_PASSWORD=<your-password> \
-- npx -y @open-deltat/mcp
在 Claude Desktop 中,编辑 claude_desktop_config.json 并重启:
{
"mcpServers": {
"deltat": {
"command": "npx",
"args": ["-y", "@open-deltat/mcp"],
"env": {
"DELTAT_HOST": "localhost",
"DELTAT_PORT": "5433",
"DELTAT_DATABASE": "public",
"DELTAT_PASSWORD": "<your-password>"
}
}
}
}
DELTAT_PASSWORD 是必填项,没有默认值;如果未设置,服务会拒绝启动,而不是尝试使用容易猜到的密码。其他所有配置默认连接到 localhost:5433 上的本地 deltat,数据库为 public。每个数据库名称都是一个隔离租户。
然后只需询问:“创建一个名为 Haircuts 的日历,开放时间为工作日下午,然后帮我预约明天第一个空闲的 30 分钟时段。”
关于身份验证,实话实说
这个 stdio 服务没有登录功能。 配置中的 deltat 密码就是凭据,服务以你的用户权限运行。这就是单操作员自托管模式:你拥有 deltat,也拥有 Agent,因此无需 OAuth。
托管式多租户服务是另一个问题。 在这种情况下,Agent 使用 OAuth 2.1 进行身份验证:服务收到未经身份验证的调用时,会返回 401 和受保护资源元数据(RFC 9728);具备相应能力的客户端则会自行运行授权码 + PKCE 流程,并通过 Client ID Metadata Document 注册,且需获得一次性人工授权。本包中的 OidcAdapter 是资源服务器端的实现,可离线使用颁发者的 JWKS 验证 bearer JWT,无需针对每个请求进行网络调用。
这个包目前还不包含该托管式传输方式。上述 stdio 服务是当前可运行的方式。
嵌入
import { createDeltatMcpServer } from "@open-deltat/mcp";
import { DeltaT } from "@open-deltat/client";
const server = createDeltatMcpServer(new DeltaT({ /* ... */ }));
await server.connect(yourTransport);
端到端验证
在 deltat 运行时:
bun scripts/smoke.ts
会通过内存中的 MCP 客户端驱动完整流程,并验证整个往返过程 (创建 → 查询可用性 → 查找 → 暂留 → 确认 → 列出 → 取消)。
许可证
MIT
评论
一个开放协议,规定了 AI 应用如何以统一方式连接外部工具与数据源。客户端负责与模型对话,MCP 服务负责提供工具、资源和提示模板,两者通过标准消息通信,同一个服务可以被不同客户端复用。