Skip to main content
Glama
jbeker

calendar-mcp

calendar-mcp

An MCP server that exposes read-only calendar data from iCal (.ics) feed URLs to an MCP-compatible agent.

You point it at one or more iCal URLs. It fetches them on a regular interval, expands recurring events, caches everything in memory, and offers tools an agent can call to answer questions like "what's on my calendar this week?", "am I free Thursday afternoon?", or "find events mentioning 'dentist'".

It never writes to any calendar — iCal feeds are pull-only sources.

Install

Requires Python ≥ 3.11 and uv.

uv sync

Related MCP server: iCloud Calendar MCP

Configure

Copy the example config and edit it:

cp calendars.example.toml calendars.toml
refresh_interval_minutes = 15          # how often to re-fetch (min 5)
default_timezone = "America/New_York"  # applied to floating times & agenda math
max_results = 200                      # cap on events returned per tool call

[[calendars]]
name = "Work"
url  = "https://example.com/work.ics"

[[calendars]]
name = "Family"
url  = "https://calendar.google.com/.../basic.ics"

Feed URLs are secrets. Google/Apple/Outlook "private address" URLs embed an access token. The server never logs or exposes the URL — only the friendly name appears in output. calendars.toml is git-ignored; keep it private.

Run

uv run calendar-mcp calendars.toml

The server speaks the MCP stdio protocol, so it's normally launched by your MCP client rather than by hand. Register it like this:

{
  "mcpServers": {
    "calendar": {
      "command": "uv",
      "args": ["run", "calendar-mcp", "/absolute/path/to/calendars.toml"]
    }
  }
}

Tools

Tool

What it does

list_calendars

Each feed's refresh state, event count, last success (no URLs).

list_events

Events in a window (recurrences expanded), sorted by start.

get_event

Full detail (description, attendees) for one event UID.

search_events

Case-insensitive match on summary/description/location.

free_busy

Merged busy intervals and the free gaps between them.

agenda

Convenience: today | tomorrow | week | next.

refresh

Force an immediate re-fetch of all feeds.

Times are ISO-8601. If you omit a window, a sensible default is used. Results are capped at max_results and flagged truncated when trimmed, so large calendars don't overrun the agent's context.

Design notes

  • Recurrence (RRULE/EXDATE/RECURRENCE-ID overrides) is expanded by recurring-ical-events.

  • Timezones: UTC, TZID, and floating times are all normalized to timezone-aware ISO-8601; floating times get default_timezone.

  • Resilience: a feed that fails to fetch or parse keeps its last-known-good data and records the error in its status — it never wipes the cache or crashes the refresh loop.

  • HTTP efficiency: ETag/Last-Modified are stored and sent as conditional requests, so an unchanged feed returns 304 and skips re-parsing.

Develop

uv run pytest        # unit tests + fixtures

Not in scope (v1)

Writing/creating events, tasks (VTODO) and reminders (VALARM), an HTTP/daemon transport, and on-disk cache persistence across restarts.

License

Copyright (C) 2026 Jeremy Beker.

This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.

It is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details.

Related MCP Connectors

Related MCP Servers