Skip to main content
Glama
DRITI2906
by DRITI2906
README.md
# MCP Calendar Server

A production-quality **Model Context Protocol (MCP)** server for calendar management with robust timezone handling, recurrence support, and idempotency guarantees.

## ๐ŸŽฏ What is MCP?

The **Model Context Protocol** is a standardized way for AI agents (like Claude, ChatGPT, etc.) to interact with external tools and data sources. This server exposes calendar operations as MCP tools, allowing AI assistants to:

- Create events with timezone-aware scheduling
- List events with automatic recurrence expansion
- Cancel individual instances or entire event series

## โœจ Features

### ๐ŸŒ Timezone Correctness (Top Priority)

Timezone handling is **notoriously difficult**. Here's how we handle it:

- **Storage**: All events stored in UTC (source of truth)
- **Input**: Accept ISO 8601 datetime + IANA timezone (e.g., `"Asia/Kolkata"`)
- **Output**: Return events in their original timezone
- **DST Handling**: Recurrence expansion happens in local time to preserve wall-clock semantics (e.g., "9 AM daily" stays at 9 AM even across DST transitions)
- **Library**: Uses [Luxon](https://moment.github.io/luxon/) for robust timezone math

**Why UTC Storage?**
- Single source of truth
- No ambiguity during DST transitions
- Easy comparison and sorting
- Portable across systems

**Why Original Timezone Matters?**
- Preserves user intent ("9 AM in Kolkata" not "3:30 AM UTC")
- Correct recurrence expansion (daily meetings stay at same wall time)

### ๐Ÿ” Recurrence Support

Supports repeating events with:
- **Frequencies**: `daily`, `weekly`, `monthly`
- **Intervals**: Every N days/weeks/months (e.g., "every 2 weeks")
- **End Dates**: `until` parameter (ISO 8601)

**Implementation Design:**
- Recurrence rules stored as JSON in the database
- **Virtual instances** generated on-demand during `calendar.list`
- No duplicate rows for recurring events
- Cancel individual instances via `cancellations` table
- Safety limits: Max 1000 instances per query to prevent DOS

### ๐Ÿ”’ Idempotency

Prevents duplicate events from retries:
- Optional `idempotency_key` parameter in `calendar.create`
- If key exists, returns existing event (no duplicate created)
- Unique constraint enforced at database level

### ๐Ÿ’พ Storage

Uses **SQLite** with `better-sqlite3`:
- **Synchronous API**: Simpler, more reliable than async
- **WAL Mode**: Better concurrency
- **Schema**:
  - `events`: Core event data (title, start_utc, duration, timezone, recurrence)
  - `cancellations`: Tracks cancelled instances of recurring events
  - Indexes on `start_utc` and `idempotency_key` for performance

## ๐Ÿ“ฆ Installation

```bash
# Clone or navigate to project directory
cd calender-mcp

# Install dependencies
npm install

# Build TypeScript
npm run build
```

## ๐Ÿš€ Usage

### Running the Server

```bash
npm start
```

The server runs on **stdio** (standard input/output) as per MCP specification.

### Configuring with Claude Desktop

Add to your Claude Desktop config (`claude_desktop_config.json`):

```json
{
  "mcpServers": {
    "calendar": {
      "command": "node",
      "args": ["C:/path/to/calender-mcp/dist/index.js"]
    }
  }
}
```

### Example Tool Calls

#### 1. Create a Simple Event

```json
{
  "tool": "calendar.create",
  "arguments": {
    "title": "Team Standup",
    "start": "2026-01-20T09:00:00",
    "end": "2026-01-20T09:30:00",
    "tz": "Asia/Kolkata"
  }
}
```

#### 2. Create a Recurring Event

```json
{
  "tool": "calendar.create",
  "arguments": {
    "title": "Weekly Review",
    "start": "2026-01-20T15:00:00",
    "end": "2026-01-20T16:00:00",
    "tz": "America/New_York",
    "recurrence": {
      "freq": "weekly",
      "interval": 1,
      "until": "2026-06-30T23:59:59"
    },
    "idempotency_key": "weekly-review-2026"
  }
}
```

#### 3. List Events

```json
{
  "tool": "calendar.list",
  "arguments": {
    "range_start": "2026-01-20T00:00:00Z",
    "range_end": "2026-01-27T00:00:00Z"
  }
}
```

#### 4. Cancel an Entire Event Series

```json
{
  "tool": "calendar.cancel",
  "arguments": {
    "event_id": "abc-123-def-456"
  }
}
```

#### 5. Cancel a Specific Instance

```json
{
  "tool": "calendar.cancel",
  "arguments": {
    "event_id": "abc-123-def-456",
    "instance_start_utc": "2026-01-27T10:00:00.000Z"
  }
}
```

## ๐Ÿ—๏ธ Architecture

```
src/
โ”œโ”€โ”€ index.ts              # MCP server setup & tool registration
โ”œโ”€โ”€ storage/
โ”‚   โ””โ”€โ”€ db.ts            # SQLite schema & connection
โ”œโ”€โ”€ time/
โ”‚   โ””โ”€โ”€ timezone.ts      # UTC conversion utilities (Luxon)
โ”œโ”€โ”€ recurrence/
โ”‚   โ””โ”€โ”€ expand.ts        # Recurrence expansion logic
โ””โ”€โ”€ tools/
    โ”œโ”€โ”€ create.ts        # calendar.create handler
    โ”œโ”€โ”€ list.ts          # calendar.list handler
    โ””โ”€โ”€ cancel.ts        # calendar.cancel handler
```

## ๐Ÿงช Testing

Create a test script (`test.mjs`):

```javascript
import { spawn } from 'child_process';

const server = spawn('node', ['dist/index.js']);

// Send MCP request
const request = {
  jsonrpc: '2.0',
  id: 1,
  method: 'tools/call',
  params: {
    name: 'calendar.create',
    arguments: {
      title: 'Test Event',
      start: '2026-01-20T10:00:00',
      end: '2026-01-20T11:00:00',
      tz: 'UTC'
    }
  }
};

server.stdin.write(JSON.stringify(request) + '\n');

server.stdout.on('data', (data) => {
  console.log('Response:', data.toString());
});
```

Run: `node test.mjs`

## ๐Ÿšง Known Limitations

1. **No Sync**: Events are stored locally only. No Google Calendar / Outlook sync.
2. **No Notifications**: Server doesn't send reminders or alerts.
3. **Recurrence Complexity**: Only supports simple rules (no "2nd Tuesday of month").
4. **Performance**: Recurrence expansion is brute-force iteration (acceptable for <1000 instances).
5. **No Conflict Detection**: Doesn't prevent overlapping events.

## ๐Ÿ”ฎ Future Extensions

- **Calendar Sync**: Integrate with Google Calendar API, Microsoft Graph
- **Notifications**: Email/SMS reminders via Twilio, SendGrid
- **Search**: Full-text search on event titles/notes
- **Attachments**: Store files/links with events
- **Attendees**: Multi-user support with invitations
- **Conflict Detection**: Warn about overlapping events
- **Advanced Recurrence**: RRULE support (RFC 5545)

## ๐Ÿ“š Design Decisions

### Why Luxon over Moment/Date-fns?

- **Immutable**: Safer API, no accidental mutations
- **IANA Timezone Support**: Built-in, no extra plugins
- **Modern**: Active development, ESM-first

### Why SQLite over JSON Files?

- **ACID Guarantees**: Atomic writes, no corruption
- **Indexes**: Fast lookups on `start_utc`, `idempotency_key`
- **Constraints**: Unique keys enforced at DB level
- **Scalability**: Handles thousands of events efficiently

### Why Virtual Instances for Recurrence?

- **Storage Efficiency**: One row instead of hundreds
- **Flexibility**: Change recurrence rule retroactively
- **Cancellations**: Track exceptions cleanly

## ๐Ÿ“„ License

ISC

---

**Built with โค๏ธ for the MCP ecosystem**