diff --git a/nextcloud_mcp_server/server/calendar.py b/nextcloud_mcp_server/server/calendar.py index 8c7f939..0761a72 100644 --- a/nextcloud_mcp_server/server/calendar.py +++ b/nextcloud_mcp_server/server/calendar.py @@ -29,10 +29,14 @@ def _event_dict_to_summary(event: dict) -> CalendarEventSummary: else: categories = raw_categories + start = event.get("start_datetime", "") + if not start: + logger.debug("Event %s has no start_datetime", event.get("uid", "unknown")) + return CalendarEventSummary( uid=event.get("uid", ""), summary=event.get("title", ""), - start=event.get("start_datetime", ""), + start=start, end=event.get("end_datetime"), all_day=event.get("all_day", False), location=event.get("location") or None, @@ -240,7 +244,10 @@ def configure_calendar_tools(mcp: FastMCP): limit=limit, ) - # Enrich events with calendar context for per-event mapping + # Enrich events with calendar context for per-event mapping. + # Note: calendar_display_name is not available here without an + # extra list_calendars() call; the response-level calendar_name + # already identifies the calendar for single-calendar queries. for event in events: event["calendar_name"] = calendar_name @@ -463,6 +470,7 @@ def configure_calendar_tools(mcp: FastMCP): end_datetime=end_datetime, limit=limit, ) + # calendar_display_name not available without extra API call for event in events: event["calendar_name"] = calendar_name else: diff --git a/nextcloud_mcp_server/server/contacts.py b/nextcloud_mcp_server/server/contacts.py index dc44956..ca64af7 100644 --- a/nextcloud_mcp_server/server/contacts.py +++ b/nextcloud_mcp_server/server/contacts.py @@ -19,7 +19,13 @@ logger = logging.getLogger(__name__) def _raw_contact_to_model(raw: dict) -> Contact: - """Convert a raw contact dict from the contacts client to a Contact model.""" + """Convert a raw contact dict from the contacts client to a Contact model. + + Only maps fields the client's list_contacts() currently returns: + fullname, nickname, birthday, and email. Additional Contact model fields + (phones, addresses, organization, etc.) require expanding the client's + vCard parsing in ContactsClient.list_contacts(). + """ contact_info = raw.get("contact", {}) # Convert email field (str, list, or None) to list[ContactField] diff --git a/nextcloud_mcp_server/server/deck.py b/nextcloud_mcp_server/server/deck.py index ee6f499..f48e134 100644 --- a/nextcloud_mcp_server/server/deck.py +++ b/nextcloud_mcp_server/server/deck.py @@ -107,7 +107,7 @@ def configure_deck_tools(mcp: FastMCP): ) client = await get_client(ctx) board = await client.deck.get_board(board_id) - return [label.model_dump() for label in board.labels] + return [label.model_dump() for label in (board.labels or [])] @mcp.resource("nc://Deck/boards/{board_id}/labels/{label_id}") async def deck_label_resource(board_id: int, label_id: int): @@ -209,7 +209,8 @@ def configure_deck_tools(mcp: FastMCP): """Get all labels in a Nextcloud Deck board""" client = await get_client(ctx) board = await client.deck.get_board(board_id) - return ListLabelsResponse(labels=board.labels, total=len(board.labels)) + labels = board.labels or [] + return ListLabelsResponse(labels=labels, total=len(labels)) @mcp.tool( title="Get Deck Label",