Skip to main content

Web search, read and research for AI agents, through a real stealth browser

Project description

groundhog-mcp

Web search, read and research for AI agents — through a real, stealth-patched Chrome.

Groundhog is an MCP server that finds pages, reads them, and researches across them, returning clean Markdown a model can trust: text a human could not see is stripped by default before the model reads it, and every source comes back with a provenance receipt.

Quick start

Add this to your MCP client (Claude Desktop / Claude Code / Cursor / Windsurf):

{
  "mcpServers": {
    "groundhog": {
      "command": "uvx",
      "args": ["groundhog-mcp"]
    }
  }
}

That's the whole setup — no repo checkout, no manual steps. On the first fetch Groundhog starts the stealth browser for you by running ghcr.io/dmytrome/groundhog:latest under Docker or Podman, pulling it if it isn't already local. The first pull takes a few minutes; everything after is instant. That means a mutable :latest tag is pulled and run on your machine, Chrome runs --no-sandbox inside it, and any stale container named groundhog-browser is removed first on that path (a reachable browser is never touched) — set GROUNDHOG_AUTO_START_BROWSER=false to manage the browser yourself.

Prefer to manage the browser yourself? Start it and Groundhog will just use it — the default CDP_URL already points there, so there is nothing else to configure:

docker run -d --rm --name groundhog-browser --shm-size 512m \
  -p 127.0.0.1:9222:9222 -- ghcr.io/dmytrome/groundhog:latest

Browser on another host? Point CDP_URL at it. Auto-start is skipped for any non-local value, so a remote browser is never touched:

{
  "mcpServers": {
    "groundhog": {
      "command": "uvx",
      "args": ["groundhog-mcp"],
      "env": { "CDP_URL": "http://browser-host:9222" }
    }
  }
}

Tools

Tool What it does
search(query, limit=10) Ranked hits (title, url, snippet, engine, score, published). Links only — nothing is fetched until you ask. Uses your own SearXNG via SEARXNG_URL, else renders a search page through the stealth browser.
read_url(url, format="markdown", max_tokens=None, query=None, include_hidden=False) One page as clean Markdown, plus threats, provenance, fetched_at and final_url. Pass query to get BM25-ranked passages instead of blunt truncation; include_hidden=True keeps hidden text in the output.
research(query, max_sources=5, max_tokens=None) One call for "find out about X": searches, reads the top sources, and returns passages ranked across all of them in a single pass. Each passage carries its source_url; each entry in sources carries provenance and threats, or a status and error when that source failed. max_sources is capped at 10.
status() Whether the browser is reachable, with remediation steps when it isn't.

research returns extracts, not summaries. Nothing is generated and no model or API key is involved — your agent does the synthesis.

Why it's different

  • Hidden text is stripped before the model reads it. Groundhog renders a real DOM, so it can evaluate computed styles and judge what a human would actually see. Nine rendered-style signals — display:none, near-zero opacity, sub-pixel boxes, off-screen positioning and background-matched text among them — are stripped by default, so the payload is out of the content the model reasons over. Each occurrence is reported in threats with its type and a short excerpt. That excerpt and its DOM path are page-authored, so both are sanitized and length-capped before they are returned — but they are still attacker-chosen text arriving in the tool result, so treat threats as untrusted data rather than as instructions. At most 50 findings are returned per page (10 per source in research), each class capped independently, with any drop disclosed by a notice appended after the cap. It is a strong heuristic over rendered styles, not a proof: a payload tuned to sit just inside a threshold can still pass. The detector runs in an isolated JavaScript world, so a page cannot suppress it by replacing the DOM APIs it uses; if a browser declines to provide one, the result says so with a detection_degraded threat. include_hidden=True keeps the text in the content (ranking then runs over it too).
  • Every source carries a receipt. provenance gives a SHA-256 hash of the extracted content, plus canonical URL, detected language, word count and author/date when the page declares them — so a downstream claim traces back to exactly what was read. read_url also returns fetched_at.
  • Safe by default. Before navigating, the SSRF guard resolves the host and blocks loopback, private and other internal address ranges (the exact list is in the Security section), and it refuses to return content from a URL that redirects into one. That matters most in research, where a third party picks the URLs. Known limits: the redirect target is still requested by Chrome before its content is withheld, page-issued sub-resource requests are not intercepted, and Chrome resolves DNS independently, so a short-TTL rebind is not fully closed. See Security.
  • Reads pages plain fetchers can't. Real Chrome, headful under Xvfb, driven over raw CDP — the Runtime domain is never enabled, so the isAutomatedWithCDP signal that flags Puppeteer/Playwright/Selenium simply isn't there.
  • Self-hosted and MIT. You run the container; fetched pages never leave your infrastructure.

