google-calendar-mcp
by H5SH
README.md
# google-calendar-mcp
[](LICENSE)
[](https://nodejs.org/)
[](https://modelcontextprotocol.io/)
A production-ready [Model Context Protocol (MCP)](https://modelcontextprotocol.io) server that lets AI assistants interact with your personal Google Calendar using the Google Calendar API and OAuth 2.0.
Connect it to **Cursor**, **Claude Desktop**, **VS Code**, or any MCP client that supports stdio transport. Each user runs a local copy with **their own** Google credentials.
> **Security:** Never commit `.env` or real OAuth tokens. See [SECURITY.md](SECURITY.md).
---
## Features
- **7 MCP tools** — list calendars, list / search / create / update / delete events, and free/busy queries
- **OAuth 2.0 with refresh tokens** — access tokens refresh automatically; you never paste a short-lived access token
- **Zod validation** on every tool input
- **Structured logging** to stderr (tool name, duration, API errors)
- **Graceful errors** — 401, 403, 404, 429, network failures, and expired refresh tokens return readable MCP errors without crashing
- **stdio transport** — works with local MCP hosts out of the box
---
## Architecture overview
```
┌─────────────────────┐ stdio (JSON-RPC) ┌──────────────────────────┐
│ MCP Client │ ◄───────────────────────► │ google-calendar-mcp │
│ Cursor / Claude / │ │ McpServer + tools │
│ VS Code │ │ auth.ts → OAuth2 │
└─────────────────────┘ │ calendar.ts → API calls │
└────────────┬─────────────┘
│
▼
┌──────────────────────────┐
│ Google Calendar API v3 │
│ (googleapis + refresh) │
└──────────────────────────┘
```
1. The MCP client **spawns** this server as a child process.
2. Credentials are loaded from `.env` (or the client `env` block).
3. `googleapis` refreshes access tokens using your refresh token as needed.
4. Tools in `src/tools/` call shared helpers in `src/calendar.ts` (no duplicated API logic).
This is a **single-user, local** server—not a multi-tenant cloud service.
---
## Screenshots
> Place screenshots under `docs/images/` after publishing if desired.
| Cursor MCP connected | Example tool result |
| -------------------- | ------------------- |
|  |  |
*(Image paths are placeholders until you add screenshots.)*
---
## Tech stack
| Technology | Role |
| ---------- | ---- |
| TypeScript | Strict-mode application language |
| Node.js 18+ | Runtime |
| [@modelcontextprotocol/sdk](https://www.npmjs.com/package/@modelcontextprotocol/sdk) | MCP server (`McpServer`, `registerTool`, `StdioServerTransport`) |
| [googleapis](https://www.npmjs.com/package/googleapis) | Official Google Calendar client |
| [zod](https://www.npmjs.com/package/zod) | Tool input schemas |
| [dotenv](https://www.npmjs.com/package/dotenv) | Local environment loading |
| tsx | Development runner |
**Docker is not provided.** This server uses stdio; MCP clients typically start a local Node process. Packaging it in Docker is unnecessary for the supported setup.
---
## Requirements
- Node.js **18** or later
- A [Google Cloud](https://console.cloud.google.com/) project
- Google Calendar API enabled
- OAuth 2.0 client ID and secret
- A refresh token with Calendar access
- An MCP-compatible client (Cursor, Claude Desktop, VS Code, etc.)
---
## Installation
```bash
git clone https://github.com/[your-org]/google-calendar-mcp.git
cd google-calendar-mcp
npm install
```
Copy the environment template:
```bash
cp .env.example .env
```
Fill in your credentials (see [Google Cloud OAuth setup](#google-cloud-oauth-setup) below). **Never commit `.env`.**
---
## Environment variables
| Variable | Required | Description |
| -------- | -------- | ----------- |
| `GOOGLE_CLIENT_ID` | Yes | OAuth 2.0 client ID from Google Cloud Console |
| `GOOGLE_CLIENT_SECRET` | Yes | OAuth 2.0 client secret |
| `GOOGLE_REFRESH_TOKEN` | Yes | Long-lived refresh token from the OAuth consent flow |
| `DEFAULT_CALENDAR_ID` | No | Default calendar when a tool omits `calendarId` (default: `primary`) |
Example `.env` (placeholders only):
```env
GOOGLE_CLIENT_ID=your-client-id.apps.googleusercontent.com
GOOGLE_CLIENT_SECRET=your-client-secret
GOOGLE_REFRESH_TOKEN=your-refresh-token
DEFAULT_CALENDAR_ID=primary
```
---
## Google Cloud OAuth setup
Follow these steps if you have never used Google Cloud. Screens and labels change occasionally; use the current Console navigation that matches each step.
### 1. Create a Google Cloud project
1. Open [Google Cloud Console](https://console.cloud.google.com/).
2. Click the project selector at the top → **New Project**.
3. Enter a name (for example `google-calendar-mcp`) → **Create**.
4. Select the new project.
### 2. Enable the Google Calendar API
1. Go to **APIs & Services** → **Library**.
2. Search for **Google Calendar API**.
3. Open it → click **Enable**.
### 3. Configure the OAuth consent screen
1. Go to **APIs & Services** → **OAuth consent screen**.
2. Choose **External** (unless you use Google Workspace Internal) → **Create**.
3. Fill in **App name**, **User support email**, and **Developer contact**.
4. Save and continue through Scopes / Test users as prompted.
5. Under **Test users** (while the app is in **Testing**), add the Google account whose calendar you will use.
> **Important:** In **Testing** mode, refresh tokens may expire after **7 days**. For personal long-term use, keep yourself as a test user or publish the app when eligible. Revoking access or resetting the OAuth client also invalidates tokens.
### 4. Create an OAuth client ID
1. Go to **APIs & Services** → **Credentials**.
2. Click **Create credentials** → **OAuth client ID**.
3. Application type:
- **Web application** is easiest with the OAuth Playground (recommended below), or
- **Desktop app** if you prefer another flow later.
4. Name the client (for example `calendar-mcp-local`).
5. If using **Web application**, under **Authorized redirect URIs** add:
```
https://developers.google.com/oauthplayground
```
6. Click **Create**. Copy the **Client ID** and **Client secret** into your `.env` file.
### 5. Required OAuth scope
This server needs:
```
https://www.googleapis.com/auth/calendar
```
That scope allows reading and writing calendar data for the authorized account.
### 6. Obtain a refresh token (OAuth 2.0 Playground)
1. Open [Google OAuth 2.0 Playground](https://developers.google.com/oauthplayground/).
2. Click the **gear icon** (top right).
3. Check **Use your own OAuth credentials**.
4. Paste your **Client ID** and **Client secret** → close the dialog.
5. In the left list, expand **Calendar API v3** and select
`https://www.googleapis.com/auth/calendar`
(or paste that scope in the custom scope box).
6. Click **Authorize APIs** and sign in with your test user account. Grant the permissions.
7. Click **Exchange authorization code for tokens**.
8. Copy the **`refresh_token`** value (not the access token) into `.env` as `GOOGLE_REFRESH_TOKEN`.
If you do not see a refresh token, revoke the app at [Google Account permissions](https://myaccount.google.com/permissions) and repeat with your own credentials and “offline” access (Playground does this when configured as above).
### 7. First-time authentication check
From the project root:
```bash
npm run build
npm run dev
```
On success, stderr should include structured logs such as:
- `OAuth2 client initialized`
- `Authentication validated successfully`
- `google-calendar-mcp server connected and ready`
Then stop with `Ctrl+C`. For day-to-day use, configure an MCP client so it starts the server for you (next sections). You do **not** need to keep a terminal open after the client is configured.
---
## Running locally
### Build and start
```bash
npm run build
npm start
```
### Development (no build step)
```bash
npm run dev
```
### Type-check only
```bash
npm run typecheck
```
The process listens on **stdio**. Running it alone in a terminal will look idle after startup—that is expected. Use an MCP client or the [MCP Inspector](https://github.com/modelcontextprotocol/inspector):
```bash
npx @modelcontextprotocol/inspector node dist/index.js
```
---
## Configuration
Use **absolute paths**. Prefer built `dist/index.js` after `npm run build`. Secrets can live in `.env` (loaded by the server) and/or in the client `env` block.
### Cursor
Project file: `.cursor/mcp.json` (or Cursor global MCP settings). See also [`examples/mcp-cursor.json`](examples/mcp-cursor.json).
```json
{
"mcpServers": {
"google-calendar": {
"command": "node",
"args": ["/absolute/path/to/google-calendar-mcp/dist/index.js"],
"env": {
"GOOGLE_CLIENT_ID": "your-client-id.apps.googleusercontent.com",
"GOOGLE_CLIENT_SECRET": "your-client-secret",
"GOOGLE_REFRESH_TOKEN": "your-refresh-token",
"DEFAULT_CALENDAR_ID": "primary"
}
}
}
}
```
Restart Cursor and confirm the server is connected under **Settings → MCP**.
### Claude Desktop
Config file:
- macOS: `~/Library/Application Support/Claude/claude_desktop_config.json`
- Windows: `%APPDATA%\Claude\claude_desktop_config.json`
```json
{
"mcpServers": {
"google-calendar": {
"command": "node",
"args": ["/absolute/path/to/google-calendar-mcp/dist/index.js"],
"env": {
"GOOGLE_CLIENT_ID": "your-client-id.apps.googleusercontent.com",
"GOOGLE_CLIENT_SECRET": "your-client-secret",
"GOOGLE_REFRESH_TOKEN": "your-refresh-token",
"DEFAULT_CALENDAR_ID": "primary"
}
}
}
}
```
### VS Code
Add under your MCP / Copilot MCP server settings (exact key may vary by extension):
```json
{
"mcp": {
"servers": {
"google-calendar": {
"command": "node",
"args": ["/absolute/path/to/google-calendar-mcp/dist/index.js"],
"env": {
"GOOGLE_CLIENT_ID": "your-client-id.apps.googleusercontent.com",
"GOOGLE_CLIENT_SECRET": "your-client-secret",
"GOOGLE_REFRESH_TOKEN": "your-refresh-token"
}
}
}
}
}
```
---
## Available MCP tools
| Tool | Description |
| ---- | ----------- |
| `list_calendars` | Returns every calendar available to the authenticated user |
| `list_events` | Lists events in a time range (optional filters) |
| `search_events` | Free-text search via Google’s `q` parameter |
| `create_event` | Creates an event; returns created event details including id |
| `update_event` | Partially updates an event (only supplied fields) |
| `delete_event` | Deletes an event |
| `free_busy` | Returns busy periods for one or more calendars |
---
## Example prompts
Ask your AI client natural language such as:
- “List my calendars.”
- “What meetings do I have tomorrow?”
- “Search my calendar for dentist.”
- “Create a 1-hour event called Team Sync tomorrow at 10am.”
- “Am I free Friday afternoon?”
See [`examples/prompts.md`](examples/prompts.md) for more.
### Sample tool request
```json
{
"name": "list_events",
"arguments": {
"timeMin": "2026-07-14T00:00:00Z",
"timeMax": "2026-07-21T00:00:00Z",
"maxResults": 5,
"orderBy": "startTime"
}
}
```
### Sample tool response (illustrative only)
```json
[
{
"id": "abc123example",
"summary": "Team Sync",
"start": { "dateTime": "2026-07-15T10:00:00-07:00" },
"end": { "dateTime": "2026-07-15T11:00:00-07:00" },
"status": "confirmed",
"htmlLink": "https://www.google.com/calendar/event?eid=example"
}
]
```
More samples: [`examples/sample-responses.md`](examples/sample-responses.md).
---
## Project structure
```
google-calendar-mcp/
├── src/
│ ├── index.ts # Entry — stdio transport
│ ├── server.ts # McpServer factory and tool registration
│ ├── auth.ts # OAuth2 client and token refresh
│ ├── calendar.ts # Google Calendar API wrapper
│ ├── types.ts # Shared TypeScript types
│ ├── utils.ts # Logging, error handling, helpers
│ └── tools/
│ ├── listCalendars.ts
│ ├── listEvents.ts
│ ├── searchEvents.ts
│ ├── createEvent.ts
│ ├── updateEvent.ts
│ ├── deleteEvent.ts
│ └── freeBusy.ts
├── examples/ # Sample configs and prompts (no secrets)
├── .env.example
├── package.json
├── tsconfig.json
├── LICENSE
├── CONTRIBUTING.md
├── SECURITY.md
├── CODE_OF_CONDUCT.md
├── CHANGELOG.md
└── README.md
```
---
## Development workflow
```bash
npm install
cp .env.example .env # add your credentials
npm run typecheck
npm run build
npm run dev # or connect via MCP Inspector / Cursor
```
Contributions welcome—see [CONTRIBUTING.md](CONTRIBUTING.md).
---
## Troubleshooting
| Problem | What to try |
| ------- | ----------- |
| Missing environment variables | Copy `.env.example` → `.env` and set all three Google variables |
| Authentication failed (401) / invalid refresh token | Re-run OAuth Playground; ensure Client ID matches the token; revoke and re-authorize if needed |
| Permission denied (403) | Enable Google Calendar API; confirm scope includes calendar; ensure you are a consent screen test user |
| Rate limit (429) | Wait and retry; lower `maxResults` |
| `redirect_uri_mismatch` in Playground | Add `https://developers.google.com/oauthplayground` to authorized redirect URIs |
| MCP client cannot connect | Use absolute paths; run `npm run build` first; ensure Node 18+ is on `PATH` |
| Server seems frozen after start | Normal for stdio—clients must spawn and speak the protocol |
| Refresh token stops working after ~7 days | App is in Testing mode—see OAuth consent screen notes above |
Logs are written as JSON lines to **stderr**. Do not log secrets.
---
## FAQ
**Do I need to keep a terminal open for Cursor?**
No. Once MCP is configured, Cursor starts and stops the server automatically.
**Is this multi-user?**
No. One process uses one set of credentials and one Google account.
**Can I publish my `.env`?**
Never. Treat client secret and refresh token like passwords.
**Can I use a service account?**
This project is built for user OAuth with a refresh token, not service accounts.
**Why no Docker?**
MCP stdio clients spawn a local command. Running Node directly is the supported path.
---
## Contributing
See [CONTRIBUTING.md](CONTRIBUTING.md) and [CODE_OF_CONDUCT.md](CODE_OF_CONDUCT.md).
---
## License
This project is licensed under the [MIT License](LICENSE).
---
## Acknowledgements
- [Model Context Protocol](https://modelcontextprotocol.io/) and the [TypeScript SDK](https://github.com/modelcontextprotocol/typescript-sdk)
- [Google Calendar API](https://developers.google.com/calendar) and the [googleapis](https://github.com/googleapis/google-api-nodejs-client) Node.js client
- Contributor Covenant for the Code of Conduct template
This server cannot be deployed
Maintenance
ActivityMaintained
ResponsivenessNo issues