# calendar-mcp [Health: Active]

**Category:** 🏢 Workplace & Productivity  
**Repository:** https://github.com/deciduus/calendar-mcp  
**GitHub Stars:** 26  
**Views:** 3  
**Installs:** 0  
**Upvotes:** 0  
**Directory Page:** https://allmcps.com/mcp/calendar-mcp-2

## Description
Google Calendar for LLM agents: find, create, update, move, delete events; free/busy; scheduling.

## Tools
Capabilities this server exposes over MCP:

- **list_accounts** — List the Google accounts calendar-mcp can use, and which is the default.

    Every other tool takes an optional `account` argument naming one of these.
    An account whose `valid` is false needs `calendar-mcp auth --account <name>`
    run once in a terminal before its calendars can be read.
    
- **get_preferences** — Read the user's scheduling preferences: working hours, lunch, buffers, focus.

    Consult this before proposing meeting times or hunting for focus blocks,
    rather than assuming a nine-to-five. Never configured returns the defaults
    (Mon-Fri 09:00-17:00, no lunch break, no buffer), which is a reasonable
    guess but worth confirming with the user before you rely on it.
    
- **set_preferences** — Update the user's scheduling preferences and save them. Returns the merged result.

    Only the arguments you pass change; everything else keeps its current value,
    so a single correction ("I actually start at 8") does not have to restate
    the whole schedule. The merged result is validated before it is written, so
    a rejected change leaves the saved preferences untouched.

    Args:
        timezone: IANA timezone the working hours are expressed in, e.g.
            'Europe/Berlin'. Must be a real zone name.
        working_hours: Whole-schedule replacement, keyed by weekday
            ('mon'..'sun'), each value a list of ['HH:MM', 'HH:MM'] pairs. Days
            you omit become non-working, so pass every working day at once, e.g.
            {"mon": [["09:00", "12:00"], ["13:00", "18:00"]], "fri": [["09:00", "15:00"]]}.
        buffer_minutes: Minimum gap to leave before and after each meeting when
            proposing times. 0 disables it.
        min_focus_block_minutes: Shortest free stretch that still counts as
            usable focus time.
        lunch: Daily lunch break as ['HH:MM', 'HH:MM'], carved out of the
            working hours.
        focus_calendar_id: Calendar that focus blocks are booked on; 'primary'
            for the user's own.
        clear_lunch: Set true to remove an existing lunch break (pass this
            instead of `lunch` when the user says they no longer want one).
    
- **analyze_busyness** — Summarise how loaded each day is: event count and total scheduled minutes.

    Use this to answer "how busy is my week" without listing every event.

    Args:
        time_min: Start of the window, ISO 8601.
        time_max: End of the window, ISO 8601.
        calendar_id: Calendar to analyse.
        account: Account name from 'calendar-mcp accounts'; omit for the default.
    
- **project_recurring_events** — Compute future occurrences of recurring events from their recurrence rules.

    Unlike `find_events`, this expands the RRULEs locally, so it reaches past the
    horizon Google materialises instances for -- useful for "when do my birthdays
    / standups land next year".

    Args:
        time_min: Start of the projection window, ISO 8601.
        time_max: End of the projection window, ISO 8601.
        calendar_id: Calendar whose recurring events should be projected.
        event_query: Only project recurring events matching this text, e.g. 'Birthday'.
        account: Account name from 'calendar-mcp accounts'; omit for the default.
    
- **time_audit** — Report where the user's time went: meeting hours, who with, and what focus time is left.

    Answers "how much of my week is meetings?", "who am I spending my time
    with?" and "where did my focus time go?" in one pass. Working hours, lunch,
    buffer and the minimum focus block come from the saved preferences
    (see get_preferences), so the percentages reflect this user's actual day.

    Declined meetings, events marked 'free', and all-day entries are excluded by
    default; the 'excluded' field says how many were skipped.

    Args:
        time_min: Start of the window to audit, ISO 8601.
        time_max: End of the window to audit, ISO 8601.
        calendar_ids: Calendars to include. Defaults to ['primary'].
        group_by: Break the window down by 'day' or by ISO 'week'.
        include_all_day: Count all-day events as meeting time.
        include_declined: Count meetings the user declined.
        account: Account name from 'calendar-mcp accounts'; omit for the default.
    
