Skip to main content

MCP server for secure local browser history access

Project description

ChronicleMCP

Secure, local-first Model Context Protocol (MCP) server for browser history

PyPI Version License Tests Coverage


Table of Contents

  1. Quick Start
  2. Features
  3. Installation
  4. Usage
  5. Configuration
  6. Security & Privacy
  7. Supported Browsers
  8. Troubleshooting
  9. Development
  10. Contributing
  11. License

Quick Start

# Install
pip install chronicle-mcp

# Run MCP server (stdio mode for AI agents like Claude, Cursor)
chronicle-mcp mcp

# Or start an HTTP server
chronicle-mcp http --port 8080

# Check available browsers
chronicle-mcp list-browsers

Features

Feature Description
🔒 Privacy-First All data stays on your machine. No cloud sync, no data collection.
🌐 Multi-Browser Chrome, Firefox, Edge, Brave, Safari, Vivaldi, Opera support
🔍 Multiple Search Tools Search by query, date range, domain, or recent history
📊 Output Formats Markdown (default) or JSON
Fast Performance Built with Python and SQLite
🔐 Secure URL sanitization removes sensitive query parameters
🐳 Docker Support Run as a container
🔧 CLI Interface Full command-line control
🌐 HTTP API RESTful API for integrations
🔖 Bookmarks Read bookmarks from all supported browsers
⬇️ Downloads Track download history from all supported browsers

Installation

pip (Recommended)

pip install chronicle-mcp

pipx (Isolated Installation)

pipx install chronicle-mcp

Docker

# Pull the latest image
docker pull ghcr.io/nikolasil/chronicle-mcp:latest

# Run the server
docker run -p 8080:8080 ghcr.io/nikolasil/chronicle-mcp

From Source

git clone https://github.com/nikolasil/chronicle-mcp.git
cd chronicle-mcp
pip install -e .

Homebrew (macOS)

brew tap nikolasil/chronicle-mcp
brew install chronicle-mcp

Usage

CLI Commands

chronicle-mcp mcp

Run the MCP server for AI agents.

# Stdio mode (default for AI assistants)
chronicle-mcp mcp

# SSE mode for HTTP clients
chronicle-mcp mcp --sse --port 8080
Option Description
--sse Use SSE transport instead of stdio
--host Host to bind (SSE mode only)
--port Port to listen on (SSE mode only)

chronicle-mcp http

Start a long-running HTTP REST API server.

# Default (foreground, port 8080)
chronicle-mcp http

# Custom port
chronicle-mcp http --port 9000

# Daemon mode
chronicle-mcp http --port 8080 --daemon

# Different browser
chronicle-mcp http --browser firefox
Option Description
--host Host to bind (default: 127.0.0.1)
--port Port to listen on (default: 8080)
--browser Default browser (default: chrome)
--foreground Run in foreground (default: true)
--daemon Run as daemon

chronicle-mcp status

Check if the server is running.

chronicle-mcp status --port 8080

chronicle-mcp logs

View server logs.

chronicle-mcp logs --port 8080 --lines 100

chronicle-mcp version

Show version information.

chronicle-mcp version

chronicle-mcp list-browsers

List available browsers on the system.

chronicle-mcp list-browsers
# Output: Available browsers: chrome, edge

chronicle-mcp completion

Generate shell completion scripts.

# Bash
chronicle-mcp completion bash >> ~/.bashrc

# Zsh
chronicle-mcp completion zsh >> ~/.zshrc

# Fish
chronicle-mcp completion fish > ~/.config/fish/completions/chronicle-mcp.fish

MCP Tools

search_history

Search browser history for keywords in titles or URLs.

def search_history(
    query: str,
    limit: int = 5,
    browser: str = "chrome",
    format_type: str = "markdown"
) -> str

Example:

# Markdown output (default)
search_history("python tutorial", limit=10, browser="chrome")

# JSON output
search_history("github", limit=5, format_type="json")