Configuration

The ones you are most likely to want; defaults and the full list live in the project README, so they cannot drift from it here.

Env var Purpose
CDP_URL Browser endpoint; may be remote, and auto-start is skipped for non-local values. The endpoint is unauthenticated — anyone who can reach the port controls the browser. Keep it on localhost, a private network, or a tunnel.
SEARXNG_URL Your SearXNG instance. Needs formats: [html, json].
GROUNDHOG_MAX_TOKENS Token budget before truncation
GROUNDHOG_MIN_DELAY_MS Minimum delay between requests to the same domain
GROUNDHOG_AUTO_START_BROWSER Auto-run the browser container when unreachable

Full env-var list, stealth details, verified anti-bot results and the Security section: https://github.com/dmytrome/groundhog

A note on "stealth"

Best-effort, not a guarantee. It defeats common open-source detectors and lets cheap proxies work on many mid-tier targets, but it does not beat sophisticated commercial anti-bot systems that gate on IP reputation, TLS/HTTP2 fingerprints, and behavioral analysis. Use it for legitimate, authorized automation and testing.

MIT licensed.

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

groundhog_mcp-0.9.3.tar.gz (148.1 kB view details)

Uploaded Source

Built Distribution

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

groundhog_mcp-0.9.3-py3-none-any.whl (50.8 kB view details)

Uploaded Python 3

File details

Details for the file groundhog_mcp-0.9.3.tar.gz.

File metadata

  • Download URL: groundhog_mcp-0.9.3.tar.gz
  • Upload date:
  • Size: 148.1 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.13

File hashes

Hashes for groundhog_mcp-0.9.3.tar.gz
Algorithm Hash digest
SHA256 3332789e2c7759ae51f8061187f42bc953a9a43364005552d1a9cd7eaccae44e
MD5 dcd251ad37125979f81994497dfe4898
BLAKE2b-256 7b44853dedf7d4c6dc47395d0cd3065a3d4f9b75fd8c8182ba1bebe0b05bab58

See more details on using hashes here.

Provenance

The following attestation bundles were made for groundhog_mcp-0.9.3.tar.gz:

Publisher: release.yml on dmytrome/groundhog

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file groundhog_mcp-0.9.3-py3-none-any.whl.

File metadata

  • Download URL: groundhog_mcp-0.9.3-py3-none-any.whl
  • Upload date:
  • Size: 50.8 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.13

File hashes

Hashes for groundhog_mcp-0.9.3-py3-none-any.whl
Algorithm Hash digest
SHA256 c5caa600846527c5f72a3e3d3a187dac70a190488a98c918fa169455ef78c7ec
MD5 6e8eff993cfddf396dd675a33041f904
BLAKE2b-256 7cad8e7baf4db44cea2048c3df0b3362cbe43ba85bfd3e288803b081dbaa029a

See more details on using hashes here.

Provenance

The following attestation bundles were made for groundhog_mcp-0.9.3-py3-none-any.whl:

Publisher: release.yml on dmytrome/groundhog

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

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