- **list_calendars** — List the calendars the signed-in user can see, with their IDs and timezones.

    Start here whenever the user mentions a calendar other than their own: the
    `id` values returned are what every other tool's `calendar_id` expects.

    Args:
        min_access_role: Only return calendars where the user has at least this
            role: 'freeBusyReader', 'reader', 'writer' or 'owner'. Omit for all.
        account: Account name from 'calendar-mcp accounts'; omit for the default.
    
- **create_calendar** — Create a new secondary calendar owned by the user.

    Args:
        summary: Name for the new calendar, e.g. 'Client work'.
        account: Account name from 'calendar-mcp accounts'; omit for the default.
    
- **find_focus_time** — Find the uninterrupted blocks in a window that could be used for deep work.

    Searches inside the user's working hours (`get_preferences`), removes lunch,
    everything already booked on their calendars, and the buffer they like
    around meetings, then reports what is left -- longest block first, because
    that is the one worth protecting. Blocks shorter than
    `min_focus_block_minutes` are not reported at all.

    Availability must be readable for every requested calendar; otherwise this
    returns an error rather than unverified free time.
    Read-only: use `block_focus_time` to actually defend the time.

    Args:
        time_min: Start of the window to search, ISO 8601. Without a UTC offset
            it is read in the calendar's own timezone.
        time_max: End of the window to search, ISO 8601.
        hours_needed: How many focus hours the user is trying to find. Used to
            report whether the window can supply them; pass 0 to just look.
        calendar_ids: Calendars whose events count as busy. Omit for every
            calendar the account has selected.
        account: Account name from 'calendar-mcp accounts'; omit for the default.
    
- **block_focus_time** — Book the best free blocks in a window as focus time, until the hours add up.

    Takes the blocks `find_focus_time` would report, longest first, and creates
    an event on each until `hours_needed` is covered. The last block is trimmed
    to what is still needed rather than swallowing a whole afternoon. Events are
    created busy, with reminders off and without notifying anyone.

    Run it with `dry_run` first when the user has not yet agreed to the times.
    Both preview and booking fail before writing if any checked calendar has
    unknown availability. The destination calendar is always checked too.

    Args:
        time_min: Start of the window to book inside, ISO 8601.
        time_max: End of the window, ISO 8601.
        hours_needed: How many focus hours to book.
        title: Title for the blocks. Default 'Focus time'.
        calendar_id: Calendar to book on. Omit for the user's configured
            focus_calendar_id (see `get_preferences`).
        check_calendar_ids: Calendars whose events count as busy. Omit for every
            calendar the account has selected.
        description: Optional note to put in each block.
        dry_run: True to report the blocks that would be booked without writing
            anything to the calendar.
        account: Account name from 'calendar-mcp accounts'; omit for the default.
    
- **detect_conflicts** — Find double-booked events in a window, across every signed-in account.

    Reports two things. `conflicts` are genuine overlaps -- the user cannot be
    in both places. `tight` transitions do not overlap but leave less than the
    buffer from `get_preferences`, which is what makes a day feel impossible.

    Events the user declined, events marked free, cancelled events and (unless
    `include_all_day` is set) all-day events are ignored, because none of them
    actually occupy the user.

    Args:
        time_min: Start of the window to check, ISO 8601.
        time_max: End of the window to check, ISO 8601.
        accounts: Account names to check. Omit for every signed-in account,
            which is the point of the tool -- a work meeting clashing with a
            personal one is invisible from either calendar alone.
        calendar_ids: Restrict to these calendars (in every account checked).
            Omit for each account's selected calendars.
        include_all_day: True to let all-day events clash with timed ones.
        max_events_per_calendar: Safety limit per calendar. Default 250.
    
