-
released this
2026-08-09 15:27:21 -05:00 | 103 commits to main since this releaseFirst release that can change the calendar. Everything before this was read-only by
construction; from here it is read-only by configuration, and the configuration
defaults to read-only.Added
-
create_event— creates one non-recurring event on a calendar named in the new
WRITABLE_CALENDARSallowlist. Accepts a finish time as eitherendor
duration_minutes, handles all-day events, and echoes the whole created event back
rather than an acknowledgement, so a timezone or all-day misunderstanding shows up in
the transcript instead of on someone's phone a day later. -
WRITABLE_CALENDARS, empty by default — and empty means the tool is not
registered, not that it refuses. An offered tool is one the model will promise the
user before it discovers it cannot deliver, so with this unset the served tool surface
is identical to 0.6.0.Separate from
CALENDARSrather than a flag on it: reading a shared household
calendar to answer "when is everyone free" is not consent to write to it. -
A duplicate guard. A create whose response the caller never saw — a timeout on the
write — leaves the event created and the model convinced it was not, and the retry
books the meeting twice. An event with the same title and start is refused, and the
refusal carries the existing event so the user can be told what is already booked.
allow_duplicate=trueoverrides it. Best-effort by design: if the check cannot read
the calendar the create still proceeds, because turning iCloud throttling into an
inability to add anything is a worse failure than the one being prevented. -
A
writablepreflight check andwritable_calendarson the boot line. A name in
the allowlist matching no readable calendar registers a tool that refuses every call,
which is invisible otherwise. Reported as a failure but never fatal — a typo in the
write allowlist must not take the read path down with it. -
mcp_gateway/calendar/compose.py, the inverse ofexpand.pyand network-free for
the same reason. Two things in it are the ones worth getting right:DTENDis
exclusive, so a one-day all-day event ends on the following date — writing the same
date twice yields a zero-length event most clients render as nothing at all. And timed
events are written in UTC rather than with aTZID, because aTZIDreference is
only legal alongside a matchingVTIMEZONEcomponent; these are single non-recurring
instances, so there is no future DST transition for a zone rule to get right.
Changed
-
Calendar.read_onlyreports the truth instead of a hardcodedtrue. It is how
the model knows which calendar to offer. Subscriptions are always read-only — a feed is
an HTTPS GET against someone else's server, and naming one inWRITABLE_CALENDARS
does not change that. -
The server instructions name the writable calendars and, just as explicitly, the
limits: nothing can be edited or deleted, and repeating events cannot be created.
Stating the capability without the limits invites the model to offer the rest.
Security
-
The credential was never the boundary and still is not. An app-specific password
has granted full CalDAV read and write since the first release; what changed is that
this code will now use it.WRITABLE_CALENDARSis the boundary, it is enforced before
iCloud is contacted, and it is checked before the calendar name is even looked up — so
a real calendar that is merely not writable does not come back as "no such calendar". -
Resource URLs are never logged or returned. They carry the account DSID, and tool
output reaches the model.
Downloads
-
Source code (ZIP)
0 downloads
-
Source code (TAR.GZ)
0 downloads
-