# ============================================================================
# CHANGELOG (recent first, max 5 entries)
# 01/03/2026 - Enhanced database error messages with FDA detection (Claude)
# 01/03/2026 - Created for MCP server refactoring (Claude)
# ============================================================================
"""
Error handling utilities for MCP tool handlers.
Provides standardized error handling for common error scenarios,
particularly for RAG and external dependencies.
"""
import logging
from mcp import types
logger = logging.getLogger(__name__)
# Common error patterns for permission issues
PERMISSION_ERROR_PATTERNS = [
"unable to open database",
"permission denied",
"no such file or directory",
"operation not permitted",
"access denied",
"authorization not granted",
]
FULL_DISK_ACCESS_HELP = """
š To grant Full Disk Access:
1. Open System Settings (or System Preferences on older macOS)
2. Go to Privacy & Security ā Full Disk Access
3. Click the lock to make changes
4. Add Terminal (or your IDE) to the list
5. Toggle it ON
6. Restart Terminal/Claude Code
The Messages database is protected by macOS privacy controls.
Claude needs Full Disk Access to read your iMessage history.
"""
def handle_rag_error(
e: Exception,
operation: str = ""
) -> list[types.TextContent]:
"""
Handle RAG-specific errors with helpful messages.
Args:
e: The exception that was raised
operation: Description of what operation was being performed
Returns:
Formatted error response with helpful troubleshooting info
"""
if isinstance(e, ImportError):
return [types.TextContent(
type="text",
text=(
"RAG dependencies not installed.\n\n"
"Run: pip install chromadb openai\n\n"
f"Error: {e}"
)
)]
elif isinstance(e, ValueError):
return [types.TextContent(
type="text",
text=(
f"Configuration error: {e}\n\n"
"Make sure OPENAI_API_KEY environment variable is set "
"if using OpenAI embeddings, or configure local embeddings."
)
)]
else:
error_msg = f"Error {operation}: {e}" if operation else f"Error: {e}"
logger.error(error_msg, exc_info=True)
return [types.TextContent(
type="text",
text=error_msg
)]
def is_permission_error(error: Exception) -> bool:
"""
Check if an error is likely a permission/access error.
Args:
error: The exception to check
Returns:
True if this looks like a permission error
"""
error_str = str(error).lower()
return any(pattern in error_str for pattern in PERMISSION_ERROR_PATTERNS)
def handle_database_error(
e: Exception,
operation: str = ""
) -> list[types.TextContent]:
"""
Handle database-related errors with smart detection.
Detects permission errors vs other database errors and provides
actionable troubleshooting steps.
Args:
e: The exception that was raised
operation: Description of what operation was being performed
Returns:
Formatted error response with specific troubleshooting info
"""
error_msg = f"Database error"
if operation:
error_msg += f" during {operation}"
error_msg += f": {e}"
logger.error(error_msg, exc_info=True)
# Check if this is a permission error (most common issue)
if is_permission_error(e):
return [types.TextContent(
type="text",
text=(
f"ā Cannot access Messages database\n\n"
f"Error: {e}\n"
f"{FULL_DISK_ACCESS_HELP}"
)
)]
# Check for database locked error
error_str = str(e).lower()
if "locked" in error_str or "busy" in error_str:
return [types.TextContent(
type="text",
text=(
f"ā³ Database is locked\n\n"
f"Error: {e}\n\n"
"The Messages database may be in use by another process.\n"
"Try:\n"
"1. Close Messages.app (Cmd+Q)\n"
"2. Wait a few seconds and try again\n"
"3. If using Time Machine or iCloud sync, wait for it to complete"
)
)]
# Generic database error
return [types.TextContent(
type="text",
text=(
f"{error_msg}\n\n"
"Possible causes:\n"
"⢠Full Disk Access permission not granted (most common)\n"
"⢠Messages database is locked by another process\n"
"⢠Database file is corrupted\n"
"⢠macOS privacy restrictions\n\n"
"Try checking Full Disk Access permissions first."
)
)]
def handle_applescript_error(
e: Exception,
operation: str = ""
) -> list[types.TextContent]:
"""
Handle AppleScript-related errors.
Args:
e: The exception that was raised
operation: Description of what operation was being performed
Returns:
Formatted error response with troubleshooting steps
"""
error_msg = f"AppleScript error"
if operation:
error_msg += f" during {operation}"
error_msg += f": {e}"
logger.error(error_msg, exc_info=True)
return [types.TextContent(
type="text",
text=(
f"{error_msg}\n\n"
"Troubleshooting:\n"
"- Ensure Messages.app is running\n"
"- Check Automation permissions in System Settings ā Privacy & Security\n"
"- Verify the phone number or handle is correct"
)
)]