get_recent_history

Get recent browsing history from the last N hours.

def get_recent_history(
    hours: int = 24,
    limit: int = 20,
    browser: str = "chrome",
    format_type: str = "markdown"
) -> str

Example:

# Last 24 hours
get_recent_history(hours=48, limit=20)

count_visits

Count total visits to a specific domain.

def count_visits(
    domain: str,
    browser: str = "chrome"
) -> str

Example:

count_visits("github.com", browser="chrome")
# Output: Visits to 'github.com' in chrome: 42

list_top_domains

Get the most visited domains.

def list_top_domains(
    limit: int = 10,
    browser: str = "chrome",
    format_type: str = "markdown"
) -> str

Example:

list_top_domains(limit=20)

search_history_by_date

Search history within a date range.

def search_history_by_date(
    query: str,
    start_date: str,  # ISO format: YYYY-MM-DD
    end_date: str,    # ISO format: YYYY-MM-DD
    limit: int = 10,
    browser: str = "chrome",
    format_type: str = "markdown"
) -> str

Example:

search_history_by_date(
    "python",
    start_date="2024-01-01",
    end_date="2024-12-31",
    limit=20
)

list_available_browsers

Returns a list of browsers with detected history databases.

def list_available_browsers() -> str

Example:

list_available_browsers()
# Output: Available browsers: chrome, edge, firefox, brave

delete_history

Delete history entries matching a query.

def delete_history(
    query: str,
    limit: int = 100,
    browser: str = "chrome",
    confirm: bool = False
) -> str

Example:

# Preview what would be deleted (default)
delete_history("spam.com", browser="chrome")
# Actually delete
delete_history("spam.com", browser="chrome", confirm=True)

search_by_domain

Search history within specific domain(s).

def search_by_domain(
    domain: str,
    query: str = None,
    limit: int = 20,
    browser: str = "chrome",
    format_type: str = "markdown",
    exclude_domains: list[str] = None
) -> str

Example:

search_by_domain("github.com", browser="chrome")

get_browser_stats

Get browsing statistics for the browser database.

def get_browser_stats(browser: str = "chrome") -> str

Example:

get_browser_stats(browser="firefox")
# Returns JSON with total visits, domains, date range, etc.

get_most_visited_pages

Get the most visited individual pages.

def get_most_visited_pages(
    limit: int = 20,
    browser: str = "chrome",
    format_type: str = "markdown"
) -> str

Example:

get_most_visited_pages(limit=10, browser="chrome")

export_history

Export history to CSV or JSON format.

def export_history(
    format_type: str = "csv",
    limit: int = 1000,
    query: str = None,
    browser: str = "chrome"
) -> str

Example:

# Export to CSV
export_history(format_type="csv", limit=1000, browser="chrome")

# Export to JSON
export_history(format_type="json", limit=500, browser="firefox")

search_history_advanced

Advanced search with multiple options including regex and fuzzy matching.

def search_history_advanced(
    query: str,
    limit: int = 20,
    browser: str = "chrome",
    format_type: str = "markdown",
    exclude_domains: list[str] = None,
    sort_by: str = "date",
    use_regex: bool = False,
    use_fuzzy: bool = False,
    fuzzy_threshold: float = 0.6
) -> str

Example:

# Fuzzy search
search_history_advanced("pythn tutorial", use_fuzzy=True, fuzzy_threshold=0.7)

# Regex search
search_history_advanced(r"github\.com/.*/issues/\d+", use_regex=True)

sync_history

Sync history between browsers.

def sync_history(
    source_browser: str,
    target_browser: str,
    merge_strategy: str = "latest",
    dry_run: bool = True
) -> str

Example:

# Preview sync
sync_history("chrome", "firefox", dry_run=True)

# Actually sync
sync_history("chrome", "firefox", dry_run=False)

list_available_bookmarks

Returns a list of browsers with detected bookmarks.

def list_available_bookmarks() -> str

