Skip to main content
Glama

kalendia-mcp

An MCP server for Kalendia. Drive your calendar sync and scheduling from an MCP client (Claude Code, Claude Desktop): list connections and calendars, read your agenda, and create / delete / run sync rules, all from chat.

Tools (24)

The tool surface mirrors what a user can do in the Kalendia web app (everything reachable by an API token; OAuth-redirect actions like connecting Google/Microsoft or Stripe checkout stay in the web UI, and account deletion is intentionally not exposed).

Reads (read-only):

  • list_connections — connected calendar accounts (Google, Microsoft, iCloud, ICS, Zoom).

  • list_calendars(connection_id) — live provider calendars under one connection (no Kalendia id).

  • list_all_calendars — every persisted calendar across accounts, with the numeric id used by rules/pages.

  • list_sync_rules — all mirror rules.

  • get_agenda(connection_id, calendar_id, from_iso?, to_iso?) — events in a window.

  • list_scheduling_pages / get_scheduling_page(page_id) — booking pages.

  • get_availability(slug, from_iso?, to_iso?) — bookable slots for a page.

  • get_billing / get_billing_overview — plan, usage, limits.

  • get_audit_log(limit?) — recent account activity.

Writes (the client confirms before firing):

  • Sync rules: create_sync_rule, run_sync_rule, delete_sync_rule (destructive).

  • Connections/calendars: connect_icloud, connect_ics, refresh_calendars, discover_calendars, set_active_calendars, rename_calendar, disconnect_connection (destructive).

  • Scheduling pages: create_scheduling_page, update_scheduling_page, delete_scheduling_page (destructive).

Related MCP server: calendar-mcp

Setup

  1. Mint a personal access token in Kalendia: Settings > API tokens. Copy it (shown once; starts with kld_).

  2. Install deps: uv sync.

  3. Configure the env: copy .env.example to .env and set KALENDIA_TOKEN (and KALENDIA_API_URL if testing against a local backend).

Run

uv run kalendia-mcp          # stdio (default)
uv run kalendia-mcp --http   # Streamable HTTP

Register in Claude Code

Add to your MCP config (e.g. ~/.claude.json), filling in the token:

{
  "mcpServers": {
    "kalendia": {
      "command": "uv",
      "args": ["run", "--directory", "/Users/bogdan-georgebrad/Claude/Code/personal/kalendia-mcp", "kalendia-mcp"],
      "env": {
        "KALENDIA_TOKEN": "kld_your_token_here",
        "KALENDIA_API_URL": "https://api.kalendia.io"
      }
    }
  }
}

To test against a local backend instead, set KALENDIA_API_URL to http://localhost:8002 and use a token minted on that backend.

Remote / multi-user (HTTP)

uv run kalendia-mcp --http runs the server over Streamable HTTP as an OAuth 2.0 resource server, so ONE deployed instance serves many users: each request authenticates with the caller's own kld_ token (no per-user env var). It serves OAuth Protected Resource Metadata at /.well-known/oauth-protected-resource (advertising Kalendia as the authorization server), rejects unauthenticated/invalid tokens with 401 + WWW-Authenticate, validates a presented kld_ token against the Kalendia API, and forwards it to act as that user. Config via env: KALENDIA_MCP_HOST / KALENDIA_MCP_PORT (bind), KALENDIA_MCP_URL (public URL in the metadata), KALENDIA_API_URL (the Kalendia API).

Remaining for a public launch (tracked as Phase 2): hosting/deploy of this HTTP server, and full OAuth authorization-server discovery (dynamic client registration + auth-code + PKCE) so clients can mint tokens in-flow instead of pasting a kld_ token. Today a client connects by presenting a kld_ bearer (minted in Settings > API tokens).

Develop

uv run ruff check . && uv run ruff format --check . && uv run pyright && uv run pytest

Security

A token has the user's full account access (no scopes yet). Treat it like a password. Revoke a leaked or stale token in Kalendia: Settings > API tokens.

Available Tools

27 tools
cancel_eventA
Destructive

Cancel (delete) one event. Cannot be undone.

connection_id from list_connections; calendar_id from list_calendars; event_id from get_agenda. notify=True (default) sends a cancellation to the attendees. An unknown event returns an error on Google / Microsoft; iCloud treats an already-deleted event as success.

Inputs: event_id (required), calendar_id (required), connection_id (required), notify. Returns: object

connect_icloudA

Connect an Apple iCloud calendar. Requires an app-specific password (NOT your normal Apple password; generate one at appleid.apple.com under Sign-In and Security). Credentials are validated before saving. Returns the new connection.

