nocodia-calendar-mcp
v1.2.1
Published
Google Calendar MCP Server
Readme
Google Calendar MCP Server
A production-ready multi-tenant Google Calendar MCP (Model Context Protocol) server that provides programmatic access to Google Calendar operations for AI agents.
Features
- Multi-tenant Support: Each request requires user-specific OAuth2 credentials
- Complete Calendar Operations: List, create, update, and delete calendar events
- Idempotency: Redis-backed idempotency keys prevent duplicate operations
- Docker Ready: Fully containerized with Docker Compose setup
- MCP Protocol Compliant: Clean JSON-RPC communication via stdio
- OAuth2 Token Refresh: Automatic token refresh handling
Table of Contents
- Prerequisites
- Google Cloud Console Setup
- Environment Configuration
- Docker Deployment
- MCP Tools Reference
- JSON-RPC Examples
- n8n Integration Guide
- Development Setup
- Troubleshooting
Prerequisites
- Docker and Docker Compose
- Google Cloud Console account
- Node.js 18+ (for development)
Google Cloud Console Setup
1. Create a Google Cloud Project
- Go to Google Cloud Console
- Create a new project or select an existing one
- Note your project ID
2. Enable Google Calendar API
- Navigate to APIs & Services > Library
- Search for "Google Calendar API"
- Click Enable
3. Create OAuth2 Credentials
- Go to APIs & Services > Credentials
- Click Create Credentials > OAuth 2.0 Client IDs
- Configure the OAuth consent screen if prompted:
- User Type: External (for testing) or Internal (for organization use)
- App name: Your application name
- User support email: Your email
- Authorized domains: Add your domain if applicable
- Create OAuth 2.0 Client ID:
- Application type: Web application
- Name: "Google Calendar MCP Server"
- Authorized redirect URIs: Add your redirect URI (e.g.,
https://app.botmake.siteorhttp://localhost:3000/callback)
- Download the credentials JSON or copy the Client ID and Client Secret
4. OAuth2 Flow for User Tokens
To get user access tokens, implement an OAuth2 flow or use Google's OAuth2 Playground:
- Go to OAuth 2.0 Playground
- Click the gear icon (⚙️) and check "Use your own OAuth credentials"
- Enter your Client ID and Client Secret
- In Step 1, select "Google Calendar API v3" and choose scopes:
https://www.googleapis.com/auth/calendarhttps://www.googleapis.com/auth/calendar.events
- Click "Authorize APIs" and complete the OAuth flow
- In Step 2, click "Exchange authorization code for tokens"
- Copy the
access_tokenandrefresh_token
Environment Configuration
1. Copy Environment Template
cp .env.example .env2. Configure Environment Variables
Edit .env file with your credentials:
# Google OAuth2 Credentials (Required)
GOOGLE_CLIENT_ID=your_google_client_id_here
GOOGLE_CLIENT_SECRET=your_google_client_secret_here
# Redis Configuration
REDIS_URL=redis://localhost:6379/0
# Server Configuration
PORT=8080
LOG_LEVEL=info
DEFAULT_TIMEZONE=UTC
# Calendar Settings
ALLOWED_CALENDARS=*
RATE_LIMIT_RPS=5
# Idempotency Settings
IDEMPOTENCY_TTL_SECONDS=86400Environment Variables Reference
| Variable | Description | Default | Required |
|----------|-------------|---------|----------|
| GOOGLE_CLIENT_ID | OAuth2 Client ID from Google Cloud Console | - | ✅ |
| GOOGLE_CLIENT_SECRET | OAuth2 Client Secret from Google Cloud Console | - | ✅ |
| REDIS_URL | Redis connection URL for idempotency storage | redis://localhost:6379/0 | ❌ |
| PORT | Server port (not used in MCP mode) | 8080 | ❌ |
| LOG_LEVEL | Logging level (disabled in MCP mode) | info | ❌ |
| DEFAULT_TIMEZONE | Default timezone for events | UTC | ❌ |
| ALLOWED_CALENDARS | Comma-separated calendar IDs or * for all | * | ❌ |
| RATE_LIMIT_RPS | Requests per second limit | 5 | ❌ |
| IDEMPOTENCY_TTL_SECONDS | Idempotency key TTL in seconds | 86400 | ❌ |
Docker Deployment
1. Build and Start Services
# Build and start in detached mode
docker-compose up -d
# View logs
docker-compose logs -f
# Stop services
docker-compose down2. Test the MCP Server
# Run automated tests
./run-mcp-tests.sh
# Or test manually
docker run --rm -i --network google_calendar_tool_for_agent_default \
-e REDIS_URL=redis://redis:6379/0 calendar-mcp \
node mcp-standalone.js < test-request.jsonMCP Tools Reference
calendar_getEvents
Retrieve events from Google Calendar.
Parameters:
credentials(required): OAuth2 credentials objectcalendarId(optional): Calendar ID, defaults to "primary"timeMin(optional): Start time filter (ISO 8601)timeMax(optional): End time filter (ISO 8601)maxResults(optional): Maximum number of events (1-2500)singleEvents(optional): Expand recurring events, defaults to trueq(optional): Text search querypageToken(optional): Pagination token
calendar_createEvent
Create a new calendar event with idempotency support.
Parameters:
credentials(required): OAuth2 credentials objectuserId(required): User identifier for multi-tenant supportidempotencyKey(required): Unique key to prevent duplicatessummary(required): Event titledescription(optional): Event descriptionstart(required): Start time object with dateTime and timeZoneend(required): End time object with dateTime and timeZoneattendees(optional): Array of attendee objectsconference(optional): Conference/meeting settingsreminders(optional): Reminder settingsrecurrence(optional): Recurrence rules
calendar_updateEvent
Update an existing calendar event.
Parameters:
credentials(required): OAuth2 credentials objectuserId(required): User identifiereventId(required): Event ID to updateidempotencyKey(required): Unique key for the update operationpatch(required): Object containing fields to update
calendar_deleteEvent
Delete a calendar event.
Parameters:
credentials(required): OAuth2 credentials objectuserId(required): User identifiereventId(required): Event ID to deletesendUpdates(optional): Whether to send cancellation emails
JSON-RPC Examples
List Events
{
"jsonrpc": "2.0",
"id": 1,
"method": "tools/call",
"params": {
"name": "calendar_getEvents",
"arguments": {
"credentials": {
"access_token": "ya29.a0AS3H6Ny...",
"refresh_token": "1//0cH2eR6nJy..."
},
"timeMin": "2025-09-10T00:00:00.000Z",
"timeMax": "2025-09-13T23:59:59.000Z",
"maxResults": 10
}
}
}Create Event
{
"jsonrpc": "2.0",
"id": 2,
"method": "tools/call",
"params": {
"name": "calendar_createEvent",
"arguments": {
"credentials": {
"access_token": "ya29.a0AS3H6Ny...",
"refresh_token": "1//0cH2eR6nJy..."
},
"userId": "user-123",
"idempotencyKey": "unique-key-2025-09-10-001",
"summary": "Team Meeting",
"description": "Weekly team sync meeting",
"start": {
"dateTime": "2025-09-12T10:00:00.000Z",
"timeZone": "America/New_York"
},
"end": {
"dateTime": "2025-09-12T11:00:00.000Z",
"timeZone": "America/New_York"
},
"attendees": [
{
"email": "[email protected]",
"optional": false
}
],
"conference": {
"create": true
},
"reminders": {
"useDefault": false,
"overrides": [
{
"method": "email",
"minutes": 15
},
{
"method": "popup",
"minutes": 5
}
]
}
}
}
}Update Event
{
"jsonrpc": "2.0",
"id": 3,
"method": "tools/call",
"params": {
"name": "calendar_updateEvent",
"arguments": {
"credentials": {
"access_token": "ya29.a0AS3H6Ny...",
"refresh_token": "1//0cH2eR6nJy..."
},
"userId": "user-123",
"eventId": "event_id_from_create_response",
"idempotencyKey": "update-key-2025-09-10-001",
"patch": {
"summary": "Updated Team Meeting",
"description": "Updated description with agenda",
"start": {
"dateTime": "2025-09-12T14:00:00.000Z",
"timeZone": "America/New_York"
},
"end": {
"dateTime": "2025-09-12T15:00:00.000Z",
"timeZone": "America/New_York"
}
}
}
}
}Delete Event
{
"jsonrpc": "2.0",
"id": 4,
"method": "tools/call",
"params": {
"name": "calendar_deleteEvent",
"arguments": {
"credentials": {
"access_token": "ya29.a0AS3H6Ny...",
"refresh_token": "1//0cH2eR6nJy..."
},
"userId": "user-123",
"eventId": "event_id_to_delete",
"sendUpdates": "all"
}
}
}n8n Integration Guide
Prerequisites for n8n
- n8n instance with MCP Client node support
- Running Google Calendar MCP Server (this project)
- User OAuth2 tokens stored in your user management system
Setup Steps
1. Deploy MCP Server
# Clone and deploy this MCP server
git clone <repository-url>
cd google_calendar_tool_for_agent
cp .env.example .env
# Configure .env with your Google OAuth2 credentials
docker-compose up -d2. Configure n8n MCP Client Node
In your n8n workflow:
- Add an MCP Client node
- Configure the MCP connection:
- MCP Server Command:
docker run --rm -i --network google_calendar_tool_for_agent_default -e REDIS_URL=redis://redis:6379/0 calendar-mcp node mcp-standalone.js - Working Directory:
/path/to/google_calendar_tool_for_agent
- MCP Server Command:
3. Dynamic Credential Injection
Create a workflow that dynamically injects user credentials:
// In a Function node before MCP Client
const userTokens = $('Get User Tokens').first().json;
return {
json: {
tool: "calendar_getEvents",
arguments: {
credentials: {
access_token: userTokens.access_token,
refresh_token: userTokens.refresh_token
},
timeMin: "2025-09-10T00:00:00.000Z",
timeMax: "2025-09-17T23:59:59.000Z",
maxResults: 50
}
}
};4. Example n8n Workflow Structure
1. [Webhook/Trigger] →
2. [Get User from Database] →
3. [Function: Prepare MCP Request] →
4. [MCP Client: Calendar Operation] →
5. [Function: Process Response] →
6. [Response/Action]5. Error Handling
Add error handling for common scenarios:
// In a Function node after MCP Client
const mcpResponse = $('MCP Client').first().json;
if (mcpResponse.error) {
if (mcpResponse.error.message.includes('invalid_grant')) {
// Token expired, trigger refresh flow
return { json: { action: 'refresh_token', userId: $json.userId } };
}
throw new Error(`MCP Error: ${mcpResponse.error.message}`);
}
return { json: mcpResponse.result };Multi-tenant Best Practices
- Store user tokens securely in your database
- Implement token refresh logic for expired access tokens
- Use unique idempotency keys per user and operation
- Validate user permissions before making calendar requests
- Handle rate limiting with appropriate delays
Example Multi-tenant Flow
// Function node: Prepare Calendar Request
const userId = $json.userId;
const operation = $json.operation;
const userTokens = $('Database').first().json.tokens;
const idempotencyKey = `${userId}-${operation}-${Date.now()}`;
return {
json: {
tool: "calendar_createEvent",
arguments: {
credentials: userTokens,
userId: userId,
idempotencyKey: idempotencyKey,
summary: $json.eventTitle,
description: $json.eventDescription,
start: {
dateTime: $json.startTime,
timeZone: $json.timezone || "UTC"
},
end: {
dateTime: $json.endTime,
timeZone: $json.timezone || "UTC"
}
}
}
};Development Setup
Local Development
# Install dependencies
npm install
# Build TypeScript
npm run build
# Start development server (non-MCP mode)
npm run dev
# Run tests
npm test
# Run MCP server locally
node mcp-standalone.jsTesting
# Run all tests
npm test
# Run specific test file
npm test -- src/mcp/schemas.test.ts
# Test MCP server manually
echo '{"jsonrpc":"2.0","id":1,"method":"tools/list"}' | node mcp-standalone.jsProject Structure
src/
├── config.ts # Environment configuration
├── google/
│ ├── mappers.ts # Google API response mappers
│ └── mappers.test.ts # Mapper tests
├── http/
│ └── app.ts # HTTP server (development only)
├── infra/
│ ├── idem.ts # Idempotency service
│ ├── logger.ts # Logging (disabled in MCP mode)
│ └── redis.ts # Redis client
└── mcp/
├── schemas.ts # Zod validation schemas
├── schemas.test.ts # Schema tests
├── server.ts # MCP server implementation
└── tools.ts # MCP tool implementations
tests/ # Integration tests
docker-compose.yml # Docker services
Dockerfile # Container definition
mcp-standalone.js # MCP server entry pointTroubleshooting
Common Issues
1. Container Exits Immediately
Symptom: Docker container starts and exits with code 0 Solution: This is normal behavior. MCP servers are stateless and exit when no input is provided.
2. Redis Connection Errors
Symptom: "Reached the max retries per request limit" Solution: Ensure Redis is running and accessible:
docker-compose up redis -d
docker run --rm --network google_calendar_tool_for_agent_default redis:7-alpine redis-cli -h redis ping3. OAuth Token Errors
Symptom: "invalid_grant" or "unauthorized_client" Solutions:
- Verify Client ID and Secret in
.env - Check token expiration and refresh
- Ensure correct OAuth scopes
- Verify redirect URI matches Google Cloud Console
4. MCP Protocol Errors
Symptom: Invalid JSON-RPC responses Solutions:
- Ensure no console output in MCP mode
- Validate JSON-RPC request format
- Check tool parameter schemas
5. Calendar API Errors
Symptom: Google Calendar API returns 403/404 errors Solutions:
- Verify Calendar API is enabled in Google Cloud Console
- Check calendar permissions for the authenticated user
- Ensure calendar ID exists and is accessible
Debug Mode
For debugging, you can temporarily enable logging:
// In mcp-standalone.js, comment out console overrides
// console.log = () => {};
// console.error = () => {};Health Checks
# Test MCP server connectivity
echo '{"jsonrpc":"2.0","id":1,"method":"tools/list"}' | \
docker run --rm -i --network google_calendar_tool_for_agent_default \
-e REDIS_URL=redis://redis:6379/0 calendar-mcp node mcp-standalone.js
# Test Redis connectivity
docker run --rm --network google_calendar_tool_for_agent_default \
redis:7-alpine redis-cli -h redis ping
# Test Google Calendar API
curl -H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
"https://www.googleapis.com/calendar/v3/calendars/primary/events?maxResults=1"Architecture
- Stateless Design: No credential storage, all auth passed per request
- Redis Backend: Idempotency and caching support
- Docker Native: Optimized for container deployment
- Protocol Compliant: Follows MCP specification strictly
- Multi-tenant: Supports multiple users with isolated credentials
- Production Ready: Error handling, logging, and monitoring support
Security Considerations
- Never commit credentials to version control
- Use environment variables for sensitive configuration
- Implement proper token refresh logic
- Validate all inputs using Zod schemas
- Use HTTPS in production deployments
- Implement rate limiting to prevent abuse
- Monitor and log security events
License
MIT License - see LICENSE file for details.
