geizhals-mcp
Click on "Install Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@geizhals-mcpsearch for the cheapest RTX 4070"
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
geizhals-mcp
An MCP server that lets an LLM search Geizhals, a leading German/DACH price-comparison site, for the cheapest offers on new products. Built for self-hosting: one container, no API keys, no per-query credits.
Companion to jnslmk/kleinanzeigen-mcp — that one covers second-hand classifieds, this one covers new retail goods.
⚠️ Status: scaffolded, not yet smoke-tested. Geizhals sits behind a Cloudflare JS challenge, so this drives a real (patched, headed) browser to reach the page. The class-name-based selectors in
scraper.py(_SEL) were written without live-HTML access and must be verified against real pages before the extraction can be trusted. The JSON-LD and product-href paths are designed to hold regardless. See Verifying the scraper.
Tools
Tool | What it does |
| Search by keyword, sort by price/relevance, filter by price band. Returns product summaries. |
| Full detail — name, price range, per-merchant offers — for one product id. |
| Full details for several ids at once — the normal follow-up to a search. |
| Search from a pasted Geizhals URL, preserving filters |
The intended flow is search_products → pick interesting ids →
get_products_batch. Search results omit the per-merchant offer tables so a
broad search does not blow up the model's context window.
How it works
Geizhals has no public API and is fronted by Cloudflare's "Sichere Verbindung wird überprüft" JS challenge, which returns 403 to any plain HTTP client. So:
Browser: patchright (a patched, undetected Playwright fork) drives Chromium headed under Xvfb — a headed browser behind a virtual display clears Cloudflare far more reliably than headless from a datacenter IP.
Parsing:
scraper.pyprefers the page'sapplication/ld+jsonProduct block and the stableaNNNNNNN.htmlproduct hrefs, falling back to CSS selectors (collected in_SEL) for the human-readable fields.
Running it
docker run --rm -p 8000:8000 --shm-size=1g ghcr.io/jnslmk/geizhals-mcp:latestStreamable HTTP at http://localhost:8000/mcp, plain GET /healthz for
container healthchecks. Give the container at least 1.5 GB of memory and
--shm-size=1g — Chromium maps its renderer heap into /dev/shm.
Configuration
Variable | Default | Purpose |
|
|
|
|
| Bind address |
|
| Bind port |
|
| MCP endpoint path |
|
| Concurrent browser contexts / scrapes |
|
| Hard cap on products returned by a search |
|
| Cap on ids per |
|
| How long to wait for Cloudflare to clear |
|
|
|
|
| Python log level |
LibreChat
mcpServers:
geizhals:
type: streamable-http
url: "http://geizhals-mcp:8000/mcp"
timeout: 180000
chatMenu: true
mcpSettings:
allowedAddresses:
- "geizhals-mcp:8000"allowedAddresses is required — LibreChat's SSRF guard blocks MCP URLs that
resolve to private addresses, which a sibling container always does. The timeout
is generous because a cold call starts a browser and waits out a Cloudflare
challenge.
Claude Code
claude mcp add --transport http geizhals http://localhost:8000/mcpDevelopment
uv venv && uv pip install -e .
patchright install chromium
# Headless is fine for a quick check; headed (default) needs a display:
GH_HEADLESS=1 python -m geizhals_mcpVerifying the scraper
The extraction was written without live-HTML access. Before trusting a release:
Run the server and call
search_productswith a common term (e.g.RTX 4070).If results come back empty but the logs show no Cloudflare block, the row selectors in
_SELare stale — capture a real search page (page.content()), inspect it, and correct_SEL["row"],_SEL["row_price"]and_SEL["row_offercount"].Do the same for
get_productagainst_SEL["offer_*"].If calls fail with a Cloudflare-timeout error, the datacenter IP is flagged — route egress through a residential/mobile proxy (see below).
Cloudflare and proxies
Cloudflare challenges are much harsher on datacenter IPs. If the headed-Xvfb
browser cannot clear them from your host, point Chromium at a residential/mobile
egress proxy. browser.py is the single place to add a proxy= argument to
chromium.launch.
Images
ghcr.io/jnslmk/geizhals-mcp — multi-arch (linux/amd64, linux/arm64), built
by GitHub Actions on native runners for each architecture.
Tag | Meaning |
| Newest build of |
| A specific commit |
| Release tags |
Caveats
Geizhals has no public API, so this scrapes the site with a headless browser. It can break whenever Geizhals change their markup or tighten Cloudflare, and heavy or parallel use will trip bot detection — particularly from a datacenter IP. Scraping is also at odds with Geizhals' terms of service. Keep it to personal-scale use; the conservative concurrency defaults exist for exactly this reason.
License
MIT — see LICENSE.
This server cannot be installed
Maintenance
Resources
Unclaimed servers have limited discoverability.
Looking for Admin?
If you are the server author, to access and configure the admin panel.
Latest Blog Posts
- Your AI Chatbot Just Exposed Your CEO's Salary to an InternBy Om-Shree-0709 on .Agent IdentityMCP SecurityOAuth Delegation
- Why MCP Servers Need Execution Sandboxing (And Why Your Current Stack Isn't Enough)By Om-Shree-0709 on .Agentic AiPrompt InjectionWebAssembly
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/jnslmk/geizhals-mcp'
If you have feedback or need assistance with the MCP directory API, please join our Discord server