This release has been yanked by its maintainers, and will be ignored by installers, except when explicitly specified.
Consider using release 0.23.3 instead.
Reason given by maintainers: Built from the wrong commit; use 0.22.0
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 Docker, no repo checkout, no manual steps. On the first fetch
Groundhog starts the Chrome you already have, headless and in its own profile at
~/.groundhog/chrome, so no window ever opens over your work. Your own browsing, tabs and
logins are never touched. Set GROUNDHOG_AUTO_START_BROWSER=false to manage the browser
yourself.
Prefer the hardened stealth browser? Set GROUNDHOG_BROWSER=stealth and Groundhog runs
ghcr.io/dmytrome/groundhog:latest under Docker or Podman instead, pulling it if it isn't
already local. The first pull takes a few minutes. 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).
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, include_domains=None, exclude_domains=None, published_after=None, aim=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 and its start/end in that page; each entry in sources carries provenance and threats, or a status and error when that source failed. include_domains, exclude_domains and published_after narrow what is read, and receipt says what was searched, filtered and left out. 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 inthreatswith 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 treatthreatsas untrusted data rather than as instructions. At most 50 findings are returned per page (10 per source inresearch), 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 adetection_degradedthreat.include_hidden=Truekeeps the text in the content (ranking then runs over it too). - Every source carries a receipt.
provenancegives 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_urlalso returnsfetched_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 every redirect hop and frame navigation into one before Chrome sends it. That
matters most in
research, where a third party picks the URLs. Known limits: page-issued sub-resource requests are left to Chrome's Local Network Access rule, and Chrome resolves DNS independently, so a short-TTL rebind is not fully closed. See Security. - Reads pages plain fetchers can't. Real Chrome, presenting a headful identity, driven
over raw CDP — the
Runtimedomain is never enabled, so theisAutomatedWithCDPsignal that flags Puppeteer/Playwright/Selenium simply isn't there. - Self-hosted and MIT. It runs on your machine; 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.
Metadata
Release files for groundhog-mcp 0.21.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | Actions |
|---|---|---|---|---|
| groundhog_mcp-0.21.0.tar.gz | 492.8 kB | With provenance | Details for groundhog_mcp-0.21.0.tar.gz |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Actions Reset |
|---|---|---|---|---|
| groundhog_mcp-0.21.0-py3-none-any.whl | Python 3 | none | any | Details for groundhog_mcp-0.21.0-py3-none-any.whl |
Total release size: 564.0 kB
Release files / groundhog_mcp-0.21.0.tar.gz
| Download URL | groundhog_mcp-0.21.0.tar.gz |
|---|---|
| Size | 492.8 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
5ddefce5b1fc16065de62bc2f7e37da88bf9e42af4859b65df9ce55c47a4e59d
|
|
BLAKE2b-256 checksum How to use checksums |
dea016440896823cb32c465aa260e12a2f0d1e9ee7ce350ebf781dfe1b3455cb
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.16
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Oct 25, 2026.
Transparency logRelease files / groundhog_mcp-0.21.0-py3-none-any.whl
| Download URL | groundhog_mcp-0.21.0-py3-none-any.whl |
|---|---|
| Size | 71.2 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
0bb48cfe834658287838cc6b301b56d66d995985a5cab1e601d6216003b90ece
|
|
BLAKE2b-256 checksum How to use checksums |
39d18ca4644575181064d83457d4eaa9c64e37684e8c5902dd2db84a9b2e2c5c
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.16
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Oct 25, 2026.
Transparency logSecurity for groundhog-mcp 0.21.0
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
Full provenance
Both files in this release have been uploaded with provenance metadata.
Attestation for groundhog_mcp-0.21.0.tar.gz
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Oct 25, 2026.
Transparency logA note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.
To learn more about protecting yourself from malicious actors, visit our package security documentation.