Skip to main content
Glama
feamster

Google Calendar MCP Server

by feamster
README.md
# Google Calendar MCP Server

MCP server for accessing and analyzing Google Calendar events through the Model Context Protocol.

## Features

- šŸ“… **Access ALL your calendars** - Primary, work, shared calendars, everything!
- šŸ‘„ **Multi-account support** - Use multiple Google accounts (e.g., personal + work) with automatic account selection
- āž• **Create calendar events** - Schedule meetings with invites coming from the correct account
- āœ… **Accept/decline invitations** - Respond to calendar invites
- šŸ—‘ļø **Delete events** - Remove calendar entries
- šŸ” Find when you last met with someone
- šŸ“Š Analyze meeting patterns and time blocks
- šŸŽÆ Distinguish between deep work and flexible time blocks
- ⚔ Answer questions like "What do I need to be prepared for?"
- šŸ”— Integrates with spark-mcp for unified meeting context
- šŸ“‹ List and search events across all your calendars at once

## Requirements

- Python 3.10+
- Google Cloud project with Calendar API enabled
- OAuth2 credentials (see setup below)

## Installation

```bash
# Install in development mode
pip install -e .
```

## Setup

### 1. Create Google Cloud Project

1. Go to [Google Cloud Console](https://console.cloud.google.com)
2. Create a new project:
   - Click "Select a project" at the top
   - Click "New Project"
   - Name: `Calendar MCP` (or your choice)
   - Click "Create"
3. Enable the [Google Calendar API](https://console.cloud.google.com/apis/library/calendar-json.googleapis.com):
   - In left sidebar: "APIs & Services" → "Library"
   - Search for "Google Calendar API"
   - Click on it, then click "Enable"

### 2. Configure OAuth Consent Screen

**Important: Do this BEFORE creating credentials**

1. In left sidebar: "APIs & Services" → "OAuth consent screen"
2. If you see "Overview" page with metrics, look for navigation in left sidebar
3. Click on **"Audience"**:
   - Click "ADD USERS"
   - Add your Google email address
   - Click "Add" then "Save"
4. Click on **"Data Access"** in left sidebar:
   - Click "Add or Remove Scopes"
   - Search for "Google Calendar API"
   - Check: `.../auth/calendar` (full access for read/write operations)
   - Click "Update"
   - Click "Save"

### 3. Create OAuth Client Credentials

1. In left sidebar: Click **"Clients"**
2. Click "CREATE CLIENT" or similar button
3. Choose:
   - Application type: **Desktop app**
   - Name: `calendar-mcp-client`
4. Click "Create"
5. You'll see the client created - click on it
6. In "Client secrets" section:
   - Click "+ Add secret"
   - Copy the new secret OR click download
7. Download the JSON file (should auto-download or look for download option)
8. Save as `client_secret.json` in `/Users/feamster/src/calendar-mcp/`

**Note:** The Google Cloud Console interface uses "OAuth Platform" with Audience/Data Access/Clients, not the old consent screen wizard.

### 4. Authenticate

Run the authentication setup:

```bash
python -m calendar_mcp.auth
```

This will:
- Open your browser for Google OAuth consent
- Ask you to grant Calendar read and write permissions
- Save the refresh token to `~/.mcp-auth/calendar/tokens/`

**Important:** Google will show a warning "Google hasn't verified this app" because this is your personal project. This is normal and safe:
- Click "Advanced"
- Click "Go to Calendar MCP (unsafe)"
- Grant the calendar read and write permissions

### 4b. Multi-Account Setup (Optional)

To use multiple Google accounts (e.g., personal + work), add each account:

```bash
# Add your first account
python -m calendar_mcp.auth add feamster@gmail.com

# Add your work account and set as default
python -m calendar_mcp.auth add feamster@uchicago.edu --default

# List configured accounts
python -m calendar_mcp.auth list

# Change default account
python -m calendar_mcp.auth default feamster@uchicago.edu
```

**How multi-account works:**
- When creating events, the account is **automatically selected** based on the `calendarId`
- If `calendarId="feamster@uchicago.edu"`, invites come FROM that account
- If `calendarId="feamster@gmail.com"`, invites come FROM that account
- You can also explicitly specify `account` parameter to override

**Example usage after setup:**
```
# Creates event on UChicago calendar, invite FROM feamster@uchicago.edu
create_event(summary="Meeting", calendarId="feamster@uchicago.edu", attendees=["someone@example.com"])

# Creates event on Gmail calendar, invite FROM feamster@gmail.com
create_event(summary="Personal", calendarId="feamster@gmail.com", attendees=["friend@example.com"])
```

**Calendar display names:** `calendarId` accepts a calendar's display name in
addition to its raw id. `create_event(calendarId="Chemster Events")` resolves
to the underlying `...@group.calendar.google.com` id by matching against the
`summary`/`summaryOverride` fields returned by `list_all_calendars` (case-
insensitive, trimmed). If a name is shared across accounts pass `account=...`
to disambiguate; an unknown name returns a structured error listing the
available calendars. `primary` and raw ids are passed through unchanged.

### 5. Configure Meeting Preferences (Optional)

Create `~/.mcp-config/calendar/config.json` to customize your scheduling preferences:

```json
{
  "preferences": {
    "timezone": "America/New_York",
    "meetingPreferences": {
      "preferAdjacentToMeetings": true,
      "preferredMeetingDuration": 30,
      "avoidDeepWorkBlocks": true,
      "deepWorkBlockUsage": "end",
      "neverAvailablePatterns": ["kids"],
      "preferredDays": {
        "Wednesday-PM": 100,
        "Thursday": 100,
        "Monday-PM": 70,
        "Tuesday": 40,
        "Friday": 40
      },
      "afternoonStartHour": 12,
      "notes": "SCHEDULING PRIORITY: 1. BEST (100): Wed PM & Thu. 2. GOOD (70): Mon PM. 3. LAST RESORT (40): Tue & Fri. Prefer 30-min slots adjacent to meetings. Avoid deep work blocks. Never schedule over 'kids' blocks."
    },
    "flexibleBlockPatterns": ["flexible", "optional", "buffer", "hold"],
    "deepWorkPatterns": ["deep work", "focus time", "writing", "research", "reading"]
  }
}
```

**Meeting Preference Options:**
- `preferAdjacentToMeetings`: Suggest times next to existing meetings
- `preferredMeetingDuration`: Default meeting length in minutes
- `avoidDeepWorkBlocks`: Try not to suggest deep work time
- `deepWorkBlockUsage`: `"end"` = use end of block if needed, `"start"` = use start, `"avoid"` = never use
- `neverAvailablePatterns`: Keywords for completely unavailable blocks
- **`preferredDays`**: **Day-of-week ranking system (0-100 scale)**
  - **Higher scores = stronger preference** (100 = most preferred, 40 = least preferred, 0 = avoid)
  - Use full day names: `"Monday"`, `"Tuesday"`, `"Wednesday"`, `"Thursday"`, `"Friday"`
  - For **afternoon-only** preferences: `"Monday-PM"`, `"Wednesday-PM"`, etc.
  - **Example ranking:**
    - `"Wednesday-PM": 100` and `"Thursday": 100` = BEST days (strongly prefer)
    - `"Monday-PM": 70` = GOOD day (acceptable)
    - `"Tuesday": 40` and `"Friday": 40` = LAST RESORT (avoid if better options exist)
  - The MCP will **prioritize suggesting times on higher-scored days**
- `afternoonStartHour`: Hour when afternoon starts (default: 12 = noon)
- `notes`: Human-readable description explaining your preferences for Claude to understand

### 6. Configure Claude Desktop

Add to your Claude Desktop config (`~/Library/Application Support/Claude/claude_desktop_config.json`):

```json
{
  "mcpServers": {
    "calendar": {
      "command": "python",
      "args": ["-m", "calendar_mcp.server"],
      "cwd": "/Users/feamster/src/calendar-mcp"
    }
  }
}
```

If you have other MCP servers (like spark-mcp), add the `"calendar"` section to your existing `"mcpServers"` object.

### 7. Test the Installation (Optional but Recommended)

Before configuring Claude Desktop, verify everything works:

```bash
python test_calendar.py
```

You should see:
- āœ“ Credentials loaded successfully
- āœ“ Calendar client initialized
- āœ“ Found X events
- āœ“ All tests completed

### 8. Restart Claude Desktop

Restart Claude Desktop to load the new MCP server.

## Usage

Once configured, ask Claude questions like:

**Meeting Queries:**
- "What meetings do I have today?"
- "Show me my calendar for the next week"
- "What do I need to be prepared for?"
- "Tell me about my 2pm meeting"

**Relationship Tracking:**
- "When was the last time I met with john@example.com?"
- "Show me all my meetings with the engineering team"
- "Who do I meet with most often?"

**Time Analysis:**
- "How many hours of meetings did I have this week?"
- "Analyze my calendar blocks for today"
- "Summarize my meetings from the past week"
- "Am I free tomorrow at 2pm?"

**Finding Meeting Times (with preferences):**
- "Find me times for a 30-minute meeting this week"
- "When should I schedule a call with John?"
- "What are my best meeting times next week?"
- "Suggest times for a 1-hour meeting"

When finding meeting times, the MCP will:
- **Prioritize your preferred days** (e.g., Wed PM & Thu over Tue & Fri)
- **Look for slots adjacent to existing meetings** (if enabled)
- **Avoid deep work blocks** (but use them as last resort if needed)
- **Never suggest times with "kids" or other never-available patterns**

**Managing Calendar Events:**
- "Create a meeting titled 'Project Review' tomorrow at 2pm for 1 hour"
- "Schedule a 30-minute call with john@example.com on Thursday at 3pm"
- "Add a meeting on Friday at 10am with sarah@example.com and bob@example.com"
- "Delete the meeting about quarterly planning"
- "Accept the invitation for tomorrow's standup"
- "Decline the Friday afternoon meeting"
- "Mark the project kickoff as tentative"

**Integration with spark-mcp:**
If you have both calendar-mcp and spark-mcp installed:
- "Show me the transcript from my meeting with X last Tuesday"
- "Summarize all my meetings and their transcripts from last week"
- "What were the action items from recent meetings?"

## Available Tools

### 1. `list_accounts`
List all configured Google accounts for multi-account calendar access. Shows which accounts are set up and which is the default.

### 2. `list_all_calendars`
List all calendars you have access to (primary, work, shared, etc.)

### 3. `list_calendar_events`
List calendar events **from ALL your calendars** with filtering by time range, search query, etc. Each event shows which calendar it's from.

### 4. `get_upcoming_meetings`
Get upcoming meetings for preparation, includes time until meeting and attendee info.

### 5. `find_meetings_with_person`
Find when you last met with someone (by email or name).

### 6. `get_meeting_by_id`
Get full details of a specific calendar event.

### 7. `analyze_time_blocks`
Analyze calendar blocks and distinguish between deep work and flexible time.

### 8. `summarize_meetings`
Get summaries of meetings grouped by day, week, or person.

### 9. `check_availability`
Check if you're available at a proposed time.

### 10. `find_meeting_times`
Find best available meeting times based on your preferences (day ranking, adjacent to meetings, avoid deep work).

### 11. `create_event`
Create a new calendar event with optional attendees and automatic invitation emails. **Multi-account:** Automatically selects the correct Google account based on `calendarId`. **Recurring events:** Pass `recurrence` (structured) or `recurrenceRrule` (raw RFC 5545) to create a series:

```python
# MWF for 3 weeks
recurrence = {"freq": "WEEKLY", "by_day": ["MO", "WE", "FR"], "until": "2026-07-24"}

# First Monday of every month, for a year
recurrence = {"freq": "MONTHLY", "by_day": ["MO"], "by_set_pos": [1], "count": 12}

# MWF for 3 weeks, skipping July 13
recurrence = {"freq": "WEEKLY", "by_day": ["MO", "WE", "FR"], "until": "2026-07-24", "exceptions": ["2026-07-13"]}
```

Supported keys: `freq` (DAILY/WEEKLY/MONTHLY/YEARLY), `interval`, `by_day`, `by_month_day`, `by_month`, `by_set_pos`, `count` (mutually exclusive with `until`), `until` (ISO date or datetime — bare dates interpreted as end-of-day in `start`'s timezone), `exceptions` (list of ISO dates to skip via EXDATE). The response includes an `occurrence_count` so you can verify the series size without listing instances.

### 12. `update_event_recurrence`
Modify the recurrence rule of an existing event without deleting and recreating:
- `scope="series"` (default): replace the RRULE on the master event (pass `recurrence=null` to clear).
- `scope="this_and_following"`: split the series at `splitAt` — shortens the original's `UNTIL` to just before `splitAt` and inserts a new event with the new rule starting at `splitAt`.
- `scope="single"`: apply the change to a single instance (pass the instance event id, not the master id).

### 13. `delete_event`
Delete a calendar event with optional cancellation notifications to attendees.

### 14. `respond_to_event`
Accept, decline, or tentatively accept a calendar invitation.

## Block Type Detection

The server can distinguish between different types of calendar blocks:

- **Deep Work**: Focus time, writing, research (hard blocks)
- **Flexible**: Buffer time, optional blocks (can accommodate last-minute requests)
- **Meetings**: Scheduled meetings
- **Out of Office**: Hard blocks

Configure patterns in `~/.mcp-config/calendar/config.json`:

```json
{
  "preferences": {
    "flexibleBlockPatterns": ["flexible", "optional", "buffer"],
    "deepWorkPatterns": ["deep work", "focus time", "writing", "research"]
  }
}
```

## Integration with spark-mcp

If you have spark-mcp installed, the calendar MCP can cross-reference calendar events with meeting transcripts to provide unified context.

## Troubleshooting

### Authentication errors

1. Check credentials file exists:
   ```bash
   ls ~/.mcp-auth/calendar/credentials.json
   ```

2. Re-run authentication:
   ```bash
   python -m calendar_mcp.auth
   ```

3. Verify API is enabled in [Google Cloud Console](https://console.cloud.google.com)

### No events returned

1. Check date range in your query
2. Verify you have events in your Google Calendar
3. Check token has proper scopes

### Server not connecting

1. Check Claude Desktop logs:
   ```bash
   tail -f ~/Library/Logs/Claude/mcp-server-calendar.log
   ```

2. Verify config syntax in `claude_desktop_config.json`

3. Make sure Python path is correct in config

4. Try running server directly to check for errors:
   ```bash
   cd /Users/feamster/src/calendar-mcp
   python -m calendar_mcp.server
   # Should start without errors (Ctrl+C to exit)
   ```

### "Google hasn't verified this app" warning

This is **normal** for personal projects. Your app is safe because you created it.

**Solution:**
- Click "Advanced" on the warning page
- Click "Go to Calendar MCP (unsafe)"
- Grant the permissions

### API quota exceeded

Google Calendar API has rate limits (1M queries/day). If you hit limits:
- Wait a few minutes
- Most queries use only 1-2 quota units
- Normal usage won't hit limits

## Testing & Development

### Test Installation

```bash
# Test authentication status
python -m calendar_mcp.auth --test

# Run full test suite
python test_calendar.py
```

### Development Mode

```bash
# Install in development mode
pip install -e .

# Make changes to calendar_mcp/*.py files
# Restart Claude Desktop to reload changes
```

### Project Structure

```
calendar-mcp/
ā”œā”€ā”€ calendar_mcp/
│   ā”œā”€ā”€ __init__.py
│   ā”œā”€ā”€ auth.py              # OAuth2 authentication (multi-account support)
│   ā”œā”€ā”€ calendar_client.py   # Google Calendar API wrapper
│   └── server.py            # MCP server implementation
ā”œā”€ā”€ test_calendar.py         # Test script
ā”œā”€ā”€ setup.py                 # Package installer
ā”œā”€ā”€ README.md               # This file
ā”œā”€ā”€ SETUP.md                # Detailed setup instructions
ā”œā”€ā”€ QUICKSTART.md           # Quick start guide
└── SPEC.md                 # Technical specification

~/.mcp-config/calendar/
ā”œā”€ā”€ accounts.json            # Multi-account configuration
└── config.json              # User preferences (ignored calendars, meeting prefs)

~/.mcp-auth/calendar/
ā”œā”€ā”€ credentials.json         # OAuth client credentials
└── tokens/                  # Per-account OAuth tokens
    ā”œā”€ā”€ feamster_at_gmail_com.json
    └── feamster_at_uchicago_edu.json
```

## Privacy & Security

- **Full calendar access**: Requests `calendar` scope for read and write operations
- **Local credentials**: Tokens stored locally in `~/.mcp-auth/calendar/`
- **No data caching**: Doesn't cache calendar data
- **Secure token handling**: Automatic refresh token management
- **Notification control**: You can disable email notifications when creating/deleting events

## Documentation

- **[README.md](README.md)** (this file) - Overview and usage
- **[QUICKSTART.md](QUICKSTART.md)** - Get started in 10 minutes
- **[SETUP.md](SETUP.md)** - Detailed setup instructions with troubleshooting
- **[SPEC.md](SPEC.md)** - Complete technical specification

## Future Enhancements

See [SPEC.md](SPEC.md) for detailed roadmap, including:
- Action item extraction from meeting descriptions
- Update existing events (modify time, location, description)
- Smart scheduling suggestions
- Advanced meeting analytics
- Recurring meeting instance management (list/override individual instances of a series)
- Cross-referencing with spark-mcp transcripts

## License

MIT