Enables management of Google Calendar events across multiple calendars, including searching events, checking availability, creating and updating events with natural language or structured input, managing event invitations, and auto-generating Google Meet links.
Google Calendar MCP Server
Streamable HTTP MCP server for Google Calendar — manage events, check availability, and schedule meetings.
Author: overment
You connect this server to your MCP client at your own responsibility. Language models can make mistakes, misinterpret instructions, or perform unintended actions. Review tool outputs, verify changes (e.g., withsearch_events), and prefer small, incremental writes.
The HTTP/OAuth layer is designed for convenience during development, not production-grade security. If deploying remotely, harden it: proper token validation, secure storage, TLS termination, strict CORS/origin checks, rate limiting, audit logging, and compliance with Google's terms.
Notice
This repo works in two ways:
As a Node/Hono server for local workflows
As a Cloudflare Worker for remote interactions
For production Cloudflare deployments, see Remote Model Context Protocol servers (MCP).
Features
✅ Events — Search across ALL calendars, create, update, delete, respond to invitations
✅ Calendars — Discover available calendars
✅ Availability — Check free/busy status before scheduling
✅ Natural Language — Create events with text like "Lunch tomorrow at noon"
✅ Google Meet — Auto-create Meet links for events
✅ OAuth 2.1 — Secure PKCE flow with RS token mapping
✅ Dual Runtime — Node.js/Bun or Cloudflare Workers
Design Principles
LLM-friendly: Tools are simplified and unified, not 1:1 API mirrors
Smart defaults: Search all calendars, no notification spam, recurring expansion
Just works:
search_eventssearches all calendars by default — no setup neededClear feedback: Every response includes which calendar each event belongs to
Limited features: Due to the model's hallucinations, tools for managing calendars are not included. Ensure that you use the client that allows you to confirm dangerous actions, such as event deletion or updating.
Installation
Prerequisites: Bun, Node.js 20+, Google Cloud project. For remote: a Cloudflare account.
Ways to Run (Pick One)
Local + OAuth — Standard setup with Google OAuth
Cloudflare Worker (wrangler dev) — Local Worker testing
Cloudflare Worker (deploy) — Remote production
1. Local + OAuth — Quick Start
Set up Google Cloud Console:
Create Project & Enable API:
Go to Google Cloud Console
Create a new project (or select existing)
Navigate to APIs & Services > Library
Search for "Google Calendar API" and click Enable
Configure OAuth Consent Screen (required before credentials):
Go to APIs & Services > OAuth consent screen
Select External (or Internal for Workspace)
Fill in app name, user support email, developer email
Add scopes:
../auth/calendar.events,../auth/calendar.readonlyAdd your email as a Test user (required while app is in "Testing" mode)
Save
Create Credentials:
Go to APIs & Services > Credentials
Click Create Credentials > OAuth client ID
Application type: Web application
Name: anything (e.g., "Google Calendar MCP")
Authorized redirect URIs:
http://127.0.0.1:3001/oauth/callbackClick Create and copy Client ID and Client Secret
Configure environment:
Edit .env:
Run:
Tip: The Authorization Server runs on PORT+1 (3001 by default).
Claude Desktop / Cursor:
Token Encryption (Recommended)
Generate an encryption key for secure token storage:
Add to .env:
2. Cloudflare Worker (Local Dev)
With OAuth:
Endpoint: http://127.0.0.1:8787/mcp
3. Cloudflare Worker (Deploy)
Create KV namespace for token storage:
Output will show:
Update
wrangler.tomlwith your KV namespace ID:
Set secrets:
Note:
TOKENS_ENC_KEYencrypts OAuth tokens stored in KV (AES-256-GCM). Without it, tokens are stored unencrypted!
Update redirect URI in
wrangler.toml:
Add Workers URL to your Google Cloud OAuth app's redirect URIs
Deploy:
Endpoint: https://<worker-name>.<account>.workers.dev/mcp
Client Configuration
Pre-authenticate (Recommended)
Claude Desktop has short timeouts that can kill the OAuth flow mid-process. Pre-authenticate manually first:
Once you see "Authentication successful!", tokens are cached and Claude Desktop will use them.
Claude Desktop / Cursor (Local Server)
Claude Desktop / Cursor (Cloudflare Worker)
Node Version Issues (nvm users)
If you get ReadableStream is not defined or similar errors, Claude Desktop may be using an old Node version. Fix by specifying the full path:
Find your node path with: which node
MCP Inspector (Quick Test)
Tools
list_calendars
Discover available calendars and their IDs. Usually not needed since search_events searches all calendars by default.
search_events
Search events across all calendars by default. Returns merged results sorted by start time.
Note: Each event includes
calendarIdandcalendarNameso you know which calendar it belongs to. Use thiscalendarIdwhen callingupdate_eventordelete_event.
check_availability
Check free/busy status before scheduling.
create_event
Create events using natural language OR structured input.
update_event
Update or move existing events (PATCH semantics).
delete_event
Remove an event from calendar.
respond_to_event
Accept, decline, or tentatively accept an event invitation.
Note: Only works for events you were invited to. For events you created, you are the organizer, not an attendee.
Examples
1. List today's events (searches all calendars)
No need to call
list_calendarsfirst —search_eventssearches all accessible calendars by default and shows which calendar each event belongs to.
2. Create event with Google Meet
3. Natural language event
4. Check availability before scheduling
HTTP Endpoints
Endpoint | Method | Purpose |
| POST | MCP JSON-RPC 2.0 |
| GET | SSE stream (Node.js only) |
| GET | Health check |
| GET | OAuth AS metadata |
| GET | OAuth RS metadata |
OAuth (PORT+1):
GET /authorize— Start OAuth flowGET /oauth/callback— Google callbackPOST /token— Token exchangePOST /revoke— Revoke tokens
Development
Architecture
Troubleshooting
Issue | Solution |
"Authentication required" | Complete OAuth flow. Run
and re-authenticate. |
"redirect_uri_mismatch" | Google treats
and
as different. Use
consistently in both .env and Google Cloud Console. |
"unknown_txn" error | Stale mcp-remote processes. Run
then retry. |
"ReadableStream is not defined" | Node.js version too old (needs 18+). Use full path to newer node in config. |
"spawn bunx ENOENT" | Claude Desktop can't find
. Use
instead, or specify full path. |
"Another instance handling auth" | Kill zombie processes:
|
OAuth timeout in Claude | Claude kills auth flow too quickly. Pre-authenticate manually (see Client Configuration). |
Token expired | Google tokens expire after 1 hour. Refresh tokens are used automatically if
was set. |
OAuth doesn't start (Worker) |
should return
with
. |
KV namespace error | Run
and update
with the ID. |
Tools empty in Claude | Ensure Worker returns JSON Schema for
; use
. |
Debugging
Enable detailed logs with --debug:
Logs are written to ~/.mcp-auth/{hash}_debug.log.
Test auth flow independently:
Environment Variables
Node.js (.env)
Variable | Required | Description |
| ✓ | Google OAuth Client ID |
| ✓ | Google OAuth Client Secret |
| Prod | 32-byte base64 key for token encryption |
| MCP server port (default: 3000) | |
| Server host (default: 127.0.0.1) | |
| debug, info, warning, error | |
| Callback URL for OAuth | |
| Comma-separated allowed redirect URIs |
Cloudflare Workers (wrangler.toml + secrets)
wrangler.toml vars:
Secrets (set via
PROVIDER_CLIENT_ID— Google OAuth Client IDPROVIDER_CLIENT_SECRET— Google OAuth Client SecretTOKENS_ENC_KEY— 32-byte base64url encryption key
KV Namespace:
License
MIT