"""Exceptions for My iMessage MCP server."""
class ServerError(Exception):
"""Base exception for server errors.
Attributes:
message: Human-readable error description
is_retryable: Whether the error might succeed on retry
"""
def __init__(
self,
message: str,
is_retryable: bool = False,
) -> None:
super().__init__(message)
self.message = message
self.is_retryable = is_retryable
def __str__(self) -> str:
return self.message
class NotInitializedError(Exception):
"""Raised when the server is not properly initialized."""
pass
# iMessage-specific exceptions
class PermissionDeniedError(ServerError):
"""Raised when Full Disk Access is not granted.
The application needs Full Disk Access to read ~/Library/Messages/chat.db.
Grant FDA to Terminal.app or Claude Desktop.app in System Settings.
"""
def __init__(self, message: str = "Full Disk Access not granted") -> None:
super().__init__(message, is_retryable=False)
class DatabaseLockedError(ServerError):
"""Raised when chat.db is locked by another process (usually Messages.app).
This is typically a transient condition that resolves quickly.
"""
def __init__(self, message: str = "Database is locked by another process") -> None:
super().__init__(message, is_retryable=True)
class ContactsPermissionError(ServerError):
"""Raised when access to Contacts database is denied.
The application needs appropriate permissions to read the Contacts database.
This is distinct from Full Disk Access and requires Contacts access permission.
"""
def __init__(self, message: str = "Contacts access not granted") -> None:
super().__init__(message, is_retryable=False)
class AppleScriptError(ServerError):
"""Base exception for AppleScript-related errors."""
def __init__(self, message: str, is_retryable: bool = False) -> None:
super().__init__(message, is_retryable=is_retryable)
class MessagesNotRunningError(AppleScriptError):
"""Raised when Messages.app is not running.
AppleScript requires Messages.app to be open to send messages.
"""
def __init__(self, message: str = "Messages.app is not running") -> None:
super().__init__(message, is_retryable=True)
class AutomationDeniedError(AppleScriptError):
"""Raised when Automation permission for Messages.app is denied.
The user must approve the Automation permission prompt, or grant it in
System Settings > Privacy & Security > Automation.
"""
def __init__(
self, message: str = "Automation permission denied for Messages.app"
) -> None:
super().__init__(message, is_retryable=False)
# Search-specific exceptions
class SearchIndexError(ServerError):
"""Base exception for search index errors."""
def __init__(self, message: str, is_retryable: bool = False) -> None:
super().__init__(message, is_retryable=is_retryable)
class EmbeddingError(SearchIndexError):
"""Error during embedding generation.
This can occur when:
- OpenAI API is unavailable or returns an error
- Rate limiting is exceeded
- Network connectivity issues occur
- Invalid API key is provided
"""
def __init__(self, message: str, is_retryable: bool = True) -> None:
super().__init__(message, is_retryable=is_retryable)
class SyncError(SearchIndexError):
"""Error during search index synchronization.
This can occur when:
- Database read operations fail
- Message parsing fails
- Sync metadata updates fail
"""
def __init__(self, message: str, is_retryable: bool = True) -> None:
super().__init__(message, is_retryable=is_retryable)