Example:

list_available_bookmarks()
# Output: Available browsers with bookmarks: chrome, edge, firefox

list_available_downloads

Returns a list of browsers with detected downloads history.

def list_available_downloads() -> str

Example:

list_available_downloads()
# Output: Available browsers with downloads: chrome, edge, firefox

get_bookmarks

Get bookmarks from a browser.

def get_bookmarks(
    query: str = None,
    limit: int = 50,
    browser: str = "chrome",
    format_type: str = "markdown"
) -> str

Example:

# Get all bookmarks
get_bookmarks(browser="chrome")

# Search bookmarks
get_bookmarks(query="python", browser="firefox")

get_downloads

Get downloads history from a browser.

def get_downloads(
    query: str = None,
    limit: int = 50,
    browser: str = "chrome",
    format_type: str = "markdown"
) -> str

Example:

# Get all downloads
get_downloads(browser="chrome")

# Search downloads
get_downloads(query="pdf", browser="firefox")

HTTP API

Endpoints

Method Endpoint Description
GET /health Health check
GET /ready Readiness check
GET /metrics Basic metrics
GET /metrics/prometheus Prometheus metrics
GET /api/browsers List available browsers
POST /api/search Search history
POST /api/recent Recent history
POST /api/count Count domain visits
POST /api/top-domains Top domains
POST /api/search-date Search by date
POST /api/delete Delete history entries
POST /api/domain-search Search by domain
POST /api/stats Browser statistics
POST /api/most-visited Most visited pages
POST /api/export Export history
POST /api/advanced-search Advanced search
POST /api/sync Sync between browsers
GET /api/bookmarks List available bookmarks
POST /api/bookmarks/query Query bookmarks
GET /api/downloads List available downloads
POST /api/downloads/query Query downloads
POST /api/compare-periods Compare browsing periods
POST /api/productivity Analyze productivity
POST /api/suggest-categories Suggest URL categories
POST /api/visualization Export visualization data
POST /api/insights Generate insights report
POST /api/subscribe Subscribe to history changes
POST /api/unsubscribe Unsubscribe from history
POST /api/subscription-status Get subscription status
POST /api/find-duplicates Find duplicate entries
POST /api/delete-duplicates Delete duplicate entries

Request/Response Examples

POST /api/search

curl -X POST http://localhost:8080/api/search \
  -H "Content-Type: application/json" \
  -d '{"query": "python tutorial", "limit": 10, "browser": "chrome", "format": "markdown"}'

Response:

{
  "results": [
    {
      "title": "Python Tutorial",
      "url": "https://docs.python.org/3/tutorial/",
      "timestamp": "2024-01-15T10:30:00+00:00"
    }
  ],
  "count": 1
}

GET /health

curl http://localhost:8080/health

Response:

{
  "status": "healthy",
  "service": "chronicle-mcp",
  "version": "1.4.0",
  "timestamp": "2024-01-15T10:30:00+00:00"
}

Configuration

ChronicleMCP can be configured using environment variables or a config file.

Environment Variables

Variable Description Default
CHRONICLE_CONFIG Path to config file ~/.config/chronicle-mcp/config.toml
CHRONICLE_PORT Default port 8080
CHRONICLE_HOST Default host 127.0.0.1
CHRONICLE_BROWSER Default browser chrome

Config File

Create ~/.config/chronicle-mcp/config.toml:

[default]
browser = "chrome"
limit = 10
format = "markdown"
log_level = "INFO"

Security & Privacy

  • Local-Only: All data stays on your machine
  • URL Sanitization: Sensitive query parameters are automatically removed:
    • token, session, key, password, auth, sid, access_token
  • Temp Files: History is copied to temporary files that are cleaned up after each query
  • No Data Collection: Your browsing data is never sent to any server
  • Error Messages: No sensitive file paths are exposed

Supported Browsers

ChronicleMCP supports reading history from the following browsers:

