icskit-mcp
Server Configuration
Describes the environment variables required to run the server.
| Name | Required | Description | Default |
|---|---|---|---|
| ICSKIT_MCP_DIR | Yes | Path to the folder containing the .ics calendar files. This can also be passed as the --dir command-line option. |
Instructions
Guidance the server publishes about itself, which clients place ahead of the tool catalog so the model reads it before choosing anything.
Work with LOCAL .ics calendar files (exports from Google, Apple, Outlook, or a folder of .ics) under the directory the server was started with. No accounts, no network. Start with calendar_overview. ALWAYS pass tz (an IANA name such as Europe/Istanbul): every time in every output is shown in that zone and labelled with it. Read tools: agenda, search_events, find_free_time, find_conflicts, next_occurrences, export_agenda_markdown. Write tools (create_event, update_event, delete_event) never modify the original file by default: they write an edited copy (.new) or, with dry_run=true, only show the patch; pass in_place=true to change the original.
Capabilities
Features and capabilities supported by this server
Protocol revision2025-11-25
| Capability | Details |
|---|---|
| tools | {
"listChanged": false
} |
| prompts | {
"listChanged": false
} |
| resources | {
"subscribe": false,
"listChanged": false
} |
Tools
Functions exposed to the LLM to take actions
| Name | Description |
|---|---|
| calendar_overviewA | List the calendar files the server can see: for each file its name, how many events
and recurring series it holds, the date span of its events (as written in the file) and
the time zones its events use. Also lists files that failed to load and any events that
were skipped, with the reason. Call this first; the file names it prints are what the
|
| agendaA | Show every event occurrence between start and end, sorted, grouped by day, with recurring events already expanded (RRULE, EXDATE, RDATE and modified instances applied). start/end: '2026-03-02' or '2026-03-02T09:30' (optionally with Z or an offset). A
date-only |
| search_eventsA | Find events by text. All the filters you give must match (AND). query: case-insensitive text found in the title, description, location, categories or attendees. attendee: part of an attendee/organizer email or name. location: part of the location. regex: a Python regular expression (case-insensitive; ^ and $ match at the start and end of each field) searched in the same fields. Without start/end this lists each matching event or recurring series once (first start, recurrence rule, UID). With start AND end it lists the matching OCCURRENCES in that range, expanded, like agenda. tz sets the zone used to show times. |
| find_free_timeA | Find free slots of at least duration_minutes inside working hours, across one or several calendars at once. Returns every maximal free block (not chopped into duration-sized pieces), so a 3h gap is reported as one 3h block. start/end/tz: as in agenda (date-only end includes the whole day). working_hours: local wall-clock ranges in tz, e.g. '09:00-17:00' or '09:00-12:00,13:00-17:00' (so DST days keep their wall hours). days: working weekdays, e.g. 'Mon-Fri' or 'Mon,Wed,Fri'. buffer_minutes: padding kept clear before AND after every busy event. Busy means an uncancelled event; all-day events and events marked TRANSP:TRANSPARENT ('free') do not block unless include_all_day / include_transparent is true. |
| find_conflictsA | List pairs of events whose times overlap in the range, including across different calendars. Back-to-back events (one ends exactly when the next starts) are not conflicts. Cancelled events are ignored. All-day events and events marked TRANSP:TRANSPARENT ('free') are ignored unless include_all_day / include_transparent is true. start/end/tz as in agenda. |
| next_occurrencesA | The next n occurrences of one event (by UID) at or after |
| export_agenda_markdownA | Render seven days starting at week_start (a date such as 2026-03-02) as Markdown: one heading per day with a bullet per event, times labelled with the zone. Returns the Markdown. With output_file (a new .md file name under the server directory) it is also saved; an existing file is never overwritten. |
| create_eventA | Add an event to a calendar file. A UID and DTSTAMP are generated. calendar: file name from calendar_overview (a name that does not exist yet creates a new file; it must end in .ics). start: '2026-03-02' makes an ALL-DAY event (end, if given, is the LAST day, inclusive); '2026-03-02T09:30' makes a timed event, which needs end or duration_minutes. The times are read in tz and stored with that zone (a TZID reference; no VTIMEZONE block is added). rrule: e.g. 'FREQ=WEEKLY;BYDAY=MO,WE'. attendees: email addresses (recorded only; nothing is ever sent). status: TENTATIVE/CONFIRMED/CANCELLED. transp: OPAQUE (busy) or TRANSPARENT (free). SAFETY: the original file is never modified unless in_place=true. By default the edited copy is written next to it as '.new' (or output_file, which must not exist yet). dry_run=true writes nothing and just shows the patch. Everything else in the file - other events, VTIMEZONE blocks, unknown properties - is kept byte for byte. |
| update_eventA | Change fields of one event (the whole series if it recurs). Only the fields you pass change; an empty string removes summary-like fields (location, description, status, transp, rrule). attendees=[...] makes the attendee list exactly that (existing entries keep their details). start without end keeps the event's length. start/end are read in tz (default: the event's own zone); all-day rules as in create_event. DTSTAMP is refreshed and SEQUENCE incremented. Modifying single instances of a series is not supported. calendar: only needed if the UID is in several files. SAFETY: the original file is never modified unless in_place=true. By default the edited copy is written next to it as '.new' (or output_file, which must not exist yet). dry_run=true writes nothing and just shows the patch. Everything else in the file - other events, VTIMEZONE blocks, unknown properties - is kept byte for byte. |
| delete_eventA | Delete an event by UID (a recurring event with all its modified instances), or just one occurrence of a recurring event: pass occurrence='2026-03-09T09:00' (its start, read in tz; or a date for all-day) and an EXDATE is added instead. SAFETY: the original file is never modified unless in_place=true. By default the edited copy is written next to it as '.new' (or output_file, which must not exist yet). dry_run=true writes nothing and just shows the patch. Everything else in the file - other events, VTIMEZONE blocks, unknown properties - is kept byte for byte. |
Prompts
Interactive templates invoked by user choice
| Name | Description |
|---|---|
No prompts | |
Resources
Contextual data attached and managed by the client
| Name | Description |
|---|---|
No resources | |
TDQS
Scored across 10 tools
The three read tools (agenda, next_occurrences, search_events) overlap somewhat since all can expand recurring events over a range, but the descriptions draw clear boundaries: agenda is a range dump, next_occurrences targets one UID, search_events filters by text/attendee/location. The remaining tools (find_free_time, find_conflicts, export_agenda_markdown, create/update/delete_event, calendar_overview) each have an unmistakable purpose.
All names are snake_case and readable, with mutations consistently using verb_noun (create_event, update_event, delete_event) and searches using verb_noun (search_events, find_free_time, find_conflicts). The read tools agenda, calendar_overview and next_occurrences break the pattern by being noun- or prefix-style, but the convention still reads predictably.
Ten tools is a well-scoped surface for an iCalendar server: six query/analysis tools, one export, and exactly the three mutation tools needed. Nothing feels redundant or padded.
Full lifecycle is covered: discovery (calendar_overview), range/text lookup, free-busy and conflict analysis, export, plus create/update/delete with series-level EXDATE handling. Gaps remain: no way to read a single event's full record by UID, no per-instance modification of a series, and no tool to move/copy events between calendar files.