Skip to main content
Glama
README.md
# google-calendar-mcp

MCP server for Google Calendar: list/create/update/delete events, quick-add
from natural language, respond to invitations, and check free/busy
availability across one or more accounts.

## Why not the official Google Calendar MCP connector?

Google has started offering official remote MCP servers per Workspace app
(Gmail, Sheets, Docs, Drive, Calendar). The Calendar one is convenient but,
as of writing, ties **one Google account to one connector authorization** —
if you need several independent Google identities available as separate MCP
servers at the same time, you need one token per account, which this
self-hosted server supports natively.

## Tools (20)

| Tool | What it does |
|---|---|
| `list_calendars` | List all calendars the account can access (id, name, whether it's primary) |
| `list_events` | List events in a date range, optional free-text filter |
| `get_event` | Full detail of one event |
| `create_event` | Create an event (supports all-day and attendees) |
| `quick_add_event` | Create an event from natural language (e.g. "Meeting tomorrow at 10am") |
| `update_event` | Update only the fields you pass on an existing event |
| `respond_to_event` | Accept/decline/mark tentative on an invitation |
| `delete_event` | Delete an event — destructive, requires `confirmed=True` |
| `check_availability` | Free/busy query across one or more calendars in a time range |
| `create_calendar` | Create a brand-new calendar (different from subscribing to an existing one) |
| `update_calendar` | Change a calendar's name, description or default timezone |
| `delete_calendar` | Delete an ENTIRE calendar and all its events — requires `confirmed=True` |
| `share_calendar` | Grant access (reader/writer/owner/freeBusyReader) to an email — requires `confirmed=True` |
| `list_calendar_permissions` | Who has access to a calendar and at what role |
| `remove_calendar_access` | Revoke someone's access — requires `confirmed=True` |
| `subscribe_to_calendar` | Add an existing calendar (e.g. one shared with you) to your list |
| `unsubscribe_from_calendar` | Remove a calendar from your list without deleting it — only works on calendars you don't own (see Notes) |
| `move_event` | Move an event from one calendar to another |
| `get_recurring_instances` | List the concrete occurrences of a recurring event |
| `get_calendar_colors` | Valid color palette for `colorId` on calendars/events |

## Response size

`get_event` drops `kind` (always the same constant) and `etag` (an internal caching hash) from the raw event object — everything else is genuine content, so it stays.

## Security

- Every tool ships with [MCP Tool Annotations](https://modelcontextprotocol.io/specification)
  (`readOnlyHint`, `destructiveHint`, `idempotentHint`, `openWorldHint`), so MCP clients can use
  them to decide whether to prompt for confirmation before running a tool.
- Execution errors propagate as real MCP protocol errors (`isError: true`), not as a JSON payload
  that looks like a success with an `"error"` key buried inside — so the calling model actually
  sees the failure and can self-correct instead of silently treating it as a success.

## Setup

1. Create a Google Cloud project (or reuse one) and enable the
   **Google Calendar API**.
2. Create an OAuth 2.0 Client ID of type "Desktop app" and download it as
   `client_secret.json`.
3. If the app is in "Testing" mode, add your Google account(s) as test users.
4. Install dependencies:
   ```bash
   python3 -m venv .venv
   source .venv/bin/activate
   pip install -r requirements.txt
   ```
5. Run the OAuth flow once per account you want to expose:
   ```bash
   CLIENT_SECRET_PATH=~/.config/google-calendar-mcp/client_secret.json \
     python3 setup_auth.py personal
   ```
   This opens a browser — log in and grant access. The token is saved to
   `~/.config/google-calendar-mcp/token_personal.json`. Repeat with a
   different account label for each additional identity.

## MCP client configuration

```json
{
  "mcpServers": {
    "google-calendar": {
      "command": "/path/to/.venv/bin/python3",
      "args": ["/path/to/google-calendar-mcp/server.py"],
      "env": {
        "GOOGLE_CALENDAR_TOKEN_PATH": "~/.config/google-calendar-mcp/token_personal.json"
      }
    }
  }
}
```

To expose a second account, add another entry pointing at the same
`server.py` with a different `GOOGLE_CALENDAR_TOKEN_PATH`.

| Env var | Purpose |
|---|---|
| `GOOGLE_CALENDAR_TOKEN_PATH` | Path to the OAuth token for this account |

## Notes

- `create_event`/`update_event` detect an all-day event automatically when
  `start`/`end` are a bare `YYYY-MM-DD` date instead of a full ISO 8601
  timestamp.
- `delete_event` follows a confirm-first pattern: without `confirmed=True` it
  returns the event summary and a `requires_confirmation: true` flag instead
  of deleting anything.
- `respond_to_event` looks for the attendee entry flagged as `self` in the
  event's attendee list — you don't need to pass the account's own email.
- ⚠️ **`unsubscribe_from_calendar` doesn't work on a calendar you own** —
  verified live: Google returns `403 cannotUnsubscribeFromOwnedCalendar`. It
  only removes a calendar **someone else shared with you** from your list.
  To get rid of a calendar you created yourself, use `delete_calendar` (it
  actually deletes it, rather than just hiding it from your list).

## License

MIT — see [LICENSE](LICENSE).