Skip to main content
Glama

calendar-mcp

by deciduus
models.py13.4 kB
import datetime # Import the module itself from pydantic import BaseModel, Field, EmailStr from typing import Optional, List, Dict, Any # from datetime import datetime, date # Keep original import commented for reference # Based on Google Calendar API v3 Event resource documentation: # https://developers.google.com/calendar/api/v3/reference/events#resource class EventDateTime(BaseModel): """Represents the start or end time of an event.""" date: Optional[datetime.date] = None dateTime: Optional[datetime.datetime] = None # Renamed from 'date_time' to match API JSON timeZone: Optional[str] = None # Renamed from 'time_zone' class Config: populate_by_name = True # Changed from allow_population_by_field_name # orm_mode = True # Removed, orm_mode is deprecated in Pydantic V2, use from_attributes=True class EventAttendee(BaseModel): """Represents an attendee of an event.""" id: Optional[str] = None email: Optional[EmailStr] = None displayName: Optional[str] = None # Renamed from 'display_name' organizer: Optional[bool] = None self: Optional[bool] = None resource: Optional[bool] = None optional: Optional[bool] = None responseStatus: Optional[str] = None # Renamed from 'response_status' comment: Optional[str] = None additionalGuests: Optional[int] = None # Renamed from 'additional_guests' class Config: populate_by_name = True # Changed from allow_population_by_field_name # orm_mode = True # Removed, orm_mode is deprecated in Pydantic V2, use from_attributes=True class EventCreator(BaseModel): """Represents the creator of an event.""" id: Optional[str] = None email: Optional[EmailStr] = None display_name: Optional[str] = Field(None, alias='displayName') self: Optional[bool] = None # Whether the creator corresponds to the calendar on which this copy of the event appears. class Config: populate_by_name = True class EventOrganizer(BaseModel): """Represents the organizer of an event.""" id: Optional[str] = None email: Optional[EmailStr] = None display_name: Optional[str] = Field(None, alias='displayName') self: Optional[bool] = None # Whether the organizer corresponds to the calendar on which this copy of the event appears. class Config: populate_by_name = True class EventReminderOverride(BaseModel): method: Optional[str] = None minutes: Optional[int] = None class Config: populate_by_name = True # Changed from allow_population_by_field_name # orm_mode = True # Removed, orm_mode is deprecated in Pydantic V2, use from_attributes=True class EventReminders(BaseModel): useDefault: bool = Field(..., alias="useDefault") # Renamed from 'use_default' overrides: Optional[List[EventReminderOverride]] = None class Config: populate_by_name = True # Changed from allow_population_by_field_name # orm_mode = True # Removed, orm_mode is deprecated in Pydantic V2, use from_attributes=True # --- Main Event Model --- class GoogleCalendarEvent(BaseModel): """Pydantic model representing a Google Calendar event resource.""" kind: str = "calendar#event" id: Optional[str] = Field(None, description="Opaque identifier of the event.") status: Optional[str] = Field(None, description="Status of the event ('confirmed', 'tentative', 'cancelled').") html_link: Optional[str] = Field(None, alias='htmlLink', description="URL for the event in the Google Calendar UI.") created: Optional[datetime.datetime] = Field(None, description="Creation time of the event (RFC3339 format).") updated: Optional[datetime.datetime] = Field(None, description="Last modification time of the event (RFC3339 format).") summary: Optional[str] = Field(None, description="Title of the event.") description: Optional[str] = Field(None, description="Description of the event. Optional.") location: Optional[str] = Field(None, description="Geographic location of the event. Optional.") color_id: Optional[str] = Field(None, alias='colorId', description="Color of the event. Optional.") creator: Optional[EventCreator] = Field(None, description="The creator of the event. Read-only.") organizer: Optional[EventOrganizer] = Field(None, description="The organizer of the event.") start: Optional[EventDateTime] = Field(None, description="The start time of the event.") end: Optional[EventDateTime] = Field(None, description="The end time of the event.") end_time_unspecified: Optional[bool] = Field(None, alias='endTimeUnspecified', description="Whether the end time is actually unspecified.") recurrence: Optional[List[str]] = Field(None, description="List of RRULE, EXRULE, RDATE or EXDATE properties for recurring events.") recurring_event_id: Optional[str] = Field(None, alias='recurringEventId', description="For an instance of a recurring event, this is the id of the recurring event itself.") original_start_time: Optional[EventDateTime] = Field(None, alias='originalStartTime', description="For an instance of a recurring event, this is the original start time of the instance before modification.") attendees: Optional[List[EventAttendee]] = Field([], description="The attendees of the event.") attendees_omitted: Optional[bool] = Field(None, alias='attendeesOmitted', description="Whether attendees were omitted.") reminders: Optional[EventReminders] = Field(None, description="Information about the event's reminders.") # Add other fields as needed (e.g., attachments, conferenceData, gadget, source, etc.) class Config: populate_by_name = True # Consider adding validation logic, e.g., ensuring start is before end # --- Models for API Requests/Responses --- class EventCreateRequest(BaseModel): """Model for the request body when creating a detailed event.""" summary: str start: EventDateTime end: EventDateTime description: Optional[str] = None location: Optional[str] = None attendees: Optional[List[EmailStr]] = Field(None, description="List of attendee email addresses to invite.") recurrence: Optional[List[str]] = Field(None, description="List of RRULEs, EXRULEs, RDATEs or EXDATEs for recurring events.") reminders: Optional[EventReminders] = Field(None, description="Notification settings for the event.") # Add other creatable fields as needed class QuickAddEventRequest(BaseModel): """Model for the request body when using the quickAdd endpoint.""" text: str = Field(..., description="The text describing the event to be parsed by Google Calendar.") class EventUpdateRequest(BaseModel): """Model for the request body when updating an event. Contains only the fields that can be updated. """ summary: Optional[str] = None start: Optional[EventDateTime] = None end: Optional[EventDateTime] = None description: Optional[str] = None location: Optional[str] = None attendees: Optional[List[EventAttendee]] = None # Allow updating attendee details or list # Add other updatable fields class AddAttendeeRequest(BaseModel): """Model for adding attendees to an existing event.""" attendee_emails: List[EmailStr] = Field(..., description="List of email addresses to add as attendees.") # You might also want models for CalendarList entries, etc. # Define NotificationSettings first as it's used in CalendarListEntry class NotificationSettings(BaseModel): """Represents notification settings for a calendar.""" notifications: Optional[List[Dict[str, str]]] = None # List of {'type': 'eventCreation', 'method': 'email'} etc. class Config: populate_by_name = True # Changed from allow_population_by_field_name class CalendarListEntry(BaseModel): """Represents an entry in the user's calendar list.""" kind: str = "calendar#calendarListEntry" etag: str id: str summary: Optional[str] = None description: Optional[str] = None location: Optional[str] = None timeZone: Optional[str] = None # Renamed from 'time_zone' summaryOverride: Optional[str] = None # Renamed from 'summary_override' colorId: Optional[str] = None # Renamed from 'color_id' backgroundColor: Optional[str] = None # Renamed from 'background_color' foregroundColor: Optional[str] = None # Renamed from 'foreground_color' hidden: Optional[bool] = None selected: Optional[bool] = None accessRole: Optional[str] = None # Renamed from 'access_role' defaultReminders: Optional[List[EventReminderOverride]] = None # Renamed from 'default_reminders' notificationSettings: Optional[NotificationSettings] = None # Renamed from 'notification_settings' primary: Optional[bool] = None deleted: Optional[bool] = None class CalendarListResponse(BaseModel): """Response containing a list of calendars.""" kind: str = "calendar#calendarList" items: List[CalendarListEntry] = [] nextPageToken: Optional[str] = None nextSyncToken: Optional[str] = None # Re-inserting EventsResponse definition class EventsResponse(BaseModel): """Response containing a list of events.""" kind: str = "calendar#events" summary: Optional[str] = None description: Optional[str] = None updated: Optional[datetime.datetime] = None timeZone: Optional[str] = None accessRole: Optional[str] = None defaultReminders: Optional[List[EventReminderOverride]] = [] items: List[GoogleCalendarEvent] = [] nextPageToken: Optional[str] = None nextSyncToken: Optional[str] = None class CalendarList(BaseModel): """Represents the user's list of calendars.""" kind: str = "calendar#calendarList" etag: str nextPageToken: Optional[str] = None # Renamed from 'next_page_token' nextSyncToken: Optional[str] = None # Renamed from 'next_sync_token' items: List[CalendarListEntry] class Config: populate_by_name = True # Changed from allow_population_by_field_name # --- Models for Advanced Actions --- # --- Check Attendee Status --- class CheckAttendeeStatusRequest(BaseModel): event_id: str calendar_id: str = 'primary' attendee_emails: Optional[List[EmailStr]] = None class CheckAttendeeStatusResponse(BaseModel): status_map: Dict[EmailStr, str] = Field(..., description="Mapping of attendee email to their responseStatus ('accepted', 'declined', etc.)") # --- Find Availability (Free/Busy) --- class FreeBusyRequestItem(BaseModel): id: str # Calendar ID class FreeBusyRequest(BaseModel): time_min: datetime.datetime = Field(..., alias='timeMin') time_max: datetime.datetime = Field(..., alias='timeMax') items: List[FreeBusyRequestItem] # Optional: timeZone, groupExpansionMax, calendarExpansionMax time_zone: Optional[str] = Field(None, alias='timeZone') class Config: populate_by_name = True class TimePeriod(BaseModel): start: datetime.datetime end: datetime.datetime class FreeBusyError(BaseModel): domain: str reason: str class CalendarBusyInfo(BaseModel): errors: Optional[List[FreeBusyError]] = None busy: List[TimePeriod] = [] class FreeBusyResponse(BaseModel): kind: str = "calendar#freeBusy" time_min: datetime.datetime = Field(..., alias='timeMin') time_max: datetime.datetime = Field(..., alias='timeMax') calendars: Dict[str, CalendarBusyInfo] = {} # Optional: groups class Config: populate_by_name = True # --- Find Mutual Availability & Schedule --- class ScheduleMutualRequest(BaseModel): attendee_calendar_ids: List[str] = Field(..., description="List of calendar IDs (usually emails) for attendees whose availability should be checked.") time_min: datetime.datetime time_max: datetime.datetime duration_minutes: int event_details: EventCreateRequest # Use the existing model for core event info organizer_calendar_id: str = 'primary' working_hours_start_str: Optional[str] = Field(None, description="Optional start time for working hours constraint (HH:MM format)") working_hours_end_str: Optional[str] = Field(None, description="Optional end time for working hours constraint (HH:MM format)") send_notifications: bool = True # Response is GoogleCalendarEvent # --- Project Recurring Events --- class ProjectRecurringRequest(BaseModel): time_min: datetime.datetime time_max: datetime.datetime calendar_id: str = 'primary' event_query: Optional[str] = None # Define ProjectedEventOccurrence within models.py for consistency class ProjectedEventOccurrenceModel(BaseModel): original_event_id: str original_summary: str occurrence_start: datetime.datetime occurrence_end: datetime.datetime class ProjectRecurringResponse(BaseModel): projected_occurrences: List[ProjectedEventOccurrenceModel] # --- Analyze Busyness --- class AnalyzeBusynessRequest(BaseModel): time_min: datetime.datetime time_max: datetime.datetime calendar_id: str = 'primary' class DailyBusynessStats(BaseModel): event_count: int total_duration_minutes: float class AnalyzeBusynessResponse(BaseModel): # Use string representation for date keys in JSON busyness_by_date: Dict[str, DailyBusynessStats] = Field(..., description="Mapping of date string (YYYY-MM-DD) to busyness stats")

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/deciduus/calendar-mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server