fix: address PR #574 second review round
- Enrich single-calendar event dicts with calendar_name before mapping to CalendarEventSummary (list_events and upcoming_events paths) - Extract _raw_contact_to_model() from inline mapping in contacts.py, fix custom_fields type annotation to dict[str, Any] - Add unit tests for _event_dict_to_summary covering categories parsing, falsy coercion, and calendar name passthrough - Replace duplicated test helper with import of production function Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -240,6 +240,10 @@ def configure_calendar_tools(mcp: FastMCP):
|
||||
limit=limit,
|
||||
)
|
||||
|
||||
# Enrich events with calendar context for per-event mapping
|
||||
for event in events:
|
||||
event["calendar_name"] = calendar_name
|
||||
|
||||
# Apply filters if provided
|
||||
if filters:
|
||||
events = client.calendar._apply_event_filters(events, filters)
|
||||
@@ -459,6 +463,8 @@ def configure_calendar_tools(mcp: FastMCP):
|
||||
end_datetime=end_datetime,
|
||||
limit=limit,
|
||||
)
|
||||
for event in events:
|
||||
event["calendar_name"] = calendar_name
|
||||
else:
|
||||
# Get events from all calendars
|
||||
all_calendars = await client.calendar.list_calendars()
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import logging
|
||||
from typing import Any
|
||||
|
||||
from mcp.server.fastmcp import Context, FastMCP
|
||||
from mcp.types import ToolAnnotations
|
||||
@@ -17,6 +18,34 @@ from nextcloud_mcp_server.observability.metrics import instrument_tool
|
||||
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."""
|
||||
contact_info = raw.get("contact", {})
|
||||
|
||||
# Convert email field (str, list, or None) to list[ContactField]
|
||||
raw_email = contact_info.get("email")
|
||||
emails: list[ContactField] = []
|
||||
if isinstance(raw_email, list):
|
||||
emails = [ContactField(type="email", value=e) for e in raw_email if e]
|
||||
elif isinstance(raw_email, str) and raw_email:
|
||||
emails = [ContactField(type="email", value=raw_email)]
|
||||
|
||||
# Nickname goes into custom_fields (no dedicated model field)
|
||||
custom_fields: dict[str, Any] = {}
|
||||
nickname = contact_info.get("nickname")
|
||||
if nickname:
|
||||
custom_fields["nickname"] = nickname
|
||||
|
||||
return Contact(
|
||||
uid=raw["vcard_id"],
|
||||
fn=contact_info.get("fullname", ""),
|
||||
etag=raw.get("getetag"),
|
||||
birthday=contact_info.get("birthday"),
|
||||
emails=emails,
|
||||
custom_fields=custom_fields,
|
||||
)
|
||||
|
||||
|
||||
def configure_contacts_tools(mcp: FastMCP):
|
||||
# Contacts tools
|
||||
@mcp.tool(
|
||||
@@ -53,34 +82,7 @@ def configure_contacts_tools(mcp: FastMCP):
|
||||
"""List all contacts in the specified addressbook."""
|
||||
client = await get_client(ctx)
|
||||
contacts_data = await client.contacts.list_contacts(addressbook=addressbook)
|
||||
contacts = []
|
||||
for c in contacts_data:
|
||||
contact_info = c.get("contact", {})
|
||||
|
||||
# Convert email field (str, list, or None) to list[ContactField]
|
||||
raw_email = contact_info.get("email")
|
||||
emails: list[ContactField] = []
|
||||
if isinstance(raw_email, list):
|
||||
emails = [ContactField(type="email", value=e) for e in raw_email if e]
|
||||
elif isinstance(raw_email, str) and raw_email:
|
||||
emails = [ContactField(type="email", value=raw_email)]
|
||||
|
||||
# Nickname goes into custom_fields (no dedicated model field)
|
||||
custom_fields: dict[str, str] = {}
|
||||
nickname = contact_info.get("nickname")
|
||||
if nickname:
|
||||
custom_fields["nickname"] = nickname
|
||||
|
||||
contacts.append(
|
||||
Contact(
|
||||
uid=c["vcard_id"],
|
||||
fn=contact_info.get("fullname", ""),
|
||||
etag=c.get("getetag"),
|
||||
birthday=contact_info.get("birthday"),
|
||||
emails=emails,
|
||||
custom_fields=custom_fields,
|
||||
)
|
||||
)
|
||||
contacts = [_raw_contact_to_model(c) for c in contacts_data]
|
||||
return ListContactsResponse(
|
||||
contacts=contacts, addressbook=addressbook, total_count=len(contacts)
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user