Skip to main content
Glama
mixophrygian

Browser History Analysis MCP

by mixophrygian

get_browser_history

Retrieve raw browser history data from Firefox, Chrome, or Safari for analysis. Specify time period and browser type to extract browsing records.

Instructions

Step 2: Get raw browser history data without analysis. This is the fastest way to retrieve browser history and should be used before any analysis.

Args: time_period_in_days: Number of days of history to retrieve (default: 7) browser_type: Browser type ('firefox', 'chrome', 'safari', or None for auto-detect) all_browsers: If True, get history from all available browsers (default: True) Returns: Either a list of history entries or a dictionary with partial results and browser status

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
time_period_in_daysNo
browser_typeNo
all_browsersNo

Implementation Reference

  • server/main.py:40-52 (registration)
    Registers the 'get_browser_history' tool using @mcp.tool() decorator. Defines input schema via type hints and comprehensive docstring describing parameters and return types.
    @mcp.tool() async def get_browser_history(time_period_in_days: int = 7, browser_type: Optional[str] = None, all_browsers: bool = True) -> Union[List[HistoryEntryDict], Dict[str, Any]]: """Step 2: Get raw browser history data without analysis. This is the fastest way to retrieve browser history and should be used before any analysis. Args: time_period_in_days: Number of days of history to retrieve (default: 7) browser_type: Browser type ('firefox', 'chrome', 'safari', or None for auto-detect) all_browsers: If True, get history from all available browsers (default: True) Returns: Either a list of history entries or a dictionary with partial results and browser status """ return await tool_get_browser_history(time_period_in_days, CACHED_HISTORY, browser_type, all_browsers)
  • Core implementation of browser history retrieval. Detects available browsers (Firefox, Chrome, Safari), queries their SQLite databases for recent history, handles locked databases and errors, supports multi-browser aggregation and single-browser modes, returns structured results or errors with user guidance.
    async def tool_get_browser_history(time_period_in_days: int, CACHED_HISTORY: CachedHistory, browser_type: Optional[str] = None, all_browsers: bool = True) -> Union[List[HistoryEntryDict], BrowserHistoryResult]: start_time = time.time() print(f"🚀 Starting browser history retrieval for {time_period_in_days} days...") if time_period_in_days <= 0: raise ValueError("time_period_in_days must be a positive integer") # Map browser types to their handler functions browser_handlers = { "firefox": get_firefox_history, "chrome": get_chrome_history, "safari": get_safari_history } if all_browsers: # Step 1: Detect available browsers step_start = time.time() print("📊 Step 1: Detecting available browsers...") browser_status = tool_detect_available_browsers() detect_time = time.time() - step_start print(f"📊 Browser detection completed in {detect_time:.3f}s") if browser_status.get("status") == "error": print(f"❌ {browser_status['error_message']}") raise RuntimeError(browser_status['error_message']) elif browser_status.get("status") == "browser_locked": print(f"❌ {browser_status['error_message']}") raise RuntimeError(browser_status['error_message']) available_browsers = browser_status.get('available_browsers', []) if not available_browsers: print("❌ No available browsers found") raise RuntimeError("No browser history databases found. Please ensure Firefox, Chrome, or Safari is installed and try again.") print(f"📊 Available browsers: {available_browsers}") # Step 2: Get history from each browser all_entries = [] successful_browsers = [] failed_browsers = [] failure_reasons = {} for browser in available_browsers: browser_start = time.time() print(f"📊 Step 2.{len(successful_browsers) + len(failed_browsers) + 1}: Getting {browser} history...") try: entries = browser_handlers[browser](time_period_in_days) browser_time = time.time() - browser_start print(f"📊 {browser} history retrieved in {browser_time:.3f}s: {len(entries)} entries") logger.warning(f"Retrieved {len(entries)} {browser} history entries from last {time_period_in_days} days") all_entries.extend([entry.to_dict() for entry in entries]) successful_browsers.append(browser) except Exception as e: browser_time = time.time() - browser_start error_msg = str(e) print(f"❌ {browser} history failed in {browser_time:.3f}s: {error_msg}") logger.warning(f"Failed to get {browser} history: {error_msg}. If the database is locked, please try closing the browser and running the tool again.") failed_browsers.append(browser) failure_reasons[browser] = error_msg continue total_time = time.time() - start_time print(f"📊 Total browser history retrieval time: {total_time:.3f}s") # If we have any successful browsers, return partial results if successful_browsers: recommendation = "" if failed_browsers: locked_browsers = [browser for browser in failed_browsers if "database is locked" in failure_reasons.get(browser, "").lower()] if locked_browsers: recommendation = f"🔒 BROWSER LOCKED: {', '.join([b.title() for b in locked_browsers])} {'is' if len(locked_browsers) == 1 else 'are'} currently running. Please close {'this browser' if len(locked_browsers) == 1 else 'these browsers'} completely to get complete history analysis. You can restore tabs with Ctrl+Shift+T (Cmd+Shift+T on Mac). " recommendation += f"Successfully retrieved {len(all_entries)} entries from {', '.join([b.title() for b in successful_browsers])}." else: recommendation = f"✅ Successfully retrieved {len(all_entries)} entries from all browsers: {', '.join([b.title() for b in successful_browsers])}." logger.warning(f"Retrieved total of {len(all_entries)} history entries from {len(successful_browsers)} browsers") return { "history_entries": all_entries, "successful_browsers": successful_browsers, "failed_browsers": failed_browsers, "failure_reasons": failure_reasons, "total_entries": len(all_entries), "status": "partial_success" if failed_browsers else "success", "user_action_required": bool(failed_browsers), "recommendation": recommendation } # If no browsers succeeded, raise error with detailed information locked_browsers = [browser for browser in failed_browsers if "database is locked" in failure_reasons.get(browser, "").lower()] if locked_browsers: error_message = f"🔒 BROWSER LOCKED: All browsers ({', '.join([b.title() for b in locked_browsers])}) are currently running and their databases are locked. Please close ALL browsers completely to analyze history. You can restore tabs with Ctrl+Shift+T (Cmd+Shift+T on Mac)." else: error_details = "; ".join([f"{browser}: {reason}" for browser, reason in failure_reasons.items()]) error_message = f"❌ ERROR: Failed to retrieve history from any browser: {error_details}" raise RuntimeError(error_message) else: # Single browser mode (original behavior) if browser_type is None: browser_status = tool_detect_available_browsers() if browser_status.get("status") == "error": raise RuntimeError(browser_status['error_message']) elif browser_status.get("status") == "browser_locked": raise RuntimeError(browser_status['error_message']) # Get the first available browser from the available_browsers list available_browsers = browser_status.get('available_browsers', []) browser_type = available_browsers[0] if available_browsers else None if browser_type is None: raise RuntimeError("No browser history databases found. Please ensure Firefox, Chrome, or Safari is installed and try again.") logger.warning(f"Auto-detected active browser: {browser_type}") if browser_type not in browser_handlers: raise ValueError(f"Unsupported browser type: {browser_type}. Supported types: {list(browser_handlers.keys())}") try: entries = browser_handlers[browser_type](time_period_in_days) logger.warning(f"Retrieved {len(entries)} {browser_type} history entries from last {time_period_in_days} days") # Ensure we are always working with dictionaries entries_dict = [ensure_history_entry_dict(e) for e in entries] # Cache the history for later use CACHED_HISTORY.add_history(entries_dict, time_period_in_days, browser_type) return entries_dict except sqlite3.Error as e: logger.error(f"Error querying {browser_type} history: {e}") if "database is locked" in str(e).lower(): raise RuntimeError(f"🔒 BROWSER LOCKED: {browser_type.title()} is currently running and its database is locked. Please close {browser_type.title()} completely to analyze its history. You can restore tabs with Ctrl+Shift+T (Cmd+Shift+T on Mac).") else: raise RuntimeError(f"❌ ERROR: Failed to query {browser_type.title()} history: {e}") except Exception as e: logger.error(f"Unexpected error querying {browser_type} history: {e}") raise RuntimeError(f"❌ ERROR: Failed to query {browser_type.title()} history: {e}")
  • Helper utility to detect available browsers and their history database accessibility. Critical for the main handler to determine which browsers to query and detect locked/running browsers.
    def tool_detect_available_browsers() -> Dict[str, Any]: browsers_to_check = [] # Check Firefox if PATH_TO_FIREFOX_HISTORY: browsers_to_check.append(('firefox', PATH_TO_FIREFOX_HISTORY)) # Check Chrome if PATH_TO_CHROME_HISTORY: browsers_to_check.append(('chrome', PATH_TO_CHROME_HISTORY)) # Check Safari if PATH_TO_SAFARI_HISTORY: browsers_to_check.append(('safari', PATH_TO_SAFARI_HISTORY)) if not browsers_to_check: logger.warning("No browser history databases found") return { "available_browsers": [], "active_browsers": [], "status": "error", "error_message": "No browser history databases found. Please ensure Firefox, Chrome, or Safari is installed.", "user_action_required": False, "recommended_action": "Install Firefox, Chrome, or Safari to use this tool" } browsers_in_use = [] for browser_name, db_path in browsers_to_check: logger.warning(f"Checking {browser_name} database at {db_path}") try: # Try to connect with read-only mode conn = sqlite3.connect(f"file:{db_path}?mode=ro", uri=True) # Test if we can actually query the database cursor = conn.cursor() cursor.execute("SELECT COUNT(*) FROM sqlite_master WHERE type='table';") table_count = cursor.fetchone()[0] conn.close() logger.warning(f"Successfully connected to {browser_name} database and queried {table_count} tables. Browser may still be running, encourage the user to close this browser to make history available.") except sqlite3.OperationalError as e: if "database is locked" in str(e).lower(): logger.warning(f"Database locked for {browser_name} - browser is likely open and active - prompt the user to close it to get complete history.") # Short-circuit: return immediately if any browser is locked return { "available_browsers": [browser[0] for browser in browsers_to_check], "active_browsers": [browser_name], "status": "browser_locked", "error_message": f"🔒 BROWSER LOCKED: {browser_name.title()} is currently running and its database is locked.", "user_action_required": True, "recommended_action": f"❗ IMPORTANT: Please close all browsers, especially {browser_name.title()} completely to analyze its history. You can restore your tabs later with Ctrl+Shift+T (Cmd+Shift+T on Mac).", "technical_details": f"Database error: {str(e)}" } else: logger.warning(f"Error connecting to {browser_name} database: {e} - please inform the user that this browser is not available for analysis.") except Exception as e: logger.warning(f"Unexpected error connecting to {browser_name} database: {e}") # Short-circuit: return immediately if any browser has an error return { "available_browsers": [browser[0] for browser in browsers_to_check], "active_browsers": [browser_name], "status": "error", "error_message": f"❌ ERROR: Cannot access {browser_name.title()} database.", "user_action_required": True, "recommended_action": f"Please close all browsers, especially {browser_name.title()} completely and try again. You can restore your tabs later with Ctrl+Shift+T (Cmd+Shift+T on Mac).", "technical_details": f"Technical error: {str(e)}" } # If we get here, no browsers are locked available_browsers = [browser[0] for browser in browsers_to_check] logger.warning(f"No active browser detected, available browsers: {available_browsers}") return { "available_browsers": available_browsers, "active_browsers": [], "status": "ready", "error_message": None, "user_action_required": False, "recommended_action": f"✅ All browsers are available for analysis. Found: {', '.join(available_browsers)}" }
  • Firefox-specific history extractor: connects to places.sqlite, queries moz_places table, converts timestamps, filters irrelevant entries.
    def get_firefox_history(days: int) -> List[HistoryEntry]: """Get Firefox history from the last N days""" firefox_start = time.time() print(f"📊 Firefox: Starting history retrieval for {days} days...") # Check if database exists if not os.path.exists(PATH_TO_FIREFOX_HISTORY): raise RuntimeError(f"Firefox history not found at {PATH_TO_FIREFOX_HISTORY}") # Connect to the database print(f"📊 Firefox: Connecting to database...") conn = sqlite3.connect(f"file:{PATH_TO_FIREFOX_HISTORY}?mode=ro", uri=True) try: cursor = conn.cursor() # Firefox stores timestamps as microseconds since Unix epoch cutoff_time = (datetime.now() - timedelta(days=days)).timestamp() * 1_000_000 query = """ SELECT DISTINCT h.url, h.title, h.visit_count, h.last_visit_date FROM moz_places h WHERE h.last_visit_date > ? AND h.hidden = 0 AND h.url NOT LIKE 'moz-extension://%' ORDER BY h.last_visit_date DESC """ cursor.execute(query, (cutoff_time,)) results = cursor.fetchall() entries = [] for url, title, visit_count, last_visit_date in results: # Convert Firefox timestamp (microseconds) to datetime visit_time = datetime.fromtimestamp(last_visit_date / 1_000_000) entries.append(HistoryEntry( url=url or "", title=title, visit_count=visit_count or 0, last_visit_time=visit_time )) firefox_time = time.time() - firefox_start print(f"📊 Firefox: History retrieval completed in {firefox_time:.3f}s: {len(entries)} entries") return entries except Exception as e: firefox_time = time.time() - firefox_start print(f"❌ Firefox: History retrieval failed in {firefox_time:.3f}s: {e}") logger.error(f"Error querying Firefox history: {e}") raise RuntimeError(f"Failed to query Firefox history: {e}") finally: if conn: conn.close()

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/mixophrygian/browser_history_mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server