Inputs: apple_id (required), app_specific_password (required). Returns: object

connect_icsA

Connect a read-only calendar from a public ICS feed URL (an https/webcal .ics link). The feed is fetched and parsed to validate it. An ICS calendar can be a sync source but not a target.

Inputs: feed_url (required). Returns: object

create_eventA

Create a calendar event.

connection_id from list_connections; calendar_id is a writable calendar's provider_calendar_id from list_calendars. start_iso / end_iso are ISO-8601 datetimes (e.g. "2026-06-20T14:00:00Z"; naive is read as UTC). attendees is a list of email addresses; notify=True (default) emails them an invitation. add_conference=True attaches a native video link (Google Meet / Teams). The target must be writable and must NOT be the target of a sync rule. Returns {event: {...}}.

Inputs: title (required), end_iso (required), start_iso (required), calendar_id (required), +7 more. Returns: object

create_scheduling_pageA

Create a scheduling (booking) page.

slug is the public URL suffix (unique per account). availability_window is {"timezone": "<IANA, e.g. Europe/Bucharest>", "weekly": {"mon": [["09:00","17:00"]], "tue": [...]}} where each day key (mon..sun) maps to a list of [start, end] "HH:MM" intervals; omit a day for no availability. source_calendar_ids are checked for conflicts; target_calendar_id (must be writable and active) is where confirmed bookings are created. video_provider is one of none | google_meet | teams | zoom (zoom also needs zoom_connection_id).

Inputs: slug (required), title (required), duration_minutes (required), target_calendar_id (required), +9 more. Returns: object

create_sync_ruleA

Create a sync rule that mirrors events from source_calendar_id onto target_calendar_id.

Calendar ids come from list_calendars; the target must be writable. visibility_mode is one of "busy_only" (hide all details, default), "title_only", or "full_details". mirror_prefix is an optional string prepended to mirrored event titles (e.g. "[Personal]"). Set enabled=False to create it paused. Mirrors the source's current events into the target on creation.

Inputs: source_calendar_id (required), target_calendar_id (required), enabled, mirror_prefix, +1 more. Returns: object

delete_scheduling_pageA
Destructive

Delete a scheduling page (page_id from list_scheduling_pages). Cannot be undone.

Inputs: page_id (required). Returns: object

delete_sync_ruleA
Destructive

Delete a sync rule (rule_id from list_sync_rules). The mirrored events it created on the target calendar are cleaned up afterward by Kalendia. This cannot be undone.

Inputs: rule_id (required). Returns: object

disconnect_connectionA
Destructive

Disconnect a calendar account: permanently deletes the connection and its stored credentials, pauses any booking pages that targeted its calendars, and removes sync rules that used them. The provider OAuth grant is revoked best-effort. Cannot be undone (you would reconnect from scratch). Returns which pages were paused and how many rules were removed.

Inputs: connection_id (required). Returns: object

discover_calendarsA

Re-discover the calendars under one connection (connection_id from list_connections). Newly visible calendars are auto-activated up to the plan's calendar limit.

Inputs: connection_id (required). Returns: result

get_agendaA
Read-only

Read events from one calendar in a time window.

connection_id comes from list_connections; calendar_id is a calendar's provider_calendar_id from list_calendars (e.g. an email, or "primary"). from_iso / to_iso are ISO-8601 datetimes (e.g. "2026-06-15T00:00:00Z"); omit them to use Kalendia's default window. Returns {events: [...]}.

Inputs: calendar_id (required), connection_id (required), to_iso, from_iso. Returns: object

get_audit_logA
Read-only

Get recent account audit-log entries, most recent first: id, action, target_type, target_id, payload, created_at. limit is clamped to 1..500.

Inputs: limit. Returns: result

get_availabilityA
Read-only

Get bookable slots for a scheduling page by its slug, optionally within an ISO-8601 UTC window (from_iso/to_iso). Returns {slug, title, duration_minutes, slots: [ISO datetimes]}. Omit the window for the page's full horizon.

Inputs: slug (required), to_iso, from_iso. Returns: object

get_billingA
Read-only

Get the current plan and subscription status: plan, tier, subscription_status, current_period_end, is_comp.

Inputs: no named parameters. Returns: object

get_billing_overviewA
Read-only

Get plan plus current usage and limits: usage {calendars, scheduling_pages} against limits {max_calendars, max_scheduling_pages, teams_app, custom_domain} (null limit = unlimited).

Inputs: no named parameters. Returns: object

get_scheduling_pageA
Read-only

Get one scheduling (booking) page's full config, including its availability_window. Fetch this before update_scheduling_page, since update is a full replace.

