from typing import Optional from fastmcp import FastMCP from services.calendar_service import CalendarService def register_calendar_tools(mcp: FastMCP, service: CalendarService): """Register all calendar-related MCP tools.""" @mcp.tool(description="List all available calendars from CalDAV and configured ICS feeds. Returns calendar ID, name, and properties.") def list_calendars() -> list[dict]: """List all calendars.""" calendars = service.list_calendars() return [c.model_dump() for c in calendars] @mcp.tool(description="List events within a date range. If no calendar is specified, lists events from all calendars.") def list_events( start_date: str, end_date: str, calendar_id: Optional[str] = None, include_recurring: bool = True, ) -> dict: """ List events in a date range. Args: start_date: Start of date range (ISO format: YYYY-MM-DD) end_date: End of date range (ISO format: YYYY-MM-DD) calendar_id: The calendar ID (CalDAV URL or ICS ID) to query. If not provided, lists from all calendars. include_recurring: Whether to expand recurring events (default: True) """ if calendar_id: # Single calendar result = service.list_events(calendar_id, start_date, end_date, include_recurring) return result.model_dump() else: # All calendars calendars = service.list_calendars() all_events = [] for cal in calendars: try: result = service.list_events(cal.id, start_date, end_date, include_recurring) for event in result.events: event_dict = event.model_dump() event_dict["calendar_name"] = cal.name all_events.append(event_dict) except Exception: continue # Skip calendars that fail # Sort by start time all_events.sort(key=lambda e: e.get("start", "")) return { "events": all_events, "total": len(all_events), "calendar_id": "all", "start_date": start_date, "end_date": end_date, } @mcp.tool(description="Get detailed information about a specific calendar event including attendees and recurrence.") def get_event( calendar_id: str, event_id: str, ) -> Optional[dict]: """ Get a specific event. Args: calendar_id: The calendar ID (CalDAV URL or ICS ID) containing the event event_id: The unique ID (UID) of the event """ result = service.get_event(calendar_id, event_id) return result.model_dump() if result else None @mcp.tool(description="Create a new calendar event with title, time, location, attendees, and optional recurrence.") def create_event( calendar_id: str, title: str, start: str, end: str, description: Optional[str] = None, location: Optional[str] = None, attendees: Optional[list[str]] = None, reminders: Optional[list[int]] = None, recurrence: Optional[str] = None, ) -> dict: """ Create a new calendar event. Args: calendar_id: The calendar ID to create the event in (CalDAV only) title: Event title/summary start: Start datetime (ISO format: YYYY-MM-DDTHH:MM:SS) end: End datetime (ISO format: YYYY-MM-DDTHH:MM:SS) description: Event description (optional) location: Event location (optional) attendees: List of attendee email addresses (optional) reminders: List of reminder times in minutes before event (optional) recurrence: iCalendar RRULE string for recurring events (optional) """ result = service.create_event( calendar_id, title, start, end, description, location, attendees, reminders, recurrence ) return result.model_dump() @mcp.tool(description="Update an existing calendar event. Only provided fields will be modified.") def update_event( calendar_id: str, event_id: str, title: Optional[str] = None, start: Optional[str] = None, end: Optional[str] = None, description: Optional[str] = None, location: Optional[str] = None, attendees: Optional[list[str]] = None, ) -> Optional[dict]: """ Update an existing event. Args: calendar_id: The calendar ID containing the event (CalDAV only) event_id: The unique ID of the event to update title: New event title (optional) start: New start datetime (optional) end: New end datetime (optional) description: New description (optional) location: New location (optional) attendees: New list of attendee emails (optional) """ result = service.update_event( calendar_id, event_id, title, start, end, description, location, attendees ) return result.model_dump() if result else None @mcp.tool(description="Delete a calendar event by ID.") def delete_event( calendar_id: str, event_id: str, notify_attendees: bool = True, ) -> dict: """ Delete a calendar event. Args: calendar_id: The calendar ID containing the event (CalDAV only) event_id: The unique ID of the event to delete notify_attendees: Whether to notify attendees of cancellation (default: True) """ result = service.delete_event(calendar_id, event_id, notify_attendees) return result.model_dump()