tool_check_addressbook
Diagnose AddressBook access issues on macOS to resolve problems with the Messages app functionality.
Instructions
Diagnose AddressBook access issues.
Input Schema
TableJSON Schema
| Name | Required | Description | Default |
|---|---|---|---|
No arguments | |||
Implementation Reference
- mac_messages_mcp/server.py:148-158 (handler)The handler function for tool_check_addressbook. It is decorated with @mcp.tool() for registration and calls check_addressbook_access() to diagnose AddressBook access.@mcp.tool() def tool_check_addressbook(ctx: Context) -> str: """ Diagnose AddressBook access issues. """ logger.info("Checking AddressBook access") try: return check_addressbook_access() except Exception as e: logger.error(f"Error checking AddressBook: {str(e)}") return f"Error checking AddressBook: {str(e)}"
- Core helper function that performs detailed checks on AddressBook database accessibility, file permissions, table presence, and retrieves sample contacts.def check_addressbook_access() -> str: """Check if the AddressBook database is accessible and return detailed information.""" try: home_dir = os.path.expanduser("~") sources_path = os.path.join(home_dir, "Library/Application Support/AddressBook/Sources") status = [] # Check if the directory exists if not os.path.exists(sources_path): return f"ERROR: AddressBook Sources directory not found at {sources_path} PLEASE TELL THE USER TO GRANT FULL DISK ACCESS TO THE TERMINAL APPLICATION(CURSOR, TERMINAL, CLAUDE, ETC.) AND RESTART THE APPLICATION. DO NOT RETRY UNTIL NEXT MESSAGE." status.append(f"AddressBook Sources directory exists at: {sources_path}") # Find database files db_paths = glob.glob(os.path.join(sources_path, "*/AddressBook-v22.abcddb")) if not db_paths: return f"ERROR: No AddressBook database files found in {sources_path} PLEASE TELL THE USER TO GRANT FULL DISK ACCESS TO THE TERMINAL APPLICATION(CURSOR, TERMINAL, CLAUDE, ETC.) AND RESTART THE APPLICATION. DO NOT RETRY UNTIL NEXT MESSAGE." status.append(f"Found {len(db_paths)} AddressBook database files:") for path in db_paths: status.append(f" - {path}") # Check file permissions for each database for db_path in db_paths: try: with open(db_path, 'rb') as f: # Just try to read a byte to confirm access f.read(1) status.append(f"File is readable: {db_path}") except PermissionError: status.append(f"ERROR: Permission denied when trying to read {db_path} PLEASE TELL THE USER TO GRANT FULL DISK ACCESS TO THE TERMINAL APPLICATION(CURSOR, TERMINAL, CLAUDE, ETC.) AND RESTART THE APPLICATION. DO NOT RETRY UNTIL NEXT MESSAGE.") continue except Exception as e: status.append(f"ERROR: Unknown error reading file {db_path}: {str(e)} PLEASE TELL THE USER TO GRANT FULL DISK ACCESS TO THE TERMINAL APPLICATION(CURSOR, TERMINAL, CLAUDE, ETC.) AND RESTART THE APPLICATION. DO NOT RETRY UNTIL NEXT MESSAGE.") continue # Try to connect to the database try: conn = sqlite3.connect(db_path) status.append(f"Successfully connected to database: {db_path}") # Test a simple query cursor = conn.cursor() cursor.execute("SELECT count(*) FROM sqlite_master") count = cursor.fetchone()[0] status.append(f"Database contains {count} tables") # Check if the necessary tables exist cursor.execute("SELECT name FROM sqlite_master WHERE type='table' AND name IN ('ZABCDRECORD', 'ZABCDPHONENUMBER')") tables = [row[0] for row in cursor.fetchall()] if 'ZABCDRECORD' in tables and 'ZABCDPHONENUMBER' in tables: status.append("Required tables (ZABCDRECORD, ZABCDPHONENUMBER) are present") else: status.append(f"WARNING: Some required tables are missing. Found: {', '.join(tables)}") # Get a count of contacts try: cursor.execute("SELECT COUNT(*) FROM ZABCDRECORD") contact_count = cursor.fetchone()[0] status.append(f"Database contains {contact_count} contacts") except sqlite3.OperationalError: status.append("Could not query contact count PLEASE TELL THE USER TO GRANT FULL DISK ACCESS TO THE TERMINAL APPLICATION(CURSOR, TERMINAL, CLAUDE, ETC.) AND RESTART THE APPLICATION. DO NOT RETRY UNTIL NEXT MESSAGE.") conn.close() except sqlite3.OperationalError as e: status.append(f"ERROR: Database connection error for {db_path}: {str(e)} PLEASE TELL THE USER TO GRANT FULL DISK ACCESS TO THE TERMINAL APPLICATION(CURSOR, TERMINAL, CLAUDE, ETC.) AND RESTART THE APPLICATION. DO NOT RETRY UNTIL NEXT MESSAGE.") # Try to get actual contacts contacts = get_addressbook_contacts() if contacts: status.append(f"Successfully retrieved {len(contacts)} contacts with phone numbers") else: status.append("WARNING: No contacts with phone numbers found. PLEASE TELL THE USER TO GRANT FULL DISK ACCESS TO THE TERMINAL APPLICATION(CURSOR, TERMINAL, CLAUDE, ETC.) AND RESTART THE APPLICATION. DO NOT RETRY UNTIL NEXT MESSAGE.") return "\n".join(status) except Exception as e: return f"ERROR: Unexpected error during database access check: {str(e)} PLEASE TELL THE USER TO GRANT FULL DISK ACCESS TO THE TERMINAL APPLICATION(CURSOR, TERMINAL, CLAUDE, ETC.) AND RESTART THE APPLICATION. DO NOT RETRY UNTIL NEXT MESSAGE."