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/fullcalendarpnpm add @protocoltooling/fullcalendaryarn add @protocoltooling/fullcalendarbun add @protocoltooling/fullcalendar2. 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.
Related
- Security — auth boundaries and least-privilege tool exposure
- Custom event models — domain mapping and metadata
- Next.js — App Router patterns
- Troubleshooting