npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

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

  • Docker and Docker Compose
  • Google Cloud Console account
  • Node.js 18+ (for development)

Google Cloud Console Setup

1. Create a Google Cloud Project

  1. Go to Google Cloud Console
  2. Create a new project or select an existing one
  3. Note your project ID

2. Enable Google Calendar API

  1. Navigate to APIs & Services > Library
  2. Search for "Google Calendar API"
  3. Click Enable

3. Create OAuth2 Credentials

  1. Go to APIs & Services > Credentials
  2. Click Create Credentials > OAuth 2.0 Client IDs
  3. 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
  4. 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.site or http://localhost:3000/callback)
  5. 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:

  1. Go to OAuth 2.0 Playground
  2. Click the gear icon (⚙️) and check "Use your own OAuth credentials"
  3. Enter your Client ID and Client Secret
  4. In Step 1, select "Google Calendar API v3" and choose scopes:
    • https://www.googleapis.com/auth/calendar
    • https://www.googleapis.com/auth/calendar.events
  5. Click "Authorize APIs" and complete the OAuth flow
  6. In Step 2, click "Exchange authorization code for tokens"
  7. Copy the access_token and refresh_token

Environment Configuration

1. Copy Environment Template

cp .env.example .env

2. 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=86400

Environment 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 down

2. 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.json

MCP Tools Reference

calendar_getEvents

Retrieve events from Google Calendar.

Parameters:

  • credentials (required): OAuth2 credentials object
  • calendarId (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 true
  • q (optional): Text search query
  • pageToken (optional): Pagination token

calendar_createEvent

Create a new calendar event with idempotency support.

Parameters:

  • credentials (required): OAuth2 credentials object
  • userId (required): User identifier for multi-tenant support
  • idempotencyKey (required): Unique key to prevent duplicates
  • summary (required): Event title
  • description (optional): Event description
  • start (required): Start time object with dateTime and timeZone
  • end (required): End time object with dateTime and timeZone
  • attendees (optional): Array of attendee objects
  • conference (optional): Conference/meeting settings
  • reminders (optional): Reminder settings
  • recurrence (optional): Recurrence rules

calendar_updateEvent

Update an existing calendar event.

Parameters:

  • credentials (required): OAuth2 credentials object
  • userId (required): User identifier
  • eventId (required): Event ID to update
  • idempotencyKey (required): Unique key for the update operation
  • patch (required): Object containing fields to update

calendar_deleteEvent

Delete a calendar event.

Parameters:

  • credentials (required): OAuth2 credentials object
  • userId (required): User identifier
  • eventId (required): Event ID to delete
  • sendUpdates (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

  1. n8n instance with MCP Client node support
  2. Running Google Calendar MCP Server (this project)
  3. 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 -d

2. Configure n8n MCP Client Node

In your n8n workflow:

  1. Add an MCP Client node
  2. 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

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

  1. Store user tokens securely in your database
  2. Implement token refresh logic for expired access tokens
  3. Use unique idempotency keys per user and operation
  4. Validate user permissions before making calendar requests
  5. 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.js

Testing

# 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.js

Project 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 point

Troubleshooting

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 ping

3. 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

  1. Never commit credentials to version control
  2. Use environment variables for sensitive configuration
  3. Implement proper token refresh logic
  4. Validate all inputs using Zod schemas
  5. Use HTTPS in production deployments
  6. Implement rate limiting to prevent abuse
  7. Monitor and log security events

License

MIT License - see LICENSE file for details.