---
title: Custom Event Models
description: Map bookings, sessions, and other domain entities to CalendarEvent, and expose optional read-only metadata to agents.
---

Most apps store appointments, bookings, or sessions—not a generic `CalendarEvent`. Your repository is the translation layer.

See [Security](/integrations/fullcalendar/concepts/security#data-minimization) for metadata exposure guidance.

## Map your domain

```ts
import type { CalendarEvent, CreateCalendarEventInput } from "@protocoltooling/fullcalendar";

interface Appointment {
  appointmentId: string;
  patientName: string;
  startTime: string;
  endTime: string;
  clinicRoom: string;
}

function toCalendarEvent(app: Appointment): CalendarEvent {
  return {
    id: app.appointmentId,
    title: app.patientName,
    start: app.startTime,
    end: app.endTime,
    allDay: false,
  };
}

function toCreatePayload(input: CreateCalendarEventInput, clinicRoom: string) {
  return {
    patientName: input.title,
    startTime: input.start,
    endTime: input.end ?? input.start,
    clinicRoom,
  };
}
```

Use those helpers inside `list` / `get` / `create` / `update` / `delete`. Capture host context (`clinicRoom`, `userId`, tenant ids) in a factory or closure—agents never need those as tool inputs unless you choose to expose them via metadata.

```tsx
const repository = useMemo(
  () => createAppointmentRepository(clinicRoom, doctorId),
  [clinicRoom, doctorId],
);

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

## Metadata

Add `metadata` when agents need context beyond `id`, `title`, `start`, `end`, and `allDay`. You choose exactly what is exposed.

Metadata is **read-only** through FullCalendar WebMCP. Create and update tool schemas reject it.

Protocol Tooling **never** exposes FullCalendar `extendedProps` automatically. Project a deliberate subset from your domain model:

```ts
function toCalendarEvent(app: Appointment): CalendarEvent {
  return {
    id: app.appointmentId,
    title: app.patientName,
    start: app.startTime,
    end: app.endTime,
    allDay: false,
    metadata: {
      location: app.clinicRoom,
    },
  };
}
```

Omit anything agents should not see (notes, billing codes, tenant ids). Private fields stay on your domain entity.

See [`CalendarEvent`](/integrations/fullcalendar/reference/types#calendarevent) for the type definition.
