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

intentkit-calendar

v1.0.1

Published

CalDAV calendar adapter for IntentKit via tsdav

Readme

intentkit-calendar

CalDAV calendar adapter for IntentKit — read, create, update, and delete calendar events via the provider system using tsdav.

This adapter lets AI agents (like Claude via Dispatch) manage calendars through MCP.

Install

npm install intentkit-calendar

Quick Start

import { defineFunction, IntentRegistry, createContext, serve, z } from 'intentkit';
import { createCalendarProvider, type CalendarClient } from 'intentkit-calendar';

// Register your functions
const registry = new IntentRegistry().register(listCalendars, listEvents, createEvent);

// Create context (no database needed for calendar-only projects)
const context = await createContext({ events: true });

// Boot MCP server with calendar provider
await serve({
  name: 'my-calendar-agent',
  registry,
  context,
  providers: [
    createCalendarProvider({
      serverUrl: 'https://caldav.icloud.com',
      username: '[email protected]',
      password: process.env.ICLOUD_APP_PASSWORD!,
    }),
  ],
});

Configuration

| Option | Default | Description | |--------|---------|-------------| | serverUrl | — | CalDAV server URL | | username | — | CalDAV username | | password | — | Password or app-specific password | | authMethod | 'basic' | Authentication method ('basic' or 'oauth') | | defaultCalendar | — | Display name or URL of default calendar | | name | 'calendar' | Provider name in ctx.providers |

Common Provider Configs

iCloud Calendar (requires App-Specific Password):

createCalendarProvider({
  serverUrl: 'https://caldav.icloud.com',
  username: '[email protected]',
  password: process.env.ICLOUD_APP_PASSWORD!,
})

The iCloud CalDAV URL is the same for all users. Discovery is handled automatically by tsdav via the /.well-known/caldav endpoint.

Google Calendar (via CalDAV, requires App Password):

createCalendarProvider({
  serverUrl: 'https://apidata.googleusercontent.com/caldav/v2/',
  username: '[email protected]',
  password: process.env.GOOGLE_APP_PASSWORD!,
})

Note: Google's CalDAV support requires an app-specific password. The primary calendar URL is typically https://apidata.googleusercontent.com/caldav/v2/{email}/events/.

Nextcloud:

createCalendarProvider({
  serverUrl: 'https://cloud.example.com/remote.php/dav',
  username: 'your-username',
  password: process.env.NEXTCLOUD_PASSWORD!,
})

Radicale:

createCalendarProvider({
  serverUrl: 'https://radicale.example.com',
  username: 'your-username',
  password: process.env.RADICALE_PASSWORD!,
})

Baikal:

createCalendarProvider({
  serverUrl: 'https://baikal.example.com/dav.php',
  username: 'your-username',
  password: process.env.BAIKAL_PASSWORD!,
})

Using in Functions

Access the calendar client via ctx.providers.calendar:

import { defineFunction, z } from 'intentkit';
import type { CalendarClient } from 'intentkit-calendar';

export const todaysEvents = defineFunction({
  name: 'todays_events',
  intent: 'List all events happening today',
  permissions: ['calendar:read'],
  requires: ['calendar'],  // Validates provider exists at startup

  input: z.object({}),
  output: z.object({
    events: z.array(z.object({
      summary: z.string(),
      start: z.string(),
      end: z.string(),
    })),
  }),

  execute: async (_input, ctx) => {
    const cal = ctx.providers.calendar as CalendarClient;

    const now = new Date();
    const startOfDay = new Date(now.getFullYear(), now.getMonth(), now.getDate()).toISOString();
    const endOfDay = new Date(now.getFullYear(), now.getMonth(), now.getDate() + 1).toISOString();

    const events = await cal.listEvents({ start: startOfDay, end: endOfDay });

    return {
      events: events.map(e => ({ summary: e.summary, start: e.start, end: e.end })),
    };
  },
});

Example Functions

The package includes 5 ready-to-use functions in functions/events.ts:

| Function | Intent | Permission | |----------|--------|------------| | list_calendars | List all available calendars | calendar:read | | list_events | List events in a date range | calendar:read | | create_event | Create a new calendar event | calendar:write | | update_event | Update an existing event | calendar:write | | delete_event | Delete an event by UID | calendar:write |

Import and register them:

import { listCalendars, listEvents, createEvent, updateEvent, deleteEvent } from 'intentkit-calendar/functions';

const registry = new IntentRegistry()
  .register(listCalendars, listEvents, createEvent, updateEvent, deleteEvent);

CalendarClient API

The full client interface for custom function implementations:

interface CalendarClient {
  // Calendars
  listCalendars(): Promise<Calendar[]>;

  // Events
  listEvents(options: ListEventsOptions): Promise<CalendarEvent[]>;
  getEvent(uid: string, calendar?: string): Promise<CalendarEvent | null>;
  createEvent(input: CreateEventInput): Promise<CalendarEvent>;
  updateEvent(input: UpdateEventInput): Promise<CalendarEvent>;
  deleteEvent(uid: string, calendar?: string): Promise<void>;

  // Connection
  ping(): Promise<boolean>;
}

iCalendar Parsing

The adapter uses regex-based parsing to extract VEVENT properties from raw iCalendar (RFC 5545) text. This handles common cases well:

  • Standard properties: SUMMARY, DESCRIPTION, LOCATION, DTSTART, DTEND, UID, STATUS, ORGANIZER, ATTENDEE, RRULE, CREATED, LAST-MODIFIED
  • Date formats: date-time (20260321T100000Z), date-only (20260321) for all-day events
  • Line unfolding (RFC 5545 section 3.1)
  • Text escaping/unescaping (RFC 5545 section 3.3.11)

Not fully supported (inherent to regex-based parsing):

  • Multi-valued properties with complex parameters
  • VALARM (reminders)
  • VTIMEZONE definitions
  • Recurrence expansion (RRULE is stored as-is, not expanded)

For most calendar use cases via AI agents, the regex parser covers what's needed. If you need full RFC 5545 compliance, consider wrapping a dedicated iCal library in a custom client.

Claude Desktop Config

Add to ~/Library/Application Support/Claude/claude_desktop_config.json:

{
  "mcpServers": {
    "calendar": {
      "command": "node",
      "args": ["path/to/your/serve.js"],
      "env": {
        "ICLOUD_APP_PASSWORD": "your-app-password"
      }
    }
  }
}

Architecture

Claude (Dispatch / Desktop)
    | MCP tool call
IntentKit (serve + permissions + hooks)
    | ctx.providers.calendar
intentkit-calendar (CalendarClientImpl)
    |
  tsdav
  (CalDAV)
    |
CalDAV Server (iCloud, Google, Nextcloud, etc.)

The CalDAV client authenticates via login() at server startup, which discovers the principal URL and calendar home. Subsequent operations use the discovered URLs. Since CalDAV is HTTP-based, there's no persistent connection to manage — the provider's destroy is undefined.

License

MIT