Browser Version Windows macOS Linux
Chrome 120+ %LocalAppData%\Google\Chrome\User Data\Default\History ~/Library/Application Support/Google/Chrome/Default/History ~/.config/google-chrome/Default/History
Edge 120+ %LocalAppData%\Microsoft\Edge\User Data\Default\History ~/Library/Application Support/Microsoft Edge/Default/History ~/.config/microsoft-edge/Default/History
Firefox 121+ %AppData%\Mozilla\Firefox\Profiles\*.default\places.sqlite ~/Library/Mozilla/Firefox/Profiles/*.default/places.sqlite ~/.mozilla/firefox/*.default/places.sqlite
Brave 1.30+ %LocalAppData%\BraveSoftware\Brave-Default\History ~/Library/Application Support/BraveSoftware/Brave-Default/History ~/.config/BraveSoftware/Brave-Default/History
Safari 16+ N/A ~/Library/Safari/History.db N/A
Vivaldi 6.0+ %LocalAppData%\Vivaldi\Default\History ~/Library/Application Support/Vivaldi/Default/History ~/.config/vivaldi/Default/History
Opera 105+ %AppData%\Opera Software\Opera Stable\History ~/Library/Application Support/com.operasoftware.Opera/History ~/.config/opera/History

Troubleshooting

"Browser history not found"

  • Ensure the browser is installed
  • Check that Chrome/Edge isn't currently open (locks the database)
  • Run chronicle-mcp list-browsers to see detected browsers

"Permission denied"

  • Check file permissions on the browser's history database
  • On Windows, ensure the browser is closed before querying

Empty results

  • Try a more specific search term
  • Check the date range for search_history_by_date
  • Verify the browser has history data

Performance issues

  • Large history databases may take longer to query
  • Consider reducing the limit parameter

Development

Running Tests

# Run all tests
pytest

# Verbose output
pytest -v

# Specific test file
pytest tests/unit/

# Single test
pytest -k test_name

# With coverage
pytest --cov=chronicle_mcp

Development Server

# Run MCP server (stdio mode)
python -m chronicle_mcp mcp

# Run HTTP server
python -m chronicle_mcp http --port 8080

Code Quality

# Linting
ruff check .

# Formatting
ruff format .

# Type checking
mypy chronicle_mcp/

Contributing

Contributions are welcome! Please read our Contributing Guide for details.


License

MIT License - See LICENSE file for details.


Built with ❤️ for AI agents and developers

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

chronicle_mcp-1.5.0.tar.gz (70.8 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

chronicle_mcp-1.5.0-py3-none-any.whl (84.1 kB view details)

Uploaded Python 3

File details

Details for the file chronicle_mcp-1.5.0.tar.gz.

File metadata

  • Download URL: chronicle_mcp-1.5.0.tar.gz
  • Upload date:
  • Size: 70.8 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for chronicle_mcp-1.5.0.tar.gz
Algorithm Hash digest
SHA256 bdc59827accd2140ca6a236136a7bc689b5927f62c8cfd8d7ac6f4a2baf91cb9
MD5 3c335262ca94d2e0312b7b789a533bdb
BLAKE2b-256 9b4fcc3a2af50640a82bb9794d37ce5367b1572843edc7db0ce5947bcd071bfe

See more details on using hashes here.

File details

Details for the file chronicle_mcp-1.5.0-py3-none-any.whl.

File metadata

  • Download URL: chronicle_mcp-1.5.0-py3-none-any.whl
  • Upload date:
  • Size: 84.1 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for chronicle_mcp-1.5.0-py3-none-any.whl
Algorithm Hash digest
SHA256 36498924ef429afa856ea56528eb4aaf35d10a1f5f36f611061cc383e111ce20
MD5 5cf5bdc84385bb420b9ce24dbb1e71c9
BLAKE2b-256 76668deb3d1256e5e691acfe85b1f8464813852cd1a87ae046e2b2a8f99cc39d

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page