From 710666ee0a53fcd58379f31730121cca61663899 Mon Sep 17 00:00:00 2001 From: Yigit Colakoglu Date: Tue, 30 Dec 2025 17:39:53 -0800 Subject: [PATCH] Fix CardDAV URL construction for contacts MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Add _build_url helper to properly construct URLs from paths - Fix test.sh to pass addressbook_id when listing contacts - Prevents path duplication when addressbook_id is a full path 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 --- src/services/contacts_service.py | 22 ++++++++++++++-------- test.sh | 12 +++++++++--- 2 files changed, 23 insertions(+), 11 deletions(-) diff --git a/src/services/contacts_service.py b/src/services/contacts_service.py index 0f3f568..69b0cfd 100644 --- a/src/services/contacts_service.py +++ b/src/services/contacts_service.py @@ -1,4 +1,5 @@ from typing import Optional +from urllib.parse import urlparse import uuid import httpx @@ -51,6 +52,15 @@ class ContactsService: ) return self._client + def _build_url(self, path: str) -> str: + """Build full URL from a path, handling both relative and absolute paths.""" + if path.startswith("http://") or path.startswith("https://"): + return path + # Extract base URL (scheme + host) from carddav_url + parsed = urlparse(self.settings.carddav_url) + base = f"{parsed.scheme}://{parsed.netloc}" + return f"{base}{path}" + def list_addressbooks(self) -> list[AddressBook]: client = self._get_client() @@ -112,8 +122,7 @@ class ContactsService: client = self._get_client() # Build URL - base_url = self.settings.carddav_url.rstrip("/") - addressbook_url = f"{base_url}{addressbook_id}" if addressbook_id.startswith("/") else addressbook_id + addressbook_url = self._build_url(addressbook_id) response = client.request( "REPORT", @@ -183,8 +192,7 @@ class ContactsService: client = self._get_client() # Build URL - base_url = self.settings.carddav_url.rstrip("/") - contact_url = f"{base_url}{contact_id}" if contact_id.startswith("/") else contact_id + contact_url = self._build_url(contact_id) response = client.get(contact_url) @@ -275,8 +283,7 @@ class ContactsService: adr.type_param = addr_data.get("type", "home").upper() # Build URL and save - base_url = self.settings.carddav_url.rstrip("/") - addressbook_url = f"{base_url}{addressbook_id}" if addressbook_id.startswith("/") else addressbook_id + addressbook_url = self._build_url(addressbook_id) contact_url = f"{addressbook_url.rstrip('/')}/{uid}.vcf" response = client.put( @@ -343,8 +350,7 @@ class ContactsService: client = self._get_client() # Build URL - base_url = self.settings.carddav_url.rstrip("/") - contact_url = f"{base_url}{contact_id}" if contact_id.startswith("/") else contact_id + contact_url = self._build_url(contact_id) response = client.delete(contact_url) diff --git a/test.sh b/test.sh index 775194e..b12f919 100755 --- a/test.sh +++ b/test.sh @@ -74,10 +74,16 @@ echo -e "\n[6/7] Listing address books..." ADDRESSBOOKS=$(mcp_request 5 "tools/call" '{"name":"list_addressbooks","arguments":{}}') echo "$ADDRESSBOOKS" -# List contacts +# List contacts (using first addressbook from previous response) echo -e "\n[7/7] Listing contacts..." -CONTACTS=$(mcp_request 6 "tools/call" '{"name":"list_contacts","arguments":{"limit":10}}') -echo "$CONTACTS" +# Extract first addressbook ID from previous response +ADDRESSBOOK_ID=$(echo "$ADDRESSBOOKS" | grep -o '"id":"[^"]*"' | head -1 | cut -d'"' -f4) +if [ -n "$ADDRESSBOOK_ID" ]; then + CONTACTS=$(mcp_request 6 "tools/call" "{\"name\":\"list_contacts\",\"arguments\":{\"addressbook_id\":\"$ADDRESSBOOK_ID\",\"limit\":10}}") + echo "$CONTACTS" +else + echo "No addressbook found to list contacts from" +fi echo -e "\n================================" echo "All tests completed!"