Inputs: page_id (required). Returns: object

list_all_calendarsA
Read-only

List every calendar across all connected accounts as a flat list (id, connection_id, provider_calendar_id, display_name, custom_name, is_primary, is_writable, sync_enabled). Use the calendar id as a sync-rule source/target or a scheduling-page calendar.

Inputs: no named parameters. Returns: result

list_calendarsA
Read-only

List the LIVE provider calendars under one connection (a fresh fetch from the provider): each item is name, provider_calendar_id (use this as calendar_id in get_agenda), is_primary, is_writable, is_hidden. This is the provider view and has NO Kalendia calendar id; to get the numeric calendar id needed for sync rules and scheduling pages, use list_all_calendars.

Inputs: connection_id (required). Returns: result

list_connectionsA
Read-only

List the user's connected calendar accounts (Google, Microsoft, iCloud, ICS, Zoom).

Each item: id, provider, email, status, scopes. Use the id with list_calendars.

Inputs: no named parameters. Returns: result

list_scheduling_pagesB
Read-only

List the user's scheduling (booking) pages.

Inputs: no named parameters. Returns: result

list_sync_rulesA
Read-only

List all sync (mirror) rules. Each rule mirrors events from a source calendar onto a target calendar. Returns id, source_calendar_id, target_calendar_id, visibility_mode, mirror_prefix, enabled.

Inputs: no named parameters. Returns: result

refresh_calendarsA

Re-discover calendars across ALL connected accounts in one call (picks up calendars created since the account was connected). A connection whose provider errors is skipped, not fatal.

Inputs: no named parameters. Returns: result

rename_calendarA

Set a friendly display name for a calendar (calendar_id from list_calendars). Pass null or an empty string to clear the override and fall back to the provider's name.

Inputs: calendar_id (required), custom_name. Returns: object

reschedule_eventA

Reschedule one event to a new time window (the core "move this call" action).

connection_id from list_connections; calendar_id from list_calendars; event_id is the event's provider_event_id from get_agenda. For a recurring meeting, pass the specific occurrence's id (get_agenda returns each occurrence separately) to move just that one. start_iso / end_iso are ISO-8601 datetimes. Only the time changes: title, attendees, location, and the video link are preserved. notify=True (default) emails the attendees about the new time. Returns {event: {...}}.

Inputs: end_iso (required), event_id (required), start_iso (required), calendar_id (required), +2 more. Returns: object

run_sync_ruleA

Run a sync rule now (a full reconcile that backfills the target from the source). Returns counts of events created / updated / deleted, plus any per-event errors.

Inputs: rule_id (required). Returns: object

set_active_calendarsA

Set which calendars under a connection are active (synced and usable in rules/pages). This REPLACES the full active set for that connection. Note: passing an empty list would disconnect the whole account, so this tool rejects an empty list; use disconnect_connection for that.

Inputs: connection_id (required), active_calendar_ids (required). Returns: object

update_scheduling_pageA

Update a scheduling page. This is a FULL REPLACE: call get_scheduling_page first, change the fields you want, and pass every field back (any omitted optional field resets to its default). Set is_active=False to pause the page. Field meanings match create_scheduling_page.

Inputs: slug (required), title (required), page_id (required), duration_minutes (required), +11 more. Returns: object

Tool Schema Changelog

Recent tool additions, removals, and schema changes observed during successful MCP inspections.

Loading tool schema changelog…

    TDQS

    A3.9/5.0

    Scored across 27 tools

    Disambiguation4/5

    Most tools target distinct resources and actions, with clear descriptions. A few pairs overlap slightly (refresh_calendars vs. discover_calendars, get_billing vs. get_billing_overview), but the usage context is explained well enough to avoid major confusion.

    Naming Consistency5/5

    All tool names consistently follow a verb_noun pattern in lowercase snake_case (e.g., list_connections, create_event, delete_scheduling_page). The use of provider names in connect_ics/connect_icloud is a natural and predictable deviation.

    Tool Count2/5

    With 27 tools, the set exceeds the 25-tool threshold considered too many for most servers. While the domain is broad, several tools could be consolidated (e.g., billing tools, refresh/discover), making the count feel inflated rather than well-scoped.

    Completeness3/5

    The server provides solid coverage for connections, calendars, sync rules, events, and scheduling pages, with full CRUD for scheduling pages. However, sync rules lack an update endpoint, and event editing is limited to rescheduling (no metadata updates), which are notable gaps for a calendar service.

    Maintenance

    ActivityInactive
    ResponsivenessNo recent community issues

    Related MCP Connectors

    Related MCP Servers