diff --git a/src/apple_eventkit_mcp/calendar_tools.py b/src/apple_eventkit_mcp/calendar_tools.py index f7f73fa..6a29067 100644 --- a/src/apple_eventkit_mcp/calendar_tools.py +++ b/src/apple_eventkit_mcp/calendar_tools.py @@ -75,6 +75,7 @@ def calendar_list_events( start_date: str, end_date: str, calendar_name: Optional[str] = None, + exclude_calendars: Optional[list[str]] = None, limit: int = 50 ) -> dict: """List calendar events within a date range. @@ -83,6 +84,8 @@ def calendar_list_events( start_date: Start date in ISO 8601 format (YYYY-MM-DD or YYYY-MM-DDTHH:MM:SS) end_date: End date in ISO 8601 format calendar_name: Filter to specific calendar (optional) + exclude_calendars: Calendar titles to skip (optional, case-insensitive + exact match). Ignored when ``calendar_name`` is set. limit: Maximum events to return (default: 50) """ try: @@ -93,6 +96,7 @@ def calendar_list_events( start=start, end=end, calendar_name=calendar_name, + exclude_calendars=exclude_calendars, limit=limit ) @@ -160,6 +164,7 @@ def calendar_search_events( start_date: Optional[str] = None, end_date: Optional[str] = None, tags: Optional[list[str]] = None, + exclude_calendars: Optional[list[str]] = None, limit: int = 50 ) -> dict: """Search events by text in title, location, or notes. @@ -169,6 +174,8 @@ def calendar_search_events( start_date: Search range start (optional, defaults to 30 days ago) end_date: Search range end (optional, defaults to 90 days from now) tags: Filter by tags (optional) + exclude_calendars: Calendar titles to skip (optional, case-insensitive + exact match). limit: Maximum events to return (default: 50) """ try: @@ -184,6 +191,7 @@ def calendar_search_events( start=start, end=end, tags=tags, + exclude_calendars=exclude_calendars, limit=limit ) diff --git a/src/apple_eventkit_mcp/eventkit_store.py b/src/apple_eventkit_mcp/eventkit_store.py index 89510a9..758f8bd 100644 --- a/src/apple_eventkit_mcp/eventkit_store.py +++ b/src/apple_eventkit_mcp/eventkit_store.py @@ -65,9 +65,15 @@ def get_events( start: datetime, end: datetime, calendar_name: Optional[str] = None, + exclude_calendars: Optional[list[str]] = None, limit: int = 50 ) -> list[dict]: - """Fetch events in date range.""" + """Fetch events in date range. + + If ``exclude_calendars`` is provided, calendars whose title matches + any entry (case-insensitive exact match) are dropped before fetching. + Ignored when ``calendar_name`` is set (single-include wins). + """ require_calendar_permission() with self._lock: start_ns = self._datetime_to_nsdate(start) @@ -81,6 +87,9 @@ def get_events( self._store.calendarsForEntityType_(EventKit.EKEntityTypeEvent) or [] ) + if exclude_calendars: + excluded_lower = {n.lower() for n in exclude_calendars} + calendars = [c for c in calendars if c.title().lower() not in excluded_lower] if not calendars: return [] @@ -246,9 +255,15 @@ def search_events( start: Optional[datetime] = None, end: Optional[datetime] = None, tags: Optional[list[str]] = None, + exclude_calendars: Optional[list[str]] = None, limit: int = 50 ) -> list[dict]: - """Search events by text in title, location, or notes.""" + """Search events by text in title, location, or notes. + + ``exclude_calendars`` follows the same semantics as in + :meth:`get_events`: calendars whose title matches any entry + (case-insensitive exact match) are dropped before fetching. + """ require_calendar_permission() # Default to searching past 30 days to future 90 days @@ -257,7 +272,9 @@ def search_events( if end is None: end = datetime.now() + timedelta(days=90) - events = self.get_events(start, end, limit=1000) # Get more for filtering + events = self.get_events( + start, end, exclude_calendars=exclude_calendars, limit=1000 + ) # Get more for filtering query_lower = query.lower() results = []