- **suggest_reschedule** — Propose better times for an existing meeting, ranked, keeping its length.

    Reads the event, then looks for slots of the same duration inside the user's
    working hours where the organiser is free, and ranks them: fewest attendee
    conflicts first, the event's current day next, then earliest. Slots that sit
    closer to another meeting than the user's buffer allows are penalised, not
    hidden.

    Read-only by default -- it suggests, the user decides. Set `apply` to move
    the event to the top suggestion once they have agreed to it.

    Args:
        event_id: The event to reschedule (from `find_events`).
        calendar_id: Calendar the event lives on.
        search_days: How many days ahead to search. Default 7, maximum 60.
        max_suggestions: How many times to propose. Default 5, maximum 20.
        search_from: Start searching from this time, ISO 8601. Defaults to now.
        apply: True to actually move the event to the best suggestion. Only set
            this when the user has agreed to the time.
        account: Account name from 'calendar-mcp accounts'; omit for the default.
    
- **find_events** — Search a calendar for events, expanding recurring series into instances.

    Use this before any tool that needs an `event_id`. Narrow the window with
    `time_min`/`time_max` rather than raising `max_results`.

    Args:
        calendar_id: Calendar to search. 'primary' is the user's own calendar.
        time_min: Inclusive start of the window, ISO 8601 with a UTC offset.
            Without an offset it is read in the calendar's own timezone.
        time_max: Exclusive end of the window, ISO 8601.
        query: Free-text search over title, description, location and attendees.
        max_results: Maximum events to return (default 50).
        account: Account name from 'calendar-mcp accounts'; omit for the default.
    
- **check_attendee_status** — Report who has accepted, declined or not yet answered an event invitation.

    Args:
        event_id: The event to inspect (from `find_events`).
        calendar_id: Calendar the event lives on.
        attendee_emails: Restrict the report to these addresses. Omit for all.
        account: Account name from 'calendar-mcp accounts'; omit for the default.
    
- **create_event** — Create an event with explicit start and end times, and optional attendees.

    For a one-line natural-language description ("coffee with Sam tomorrow at 3")
    prefer `quick_add_event`.

    Args:
        calendar_id: Calendar to create the event on.
        summary: Event title. Required.
        start_time: Start, ISO 8601 with a UTC offset (e.g. '2026-03-14T15:00:00-04:00').
            Without an offset it is read in the calendar's own timezone.
        end_time: End, ISO 8601, same convention as start_time.
        description: Longer notes for the event body.
        location: Free-text location or meeting link.
        attendee_emails: People to invite. They receive an invitation email
            unless send_notifications is false.
        send_notifications: Whether Google emails the attendees. Default true.
        account: Account name from 'calendar-mcp accounts'; omit for the default.
    
- **quick_add_event** — Create an event from a plain-English phrase, parsed by Google.

    Google interprets the date, time and title itself, in the calendar's own
    timezone. Check the returned start/end and tell the user what was booked --
    the parser guesses, and does not handle attendees or descriptions.

    Args:
        text: The phrase to parse, e.g. 'Dentist Thursday 9am' or
            'Team sync every Monday at 10 for 30 minutes'.
        calendar_id: Calendar to create the event on.
        account: Account name from 'calendar-mcp accounts'; omit for the default.
    
- **update_event** — Change fields on an existing event. Omitted fields are left untouched.

    To reschedule while keeping the duration, use `move_event` instead: changing
    only `start_time` here leaves the old end time in place.

    Args:
        calendar_id: Calendar the event lives on.
        event_id: The event to update (from `find_events`). Required.
        summary: New title.
        start_time: New start, ISO 8601.
        end_time: New end, ISO 8601.
        description: New description.
        location: New location.
        send_notifications: Whether Google emails the attendees. Default true.
        account: Account name from 'calendar-mcp accounts'; omit for the default.
    
