mcpField
M
microsoft-calendar-sync

microsoft-calendar-sync

github.com/timerise-aiDevelopment03 installsUpdated 3d ago
Repository

About microsoft-calendar-sync

microsoft-calendar-sync is an integration skill for developers building apps with Microsoft 365 or Outlook.com calendars. It syncs busy periods into availability data and bookings to Outlook, addressing Graph notifications, subscription renewal, time zones, token rotation, and event identification.

Key features

  1. 01Sync Outlook busy periods into stored records
  2. 02Create and delete booking events in Outlook
  3. 03Trigger busy-time sync from Graph notifications
  4. 04Renew calendar subscriptions and handle expiration
  5. 05Support Teams links and mailbox time zones
  6. 06Provide Firestore and Supabase storage implementations

Use cases

  1. 01Use a host's Outlook calendar for availability
  2. 02Sync confirmed bookings to a host's calendar
  3. 03Remove calendar events when bookings are cancelled
  4. 04Investigate expired Graph subscriptions or missed notifications

Install

Copy the command and run it in a terminal.

$npx skills add https://github.com/timerise-ai/microsoft-calendar-sync --skill microsoft-calendar-sync

After installing, type/microsoft-calendar-syncin Claude Code to confirm it loaded.

Bundled files

References and scripts installed along with SKILL.md.

.gitattributes
LICENSE
CHANGELOG.md
CLAUDE.md
README.md
.github/workflows/
agent-eval.yml
evals/
prompts.md
references/
booking-events.md
busy-sync.md
firestore-backend.md
calendars-and-settings.md
operations.md
data-model.md
supabase-backend.md
subscriptions.md
provenance.md
graph-api.md
oauth-and-tokens.md
http-routes.md
test-flows.md
time-zones.md
test-suite.md
testing.md

Details

From the project README

Microsoft Calendar Sync

Two directions, one rule each. Inbound: Outlook is the truth about a host's other commitments, so busy blocks are a disposable mirror of a time window, rebuilt by a full resync and never edited by hand. Outbound: the booking store is the truth about bookings, so an event written to Outlook carries a private marker with the booking id, is found again by it, and is never read back as busy time. Unlike Google, Graph picks event ids, wants answers within 3 s and allows one subscription per calendar.

When to use

  • Hosts connect Microsoft 365 or Outlook.com and pick calendars that block their availability.
  • Bookings appear in the host's Outlook calendar, optionally as Teams meetings, and vanish on cancel.
  • Auditing an existing Graph integration: dead after an hour, silent subscriptions, missed vacations.
  • Moving that sync between Firestore and Supabase/Postgres.

When NOT to use

  • Google Calendar: use google-calendar-sync. Same architecture and record shapes, different API.
  • Generating bookable slots, subtracting busy time: use time-slot-generator. This skill produces the busy blocks it consumes.
  • Signing users in with Microsoft (SSO) is an identity concern; this skill asks for calendar access of an already signed-in user.
  • Room and resource mailboxes, or organisation-wide access without per-user consent need application permissions with admin consent, a different design.
  • Exchange Server on-premises speaks EWS, not Graph.

Architecture

          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)

Critical facts

  1. Store the refresh token from every token response. Microsoft rotates it on each refresh. MSAL for Node keeps it in its own cache and never returns it, so an integration that stores MSAL's result has no refresh token and dies when the first access token expires, about an hour in.
  2. clientState is at most 128 characters. Graph refuses the subscription otherwise. An Outlook calendar id alone is around 150. Put only your subscription id in it, signed.
  3. Answer notifications within 3 seconds, then sync. Graph marks a slower endpoint slow, delays it, then drops its notifications for good. Verify, return 202, run the sync after the response.
  4. One subscription per calendar; renew in place. A second POST for the same resource is a 409, so Google's new-before-old renewal does not apply. PATCH the expiry; a 404 means Graph dropped it, and only then is a new one created.
  5. All-day events float. Graph returns them as midnight to midnight in the zone you requested, so in UTC they are the right dates at the wrong instants. Anchor them to the mailbox's zone, which Outlook usually reports as a Windows name.
  6. Graph assigns event ids. A booking's event is found again through a single-value extended property. Find before create; collapse duplicates; skip a write that changes nothing, because every PATCH to a meeting with attendees e-mails them.
  7. Attendees are e-mailed; there is no switch. Inviting the guest sends them an Outlook invitation from the host, and deleting the event sends a cancellation. inviteAttendees decides who is listed.

Hard rules

Never take the user id from the OAuth callback URL. The state is a key to a server-side, single-use, ten-minute record. Otherwise anyone attaches their calendar to someone else's account.

