• v0.8.0 811a992fa6

    v0.8.0 — update_event and delete_event, addressed by opaque handles
    All checks were successful
    CI / gate (push) Successful in 1m2s
    CI / image (push) Successful in 50s
    Stable

    joeyr released this 2026-08-09 16:44:19 -05:00 | 100 commits to main since this release

    Editing and deleting, addressed by an opaque handle. 0.7.0 could put things on
    the calendar and never take them off again.

    Added

    • update_event — changes the fields it is given on one stored event and
      leaves the rest alone. It mutates the stored resource rather than rebuilding
      it
      , which is the difference between an edit and a quiet amputation: a
      rebuild from the handful of fields this server models would discard the VALARM
      reminders, attendees, categories and Apple X-properties it does not, and
      nothing in the response would say so.

      Moving an event preserves its length, so a start on its own is enough. Whether
      an event is all-day is read from the stored event, never from the shape of the
      argument — converting a day off into a 30-minute appointment because a caller
      passed a time is not a thing a caller should be able to do by accident.

    • delete_event, which requires expected_title and refuses on a mismatch.
      It costs one argument the caller already has, and it converts the failure that
      cannot be undone — two ids transposed out of one response — into a refusal
      that names both titles. The response carries the whole deleted event, since
      that is the only remaining record of what was there.

    • event_id, an opaque handle on every addressable event. Not the resource
      URL, which carries the account DSID and must never reach the model; not the
      uid, which every occurrence of a series shares; and not (calendar, uid) as
      two arguments, which lets a model pair the right uid with the wrong calendar
      or assemble one from parts it half-remembers. One string, produced by a read,
      passed back verbatim.

      Its absence is the signal. No event_id means nothing here can be edited
      or deleted: repeating events, subscriptions, and everything on a calendar that
      is readable but not writable. With WRITABLE_CALENDARS unset no event carries
      one at all, so the read surface still matches the read-only releases exactly.

      Deliberately unsigned. The write allowlist is checked against the calendar
      named inside the handle before iCloud is contacted, so forging one buys
      access to exactly the calendars the operator already opted in to. Signing
      would add a key with the MCP_OAUTH_SIGNING_KEY failure mode — rotate it and
      every handle issued earlier in a conversation stops resolving, which reads as
      the events having vanished.

    Changed

    • Every error envelope out of a write tool now states created, updated or
      deleted as false.
      They were present on the failures raised from the
      client and absent on the ones caught during argument validation. A missing key
      is not the same signal as a false one, and the gap was on exactly the paths
      where the model has the least other evidence.

    • The server instructions name the new capabilities and their limits together,
      because stating the capability alone invites the model to offer the rest.

    Security

    • Repeating events are refused, and the refusal is checked against the stored
      resource rather than against anything a read inferred.
      A RECURRENCE-ID
      override carries no RRULE of its own, so a moved instance of a weekly meeting
      reads as a one-off — and editing it by UID resolves to the resource holding
      the whole series. Deleting is the same story with a worse ending: one DELETE
      against a series master removes every occurrence, past and future, and returns
      the same 204 as removing a single event.

    • A concurrent edit is refused, not overwritten. caldav sends If-Match
      from the ETag of an object it loaded, so an event changed on a phone between
      the read and the write comes back 412 and is left alone.

    • Upgrading grants deletion to anyone who had WRITABLE_CALENDARS set for
      0.7.0
      , with no configuration change. This is the roadmap's shared gate
      working as agreed, and it is worth saying out loud: the opt-in that previously
      meant "may add events" now also means "may remove them". Clearing the variable
      is still the off switch.

    Downloads