- **move_event** — Reschedule an event, and/or move it to a different calendar.

    Supplying only `new_start` keeps the event's original duration -- this is the
    right tool for "push my 2pm back an hour". Supplying
    `destination_calendar_id` transfers the event between calendars.

    Args:
        event_id: The event to move (from `find_events`).
        calendar_id: Calendar the event currently lives on.
        new_start: New start, ISO 8601. Duration is preserved if new_end is omitted.
        new_end: New end, ISO 8601. Optional when new_start is given.
        destination_calendar_id: Calendar to transfer the event to.
        send_notifications: Whether Google emails the attendees. Default true.
        account: Account name from 'calendar-mcp accounts'; omit for the default.
    
- **add_attendee** — Invite one or more people to an existing event.

    Existing attendees are kept; the new addresses are appended.

    Args:
        event_id: The event to invite people to (from `find_events`).
        attendee_emails: Email addresses to invite.
        calendar_id: Calendar the event lives on.
        send_notifications: Whether Google emails the attendees. Default true.
        account: Account name from 'calendar-mcp accounts'; omit for the default.
    
- **respond_to_event** — Set the user's own RSVP on an invitation they have received.

    Only works on events the user is an attendee of; it does not change anyone
    else's response.

    Args:
        event_id: The invitation to answer (from `find_events`).
        response_status: 'accepted', 'declined', 'tentative' or 'needsAction'.
        calendar_id: Calendar the invitation lives on.
        comment: Optional note sent to the organizer with the RSVP.
        send_notifications: Whether Google emails the organizer. Default true.
        account: Account name from 'calendar-mcp accounts'; omit for the default.
    
- **delete_event** — Permanently delete an event. This cannot be undone.

    Clients that support elicitation are asked to confirm first; if the user
    declines, nothing is deleted and `deleted` comes back false.

    Args:
        event_id: The event to delete (from `find_events`).
        calendar_id: Calendar the event lives on.
        send_notifications: Whether Google emails the attendees that it was
            cancelled. Default true.
        account: Account name from 'calendar-mcp accounts'; omit for the default.
    
- **query_free_busy** — Get busy intervals for one or more calendars, without revealing event details.

    Works for other people's calendars addressed by email, which is how you check
    whether someone is free before proposing a time.

    Args:
        calendar_ids: Calendar IDs or attendee email addresses to check.
        time_min: Start of the window, ISO 8601 with a UTC offset.
        time_max: End of the window, ISO 8601 with a UTC offset.
        account: Account name from 'calendar-mcp accounts'; omit for the default.
    
- **schedule_mutual** — Find the first slot where everyone is free, then book the meeting there.

    Reads each attendee's free/busy inside the window, picks the earliest gap
    that fits `duration_minutes`, and creates the event with all of them invited.
    Refuses to book if any attendee or organizer availability is unknown.
    Fails with an error if no common slot exists -- widen the window or shorten
    the meeting and try again.

    By default the search obeys the user's own settings from `get_preferences`:
    their working hours per weekday (so nothing lands on a day off), their lunch
    break, and the buffer they want around meetings. `working_hours_start` and
    `working_hours_end` narrow that further; they never widen it.

    Args:
        attendee_calendar_ids: Attendee email addresses whose availability matters.
        time_min: Earliest the meeting may start, ISO 8601 with a UTC offset.
        time_max: Latest the meeting may end, ISO 8601 with a UTC offset.
        duration_minutes: Length of the meeting in minutes.
        summary: Title for the meeting.
        description: Optional agenda or notes.
        location: Optional location or meeting link.
        organizer_calendar_id: Calendar the meeting is created on.
        working_hours_start: Optional daily earliest start, 'HH:MM' local to the
            calendar, e.g. '09:00'.
        working_hours_end: Optional daily latest end, 'HH:MM', e.g. '17:00'.
        send_notifications: Whether Google emails the attendees. Default true.
        respect_preferences: Obey the user's saved working hours, lunch and
            buffer. Set false to search the whole window instead.
        account: Account name from 'calendar-mcp accounts'; omit for the default.
    

