| name | ical |
| description | Read and manage Apple Calendar.app events on macOS. Use when the user asks about their schedule, wants to see upcoming events, create a new event, update or delete an existing event, accept/decline invites, check what's on today, find events in a date range, or list their calendars. Requires: macOS 13+. |
| argument-hint | calendar task — e.g. 'what's on today', 'create meeting Friday 2pm', 'open invite to accept it' |
| allowed-tools | Bash(aical *) |
ical
Full management of Apple Calendar.app via the bundled aical CLI (universal binary, no credentials, no CalDAV API keys). The plugin system adds aical to PATH automatically.
Requirements
- macOS 13+ (Intel or Apple Silicon)
- Calendar.app open and running
- Plugin installed via the Claude Code marketplace
On first aical invocation, macOS will prompt for Calendar.app automation permission — tell the user to click Allow.
List calendars
aical calendars
Returns: id, name, writable, description per calendar. Run this first if you don't know a calendar name — never guess.
List events
aical events list [--calendar "Name"] [--today] [--days N] \
[--from YYYY-MM-DD] [--to YYYY-MM-DD] \
[--limit N]
- Default: events starting today through the next 7 days, all calendars
--today — only today's events
--days N — next N days from today (default 7)
--from / --to — explicit date range (YYYY-MM-DD)
--calendar — restrict to one calendar by exact name
--limit — max events returned (default 50; 0 = unlimited)
Output fields per event: uid, summary, start, end, allDay, location, notes, status, url, calendar
Get full event details (with attendees)
aical events get <uid> [--calendar "Name"]
Returns complete event detail including the attendees array (name, email, participation status).
--calendar is optional but speeds up the search when provided.
Use this to inspect an invite: check who is attending and their RSVP status.
Create an event
aical events create --calendar "Name" --summary "Title" \
--start YYYY-MM-DDTHH:MM:SS --end YYYY-MM-DDTHH:MM:SS \
[--allday] [--location "..."] [--notes "..."] [--url "..."]
--calendar must be a writable calendar (check aical calendars for writable: true)
- Date format:
2026-05-08T14:00:00 for timed events, 2026-05-08 for all-day
--allday marks the event as all-day (time components ignored)
- Returns:
uid, summary, start, end, allDay, calendar
Before creating: show the user a plain-English summary (calendar, title, date/time) and confirm.
Update an event
aical events update <uid> [--calendar "Name"] \
[--summary "New Title"] \
[--start YYYY-MM-DDTHH:MM:SS] [--end YYYY-MM-DDTHH:MM:SS] \
[--allday true|false] \
[--location "..."] [--notes "..."] [--url "..."] \
[--status confirmed|cancelled|tentative|none]
Only the flags you provide are changed. --calendar restricts the search to one calendar (faster).
Before updating: show the current event details and the proposed changes, then confirm.
Delete an event
aical events delete <uid> [--calendar "Name"] --confirm
--confirm is required to prevent accidental deletion. Show the event summary to the user and ask before calling this.
Open an event in Calendar.app (accept/decline invites)
aical events open <uid> [--calendar "Name"]
Opens the event in the Calendar.app GUI and brings Calendar.app to the front. Use this when the user wants to accept or decline an invite — Calendar.app shows the RSVP buttons. This is the correct way to handle invite responses (participation status cannot be changed via AppleScript).
JSON output
All commands emit a JSON envelope when piped or when --format json is passed:
{
"ok": true,
"data": <T>,
"error": { "code": "...", "message": "..." } | null,
"meta": { "command": "...", "timestamp": "..." }
}
Use --pretty for formatted JSON.
aical events list --today --format json
aical events list --from 2026-05-01 --to 2026-05-31 --format json | jq '.data[] | select(.calendar == "Work") | .summary'
aical events get <uid> --format json | jq '.data.attendees'
aical calendars --format json | jq '.data[].name'
Common patterns
What's on today?
aical events list --today
This week:
aical events list --days 7
Specific calendar, next 30 days:
aical events list --calendar "Work" --days 30
Who's invited to a meeting?
aical events get <uid> --format json | jq '.data.attendees[] | {name, email, status}'
Create a 1-hour meeting:
aical events create --calendar "Work" --summary "1:1 with Alice" \
--start 2026-05-09T14:00:00 --end 2026-05-09T15:00:00
Reschedule a meeting:
aical events update <uid> --start 2026-05-09T15:00:00 --end 2026-05-09T16:00:00
Accept/decline an invite:
aical events open <uid>
Then use the RSVP buttons in Calendar.app.
Guidelines
- Always run
aical calendars first if you don't know a calendar name — never guess it
--calendar on get/update/delete/open speeds up the UID search; provide it when you know the calendar
- Use
--format json whenever you need to extract specific fields or filter output
- Before creating or updating, show the user a plain-English summary and confirm
delete is irreversible — always confirm with the user before calling with --confirm
- Participation status (accept/decline) is read-only via AppleScript; use
events open for invite handling
- If
ok is false, surface the error.message to the user and stop
- Calendar.app must be open; if an osascript error occurs, ask the user to open Calendar.app