Never sync before answering a notification. Hand the work to defer; the cron is the safety net.

Never trust a notification before its clientState verifies and names the subscription it arrived on. The endpoint is public; the signed clientState is the only credential.

Never store tokens in clear text. A refresh token is long-lived read/write access to a calendar.

Never let an Outlook failure fail a booking. Outbound writes log and return; the booking stands.

Adaptation Contract

This table is the seam contract; the skill has no adaptation.md. The rename table is in data-model.md, the order of work is the quick start below.

SeamThe skill shipsThe host supplies
Domain entitiesConnection, LinkedCalendar, WatchSubscription, BusyBlock, BookingForCalendarIts names (staff, resource, appointment)
Tenant scopePer user: one Microsoft account per userIdIts user id; org scoping stays in its guard
Auth guardauthenticate(request), returning userId | nullSession, Clerk, NextAuth, Firebase, Supabase
Data accessCalendarStore + Firestore and Supabase implementationsIts ORM or SDK
BookingsBookingSource + pushBookingEvent / removeBookingEventCalls them from its booking status flow
AvailabilityBusy blocks per user, listHostBusy queryIts slot engine (time-slot-generator)
Time zoneMailbox zone; hostTimeZone(userId); defaultTimeZoneThe host's business zone
HTTPWeb Request/Response handlersNext.js route handlers, Express adapter
Background workdefer for notifications; renewSubscriptions every 12 hafter(), waitUntil, Cloud Scheduler, Vercel Cron
StringsdescribeBooking (event title/body), error codesIts i18n
SecretsClient id/secret, clientState secret, 32-byte token key, marker GUIDIts secret manager
Testsnode:test suite, memory store, fake GraphIts runner

Quick start

  1. Register the app in Entra ID and generate the marker GUID: oauth-and-tokens.md.
  2. Copy the types and decide the rename: data-model.md.
  3. Copy the Graph client: graph-api.md; and the zone map: time-zones.md.
  4. Copy inbound sync: busy-sync.md; outbound: booking-events.md; subscriptions: subscriptions.md; the calendar list and toggles: calendars-and-settings.md.
  5. Pick a store: firestore-backend.md or supabase-backend.md.
  6. Mount the routes, defer and the cron: http-routes.md.
  7. Copy the suite and run it under two TZ values: testing.md, test-suite.md, test-flows.md.
  8. Give operators the health view: operations.md.
  9. Repairing an existing Graph integration? The fix order is in provenance.md.

Reference directory

ScenarioTrigger keywordsReference
Entities, store contract, renameLinkedCalendar, WatchSubscription, BusyBlock, CalendarStore, mutateCalendar, versiondata-model.md
Connecting an accountEntra 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-GCMoauth-and-tokens.md
Talking to Graphfetch, calendarView, Prefer outlook.timezone, @odata.nextLink, $filter extended property, 429, Retry-After, subscriptions, MSALgraph-api.md
Mailbox zonesmailboxSettings, Windows time zone, "W. Europe Standard Time", IANA, CLDR windowsZonestime-zones.md
Busy time intoBusyBlock, showAs, free, workingElsewhere, oof, tentative, declined, isCancelled, isAllDay, diffBusy, lease, horizonbusy-sync.md
Bookings outsingleValueExtendedProperties, marker GUID, transactionId, attendees, invitations, Teams, isOnlineMeeting, joinUrl, backfillbooking-events.md
SubscriptionsclientState, 128 characters, expirationDateTime, 10080 minutes, PATCH renew, 404, 409 Conflict, lifecycle, subscriptionRemoved, missedsubscriptions.md
Calendar list and togglescanEdit, isDefaultCalendar, shared calendar, syncBusy, syncBookings, planTransitionscalendars-and-settings.md
Routes and webhookvalidationToken, text/plain, 202, 3 seconds, defer, after, waitUntil, callback redirect, consent_required, cron secret, Expresshttp-routes.md
Firestorefirebase-admin, runTransaction, batch 500, composite index, security rulesfirestore-backend.md
Supabase / Postgresschema.sql, RLS, service role, optimistic concurrency, 23505, PostgREST 1000 rowssupabase-backend.md
Proving itnode:test, memory store, fake Graph, TZ=, mutation checktesting.md, test-suite.md, test-flows.md
Running ithealth, stale, subscription-expired, env vars, throttling, runbook, tunnel, localhostoperations.md
Why it is built this wayprovenance, defects, kept deliberately, fix orderprovenance.md

Part of the Timerise Skills index, which lists the sibling skills.

Comments

Markdown supported