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