## Claude Desktop Quick Installation
Install path detected from listing signals. Uses `uvx` (confidence: high):

```json
"mcpServers": {
  "calendar-mcp": {
    "command": "uvx",
    "args": ["calendar-mcp-server"],
    "env": {
      "GOOGLE_CLIENT_ID": "",
      "GOOGLE_CLIENT_SECRET": ""
    }
  }
}
```

**Requires environment variables:** `GOOGLE_CLIENT_ID`, `GOOGLE_CLIENT_SECRET` — the values above are empty placeholders; fill in real credentials before running (see the repository for what each one is for).

## Documentation

## What calendar-mcp MCP server does

The calendar-mcp MCP server connects an MCP client to Google Calendar and exposes structured operations for calendars, events, availability, and scheduling. It can list visible calendars, search events including expanded recurring instances, inspect attendee responses, query busy intervals, and measure scheduled time.

Write operations cover creating secondary calendars and events, parsing natural-language event descriptions, editing or moving events, inviting attendees, setting an RSVP, scheduling a shared free slot, and deleting events. Deletion is the only tool described as destructive, and the client is asked to confirm through elicitation when supported.

The calendar-mcp MCP server also includes scheduling logic based on saved working hours, lunch, meeting buffers, minimum focus-block length, and a selected focus calendar. It can find focus blocks, detect conflicts across signed-in accounts, audit time usage, suggest alternate meeting times, and reserve focus time. Focus-time booking supports a dry-run preview, while rescheduling applies changes only when requested.

## How it works

Google OAuth 2.0 uses a Desktop app client ID and secret. A one-time authentication command opens a browser and stores a token locally; later server runs refresh that token without opening a browser unless browser authentication is explicitly enabled. Multiple Google accounts can be signed in, with one default account and optional account selection for calendar operations.

The default transport is stdio. HTTP mode is available with a configurable host, port, and MCP path. Availability checks and writes are separate Google API calls, so another client can book a slot between those operations. Unknown or inaccessible availability is treated as unavailable for focus booking, mutual scheduling, and rescheduling actions.

## Setup and configuration

Enable the Google Calendar API in Google Cloud and create OAuth credentials for a Desktop app. Provide `GOOGLE_CLIENT_ID` and `GOOGLE_CLIENT_SECRET` through the environment or a `.env` file, then authenticate with `uvx calendar-mcp-server auth`. The package provides both `calendar-mcp` and `calendar-mcp-server` command names.

Tokens normally use the operating system configuration directory. `TOKEN_FILE_PATH` can point to an existing token file and should be absolute when used in a client configuration. Add the server to an MCP client using its standard `mcpServers` configuration. The HTTP endpoint has no authentication layer, so it should remain on loopback, a trusted authenticated proxy, or a private network rather than a public interface.

## Tools and capabilities

The tool set includes:

- Calendar and event listing, searching, creation, editing, movement, and deletion.
- Plain-English event creation through Google’s quick-add parsing.
- Attendee invitations, RSVP changes, and attendee response reporting.
- Free/busy queries, busyness summaries, recurrence projection, and mutual scheduling.
- Focus-time discovery and booking, conflict detection, rescheduling suggestions, and time audits.
- Local account and scheduling-preference management without a Google API call.

## Limitations and notes

The calendar-mcp MCP server requires Google OAuth credentials and access to the relevant calendars. Its local preference settings are stored locally and are not Google Calendar settings. HTTP transport does not currently authenticate callers; anyone who can reach the endpoint can use the saved token’s calendar access. Previewing a focus-time booking does not reserve the selected slots, and concurrent calendar changes can still create competing bookings.

_Full upstream README: https://allmcps.com/mcp/calendar-mcp-2/readme_

