Skip to content
FullCalendar WebMCP
Esc
↑↓navigate↵open⌘Jpreview
On this page

Existing App

Step-by-step guide to add FullCalendar WebMCP to a FullCalendar React app you already ship without changing your UI or backend.

If FullCalendar already runs in your React app, add WebMCP without changing your plugins, themes, or backend.

Keep as-is

  • FullCalendar plugins, views, and renderers
  • API routes, database, and auth
  • React state, React Query, SWR, or similar

1. Install

npm install @protocoltooling/fullcalendar
pnpm add @protocoltooling/fullcalendar
yarn add @protocoltooling/fullcalendar
bun add @protocoltooling/fullcalendar

2. Wrap your API

import type { CalendarEventRepository } from "@protocoltooling/fullcalendar";
import * as api from "./events-api";

export const repository: CalendarEventRepository = {
  list: (query) => api.fetchEvents(query),
  get: (id) => api.fetchEventById(id),
  create: (input) => api.createEvent(input),
  update: (id, input) => api.updateEvent(id, input),
  delete: (id) => api.deleteEvent(id),
};

3. Register the hook

import FullCalendar, { type CalendarRef } from "@fullcalendar/react";
import dayGridPlugin from "@fullcalendar/react/daygrid";
import { useCallback, useEffect, useRef, useState } from "react";
import {
  useFullCalendarWebMCP,
  type CalendarEvent,
} from "@protocoltooling/fullcalendar";
import { repository } from "../services/calendar-adapter";

export function CalendarView() {
  const calendarRef = useRef<CalendarRef>(null);
  const [events, setEvents] = useState<CalendarEvent[]>([]);

  const refreshEvents = useCallback(async () => {
    setEvents(await repository.list());
  }, []);

  useEffect(() => {
    void refreshEvents();
  }, [refreshEvents]);

  useFullCalendarWebMCP({
    calendarRef,
    events: repository,
    onEventsChanged: refreshEvents,
  });

  return (
    <FullCalendar
      ref={calendarRef}
      plugins={[dayGridPlugin]}
      initialView="dayGridMonth"
      events={events.map((event) => ({
        ...event,
        end: event.end ?? undefined,
      }))}
      editable
      selectable
    />
  );
}

Then follow Quickstart → Verify.

Persist human edits

FullCalendar WebMCP does not listen to FullCalendar drag/resize/edit callbacks. If humans can move events in the UI, your app must persist those changes through the same repository agents use:

<FullCalendar
  ref={calendarRef}
  editable
  eventDrop={async (info) => {
    await repository.update(info.event.id, {
      start: info.event.start!.toISOString(),
      end: info.event.end?.toISOString() ?? null,
      allDay: info.event.allDay,
    });
    await refreshEvents();
  }}
  eventResize={async (info) => {
    await repository.update(info.event.id, {
      start: info.event.start!.toISOString(),
      end: info.event.end?.toISOString() ?? null,
      allDay: info.event.allDay,
    });
    await refreshEvents();
  }}
  {/* ... */}
/>

Optional convergence check: after a human drag, run calendar_get_event in DevTools WebMCP and confirm the times match. See Testing → Optional host convergence.

Was this page helpful?