Use configured contacts address book
All checks were successful
Build And Test / publish (push) Successful in 47s
All checks were successful
Build And Test / publish (push) Successful in 47s
This commit is contained in:
@@ -54,7 +54,7 @@ CALDAV_PASSWORD=your-caldav-password
|
|||||||
CARDDAV_URL=https://carddav.example.com/dav
|
CARDDAV_URL=https://carddav.example.com/dav
|
||||||
CARDDAV_USERNAME=user@example.com
|
CARDDAV_USERNAME=user@example.com
|
||||||
CARDDAV_PASSWORD=your-carddav-password
|
CARDDAV_PASSWORD=your-carddav-password
|
||||||
CONTACTS_ADDRESSBOOK_ID=/dav/addressbooks/users/user@example.com/contacts/
|
CONTACTS_ADDRESSBOOK_URL=https://carddav.example.com/dav/addressbooks/users/user@example.com/contacts/
|
||||||
|
|
||||||
# =============================================================================
|
# =============================================================================
|
||||||
# Cache Configuration
|
# Cache Configuration
|
||||||
|
|||||||
@@ -94,10 +94,10 @@ ICS_CALENDAR_TIMEOUT=20
|
|||||||
CARDDAV_URL=https://carddav.example.com/dav
|
CARDDAV_URL=https://carddav.example.com/dav
|
||||||
CARDDAV_USERNAME=you@example.com
|
CARDDAV_USERNAME=you@example.com
|
||||||
CARDDAV_PASSWORD=your-password
|
CARDDAV_PASSWORD=your-password
|
||||||
CONTACTS_ADDRESSBOOK_ID=/dav/addressbooks/users/you@example.com/contacts/
|
CONTACTS_ADDRESSBOOK_URL=https://carddav.example.com/dav/addressbooks/users/you@example.com/contacts/
|
||||||
```
|
```
|
||||||
|
|
||||||
Contacts tools always use `CONTACTS_ADDRESSBOOK_ID`. Listing address books is not exposed via MCP.
|
Contacts tools always use `CONTACTS_ADDRESSBOOK_URL` (full CardDAV address book URL). Listing address books is not exposed via MCP.
|
||||||
|
|
||||||
ICS calendars are optional and read-only. Set `ICS_CALENDARS` to a comma-separated list of entries, each as `name|url` or just `url` if you want the name inferred.
|
ICS calendars are optional and read-only. Set `ICS_CALENDARS` to a comma-separated list of entries, each as `name|url` or just `url` if you want the name inferred.
|
||||||
|
|
||||||
|
|||||||
@@ -40,9 +40,9 @@ class Settings(BaseSettings):
|
|||||||
carddav_url: Optional[str] = Field(default=None, alias="CARDDAV_URL")
|
carddav_url: Optional[str] = Field(default=None, alias="CARDDAV_URL")
|
||||||
carddav_username: Optional[str] = Field(default=None, alias="CARDDAV_USERNAME")
|
carddav_username: Optional[str] = Field(default=None, alias="CARDDAV_USERNAME")
|
||||||
carddav_password: Optional[SecretStr] = Field(default=None, alias="CARDDAV_PASSWORD")
|
carddav_password: Optional[SecretStr] = Field(default=None, alias="CARDDAV_PASSWORD")
|
||||||
contacts_addressbook_id: Optional[str] = Field(
|
contacts_addressbook_url: Optional[str] = Field(
|
||||||
default=None,
|
default=None,
|
||||||
alias="CONTACTS_ADDRESSBOOK_ID",
|
alias="CONTACTS_ADDRESSBOOK_URL",
|
||||||
)
|
)
|
||||||
|
|
||||||
# SQLite Cache
|
# SQLite Cache
|
||||||
@@ -154,7 +154,7 @@ class Settings(BaseSettings):
|
|||||||
self.carddav_url,
|
self.carddav_url,
|
||||||
self.carddav_username,
|
self.carddav_username,
|
||||||
self.carddav_password,
|
self.carddav_password,
|
||||||
self.contacts_addressbook_id,
|
self.contacts_addressbook_url,
|
||||||
])
|
])
|
||||||
|
|
||||||
def is_notification_configured(self) -> bool:
|
def is_notification_configured(self) -> bool:
|
||||||
|
|||||||
@@ -351,7 +351,6 @@ class ContactsService:
|
|||||||
def delete_contact(self, contact_id: str, addressbook_id: Optional[str] = None) -> OperationResult:
|
def delete_contact(self, contact_id: str, addressbook_id: Optional[str] = None) -> OperationResult:
|
||||||
try:
|
try:
|
||||||
client = self._get_client()
|
client = self._get_client()
|
||||||
addressbook_id = self._resolve_addressbook_id(addressbook_id)
|
|
||||||
|
|
||||||
# Build URL
|
# Build URL
|
||||||
contact_url = self._build_url(contact_id)
|
contact_url = self._build_url(contact_id)
|
||||||
@@ -375,7 +374,7 @@ class ContactsService:
|
|||||||
return OperationResult(success=False, message=str(e))
|
return OperationResult(success=False, message=str(e))
|
||||||
|
|
||||||
def _parse_vcard(
|
def _parse_vcard(
|
||||||
self, vcard_data: str, addressbook_id: str, href: str
|
self, vcard_data: str, addressbook_id: Optional[str], href: str
|
||||||
) -> Optional[Contact]:
|
) -> Optional[Contact]:
|
||||||
try:
|
try:
|
||||||
vcard = vobject.readOne(vcard_data)
|
vcard = vobject.readOne(vcard_data)
|
||||||
@@ -471,9 +470,10 @@ class ContactsService:
|
|||||||
except Exception:
|
except Exception:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
resolved_addressbook_id = addressbook_id or self._derive_addressbook_id(href)
|
||||||
return Contact(
|
return Contact(
|
||||||
id=href,
|
id=href,
|
||||||
addressbook_id=addressbook_id,
|
addressbook_id=resolved_addressbook_id,
|
||||||
first_name=first_name,
|
first_name=first_name,
|
||||||
last_name=last_name,
|
last_name=last_name,
|
||||||
display_name=display_name,
|
display_name=display_name,
|
||||||
@@ -486,9 +486,15 @@ class ContactsService:
|
|||||||
birthday=birthday,
|
birthday=birthday,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
def _derive_addressbook_id(self, contact_href: str) -> str:
|
||||||
|
if "/" not in contact_href:
|
||||||
|
return contact_href
|
||||||
|
base = contact_href.rsplit("/", 1)[0]
|
||||||
|
return f"{base}/"
|
||||||
|
|
||||||
def _resolve_addressbook_id(self, addressbook_id: Optional[str]) -> str:
|
def _resolve_addressbook_id(self, addressbook_id: Optional[str]) -> str:
|
||||||
if addressbook_id:
|
if addressbook_id:
|
||||||
return addressbook_id
|
return addressbook_id
|
||||||
if self.settings.contacts_addressbook_id:
|
if self.settings.contacts_addressbook_url:
|
||||||
return self.settings.contacts_addressbook_id
|
return self.settings.contacts_addressbook_url
|
||||||
raise ValueError("CONTACTS_ADDRESSBOOK_ID must be set to use contacts tools")
|
raise ValueError("CONTACTS_ADDRESSBOOK_URL must be set to use contacts tools")
|
||||||
|
|||||||
@@ -70,7 +70,6 @@ def register_contacts_tools(mcp: FastMCP, service: ContactsService):
|
|||||||
birthday: Birthday in ISO format (YYYY-MM-DD)
|
birthday: Birthday in ISO format (YYYY-MM-DD)
|
||||||
"""
|
"""
|
||||||
result = service.create_contact(
|
result = service.create_contact(
|
||||||
addressbook_id=None,
|
|
||||||
first_name=first_name,
|
first_name=first_name,
|
||||||
last_name=last_name,
|
last_name=last_name,
|
||||||
display_name=display_name,
|
display_name=display_name,
|
||||||
@@ -115,7 +114,6 @@ def register_contacts_tools(mcp: FastMCP, service: ContactsService):
|
|||||||
"""
|
"""
|
||||||
result = service.update_contact(
|
result = service.update_contact(
|
||||||
contact_id,
|
contact_id,
|
||||||
addressbook_id=None,
|
|
||||||
first_name=first_name,
|
first_name=first_name,
|
||||||
last_name=last_name,
|
last_name=last_name,
|
||||||
display_name=display_name,
|
display_name=display_name,
|
||||||
|
|||||||
@@ -181,15 +181,16 @@ def register_email_tools(mcp: FastMCP, service: EmailService):
|
|||||||
reply_all: Whether to include original recipients when replying (default: False)
|
reply_all: Whether to include original recipients when replying (default: False)
|
||||||
"""
|
"""
|
||||||
result = service.save_draft(
|
result = service.save_draft(
|
||||||
to,
|
to=to,
|
||||||
subject,
|
subject=subject,
|
||||||
body,
|
body=body,
|
||||||
cc,
|
cc=cc,
|
||||||
bcc,
|
bcc=bcc,
|
||||||
mailbox,
|
html_body=None,
|
||||||
in_reply_to_email_id,
|
mailbox=mailbox,
|
||||||
in_reply_to_mailbox,
|
in_reply_to_email_id=in_reply_to_email_id,
|
||||||
reply_all,
|
in_reply_to_mailbox=in_reply_to_mailbox,
|
||||||
|
reply_all=reply_all,
|
||||||
)
|
)
|
||||||
return result.model_dump()
|
return result.model_dump()
|
||||||
|
|
||||||
@@ -223,16 +224,17 @@ def register_email_tools(mcp: FastMCP, service: EmailService):
|
|||||||
reply_all: Whether to include original recipients when replying (default: False)
|
reply_all: Whether to include original recipients when replying (default: False)
|
||||||
"""
|
"""
|
||||||
result = service.update_draft(
|
result = service.update_draft(
|
||||||
email_id,
|
email_id=email_id,
|
||||||
mailbox,
|
mailbox=mailbox,
|
||||||
to,
|
to=to,
|
||||||
subject,
|
subject=subject,
|
||||||
body,
|
body=body,
|
||||||
cc,
|
cc=cc,
|
||||||
bcc,
|
bcc=bcc,
|
||||||
in_reply_to_email_id,
|
html_body=None,
|
||||||
in_reply_to_mailbox,
|
in_reply_to_email_id=in_reply_to_email_id,
|
||||||
reply_all,
|
in_reply_to_mailbox=in_reply_to_mailbox,
|
||||||
|
reply_all=reply_all,
|
||||||
)
|
)
|
||||||
return result.model_dump()
|
return result.model_dump()
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user