关于 microsoft-calendar-sync
microsoft-calendar-sync 是为使用 Microsoft 365 或 Outlook.com 的应用开发者提供的集成技能,帮助将日历忙碌时段同步到可用性数据,并将预订写入 Outlook。它着重处理 Graph 通知、订阅续期、时区、令牌轮换及事件标记等同步细节。
主要功能
- 01将 Outlook 忙碌时段同步为数据记录
- 02将预订事件写入并从 Outlook 删除
- 03通过 Graph 通知触发忙碌时间同步
- 04续订日历订阅并处理失效情况
- 05支持 Teams 会议链接及邮箱时区
- 06提供 Firestore 与 Supabase 存储实现
适用场景
- 01将主办人的 Outlook 日程用于可用性判断
- 02把已确认预订同步至主办人的日历
- 03预订取消时删除对应日历事件
- 04排查 Graph 订阅过期或通知遗漏
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ timerise-ai/ microsoft-calendar-sync --skill microsoft-calendar-sync安装后在 Claude Code 中输入/microsoft-calendar-sync确认已加载。
包含的文件
除 SKILL.md 外,安装时一起下载的参考资料和脚本。
.gitattributesLICENSECHANGELOG.mdCLAUDE.mdREADME.md.github/workflows/agent-eval.ymlevals/prompts.mdreferences/booking-events.mdbusy-sync.mdfirestore-backend.mdcalendars-and-settings.mdoperations.mddata-model.mdsupabase-backend.mdsubscriptions.mdprovenance.mdgraph-api.mdoauth-and-tokens.mdhttp-routes.mdtest-flows.mdtime-zones.mdtest-suite.mdtesting.md详细介绍
Microsoft Calendar Sync
两个方向,各有一条规则。入站:Outlook 是主持人其他日程安排的真实来源,因此忙碌时段只是某个时间窗口的临时镜像,由完整重新同步重建,绝不手动编辑。出站:预订存储是预订信息的真实来源,因此写入 Outlook 的事件会带有包含预订 ID 的私有标记,可通过该标记再次找到,并且绝不会被读回为忙碌时段。与 Google 不同,Graph 会指定事件 ID,要求在 3 秒内响应,并且每个日历只能有一个订阅。
适用场景
- 主持人连接 Microsoft 365 或 Outlook.com,并选择用于屏蔽可用时间的日历。
- 预订会显示在主持人的 Outlook 日历中,可选择创建 Teams 会议;取消后则会消失。
- 审查现有的 Graph 集成:一小时后失效、订阅无响应、遗漏假期。
- 在 Firestore 与 Supabase/Postgres 之间迁移同步功能。
不适用场景
- Google Calendar:使用
google-calendar-sync。架构和记录形状相同,但 API 不同。 - 生成可预订时段、扣除忙碌时间:使用
time-slot-generator。此技能会生成它所消耗的忙碌时段。 - **使用 Microsoft 登录用户(SSO)**属于身份验证范畴;此技能为已登录用户请求日历访问权限。
- 会议室和资源邮箱,或无需逐用户授权的组织范围访问需要管理员同意的应用程序权限,设计方式也不同。
- 本地部署的 Exchange Server使用 EWS,而不是 Graph。
架构
connect (PKCE, single-use state) -> callback -> id token tid:oid + encrypted tokens + mailbox zone -> rows
|
inbound Graph -- POST {value[]} -> /notifications -> verify clientState -> 202 -> defer(syncBusy) -> calendarView pages
^ -> toBusyBlock -> diffBusy -> busy blocks
+-- subscription <-- cron: PATCH renew, 404 replace, 409 clear, resync every busy calendar
outbound booking confirmed -> find by marker -> unchanged? skip : PATCH - none? POST (transactionId) -> Teams link back
booking cancelled -> find by marker -> DELETE each (404 = done)
关键事实
- 保存每次令牌响应中的刷新令牌。 Microsoft 每次刷新时都会轮换刷新令牌。 Node 版 MSAL 会将其保存在自己的缓存中,且不会返回,因此如果集成保存的是 MSAL 的 结果,就不会有刷新令牌,并会在第一个访问令牌过期时失效,通常约一小时后。
clientState最多 128 个字符。 否则 Graph 会拒绝订阅。仅 Outlook 日历 ID 就约有 150 个字符。只放入经签名的订阅 ID。- 在 3 秒内响应通知,然后再同步。 如果端点响应较慢,Graph 会将其标记为慢速,延迟通知,随后永久丢弃。验证后返回 202,并在响应后运行同步。
- 每个日历只能有一个订阅;在原订阅上续订。 对同一资源发起第二次 POST 会返回 409,因此 Google 的先建新订阅再删旧订阅的续订方式不适用。PATCH 更新到期时间;如果返回 404,表示 Graph 已删除该订阅,只有此时才创建新订阅。
- 全天事件采用浮动时间。 Graph 会以你请求的时区将其作为午夜到午夜返回,因此转换为 UTC 后,日期正确,但具体时刻不对。应以邮箱的时区为准,而 Outlook 通常会返回 Windows 格式的时区名称。
- Graph 会分配事件 ID。 通过单值扩展属性重新查找预订对应的事件。先查找再创建;合并重复事件;如果没有任何变化就跳过写入,因为对有与会者的会议执行每次 PATCH 都会发送邮件。
- 与会者会收到邮件;没有关闭选项。 邀请访客会从主持人的 Outlook 向其发送邀请,删除事件则会发送取消通知。
inviteAttendees决定列出哪些人。
硬性规则
绝不能从 OAuth 回调 URL 获取用户 ID。 state 是服务器端一次性记录的键,有效期为十分钟。否则,任何人都能将自己的日历关联到他人的账户。
绝不能在响应通知之前进行同步。 将工作交给
defer;cron 是安全保障。
在验证
clientState并确认其中标明了通知所属的订阅之前,绝不能信任通知。 该端点是公开的;经过签名的 clientState 是唯一凭证。
绝不能以明文存储令牌。 刷新令牌拥有长期的日历读写权限。
绝不能让 Outlook 故障导致预订失败。 出站写入只记录日志并返回;预订仍然有效。
适配契约
此表是衔接契约;该技能没有 adaptation.md。重命名对照表见
data-model.md,工作顺序见下方快速入门。
| 衔接项 | 技能提供 | 宿主提供 |
|---|---|---|
| 领域实体 | Connection、LinkedCalendar、WatchSubscription、BusyBlock、BookingForCalendar | 宿主使用的名称(员工、资源、预约) |
| 租户范围 | 每位用户:每个 userId 对应一个 Microsoft 账户 | 宿主的用户 ID;组织范围仍由其守卫处理 |
| 身份验证守卫 | authenticate(request),返回 userId | null | Session、Clerk、NextAuth、Firebase、Supabase |
| 数据访问 | CalendarStore + Firestore 和 Supabase 实现 | 宿主的 ORM 或 SDK |
| 预约 | BookingSource + pushBookingEvent / removeBookingEvent | 在预约状态流程中调用这些接口 |
| 可用时间 | 每位用户的忙碌时间块、listHostBusy 查询 | 宿主的时段引擎(time-slot-generator) |
| 时区 | 邮箱时区;hostTimeZone(userId);defaultTimeZone | 宿主的业务时区 |
| HTTP | Web Request/Response 处理程序 | Next.js 路由处理程序、Express 适配器 |
| 后台工作 | 用于通知的 defer;每 12 小时运行一次 renewSubscriptions | after()、waitUntil、Cloud Scheduler、Vercel Cron |
| 字符串 | describeBooking(事件标题/正文)、错误代码 | 宿主的 i18n |
| 密钥 | 客户端 ID/密钥、clientState 密钥、32 字节令牌密钥、标记 GUID | 宿主的密钥管理器 |
| 测试 | node:test 测试套件、内存存储、模拟 Graph | 宿主的运行器 |
快速入门
- 在 Entra ID 中注册应用并生成标记 GUID: oauth-and-tokens.md。
- 复制类型并确定重命名方案:data-model.md。
- 复制 Graph 客户端:graph-api.md;以及时区映射: time-zones.md。
- 复制入站同步:busy-sync.md;出站同步: booking-events.md;订阅: subscriptions.md;日历列表和开关: calendars-and-settings.md。
- 选择存储:firestore-backend.md 或 supabase-backend.md。
- 挂载路由、
defer和 cron:http-routes.md。 - 复制测试套件,并在两个
TZ值下运行:testing.md、 test-suite.md、test-flows.md。 - 向运维人员提供健康状态视图:operations.md。
- 要修复现有的 Graph 集成?修复顺序见 provenance.md。
参考目录
| 场景 | 触发关键词 | 参考资料 |
|---|---|---|
| 实体、存储契约、重命名 | LinkedCalendar、WatchSubscription、BusyBlock、CalendarStore、mutateCalendar、version | data-model.md |
| 连接账户 | Entra ID、app registration、redirect URI、tenant、organizations、common、scopes、PKCE、id_token、tid、oid、refresh token rotation、invalid_grant、interaction_required、admin consent、AES-256-GCM | oauth-and-tokens.md |
| 与 Graph 通信 | fetch、calendarView、Prefer outlook.timezone、@odata.nextLink、$filter extended property、429、Retry-After、subscriptions、MSAL | graph-api.md |
| 邮箱时区 | mailboxSettings、Windows time zone、"W. Europe Standard Time"、IANA、CLDR windowsZones | time-zones.md |
| 忙碌时间同步入站 | toBusyBlock、showAs、free、workingElsewhere、oof、tentative、declined、isCancelled、isAllDay、diffBusy、lease、horizon | busy-sync.md |
| 预订事件同步出站 | singleValueExtendedProperties、marker GUID、transactionId、attendees、invitations、Teams、isOnlineMeeting、joinUrl、backfill | booking-events.md |
| 订阅 | clientState、128 characters、expirationDateTime、10080 minutes、PATCH renew、404、409 Conflict、lifecycle、subscriptionRemoved、missed | subscriptions.md |
| 日历列表和开关 | canEdit、isDefaultCalendar、shared calendar、syncBusy、syncBookings、planTransitions | calendars-and-settings.md |
| 路由和 webhook | validationToken、text/plain、202、3 seconds、defer、after、waitUntil、callback redirect、consent_required、cron secret、Express | http-routes.md |
| Firestore | firebase-admin、runTransaction、batch 500、composite index、security rules | firestore-backend.md |
| Supabase / Postgres | schema.sql、RLS、service role、optimistic concurrency、23505、PostgREST 1000 rows | supabase-backend.md |
| 验证方法 | node:test、memory store、fake Graph、TZ=、mutation check | testing.md、test-suite.md、test-flows.md |
| 运行 | health、stale、subscription-expired、env vars、throttling、runbook、tunnel、localhost | operations.md |
| 这样设计的原因 | provenance、defects、kept deliberately、fix order | provenance.md |
本目录属于 Timerise Skills 索引,该索引列出了相关的其他技能。