webresearcher-mcp-server
A light, standalone web-research MCP server: DuckDuckGo search, headless Camoufox page scraping (a hardened open-source Firefox), and same-domain crawling — zero Firecrawl, no API keys, no per-client browser download (in Docker mode).
It exposes four LLM-facing tools over MCP; the host LLM does the synthesis:
| Tool | What it does |
|---|---|
web_search(query, limit=5) |
DuckDuckGo via ddgs (browser-grade TLS). Numbered results: title, URL, snippet. |
web_scrape(url, max_chars=20000) |
Camoufox — a hardened open-source Firefox. Renders JavaScript and gets past cookie-consent banners (Yahoo/OneTrust/Cookiebot) that a bare HTTP fetch skips; degrades to a "rely on search" note when a page is protected or unreachable. max_chars=0 = full text. |
web_crawl(url, limit=10) |
Starts a background same-domain BFS crawl (one browser, wall-detected per page). Returns a crawl id immediately — never blocks. |
web_crawl_status(crawl_id, max_chars=20000) |
Crawl progress; on completion, per-page URLs + content (0 = full text). |
Failures are graceful by design: a blocked page or outage returns a short
SCRAPE UNAVAILABLE / SEARCH UNAVAILABLE note (never an exception), and the
note tells the LLM not to retry the same URL/query.
Why not just WebFetch / WebSearch?
This is not a replacement for your IDE's built-in fetch/search — it is the tool for JS-rendered, consent-gated, and multi-page research:
web_scrapereads pages a bare HTTP fetch can't. It runs a real browser (Camoufox), so JavaScript renders and cookie-consent overlays are handled — where the built-in fetch returns an empty shell, this returns the content. When a page is protected or unreachable it degrades to a clear "rely on search" note instead of failing.web_crawlhas no built-in equivalent (multi-page BFS + per-page blocked-page detection).web_searchis free and independent of the harness's search availability; built-in search has the better generic index — use both.- Plain public static pages: the built-in fetch is fine, no need to route everything through this server.
A typical session: web_search("BOX stock news") → web_scrape the 2–4 best
results → optional web_crawl of the newsroom → synthesize with citations.
Quick start (uvx — no Docker)
Run it straight from PyPI. uvx auto-installs the package (and its mcp
extra) into an isolated env and runs the stdio server as an ordinary child
process — no Docker, no install step, and it's cleaned up when your session
ends:
uvx "webresearcher-mcp-server[mcp]" --help # sanity check
uvx "webresearcher-mcp-server[mcp]" # stdio server (default transport)
The Camoufox browser (used by web_scrape/web_crawl) is downloaded on
first use (~5 min one-time) or pre-fetched with python -m camoufox fetch.
Shared HTTP server (optional — one detached process shared by every IDE/session so pacing + cache are global; the only Docker path we still use):
docker build -t webresearch-mcp . # or: docker compose up -d
docker run -d --name webresearch-mcp -p 8001:8001 webresearch-mcp \
--transport streamable-http --host 0.0.0.0 --port 8001
(healthcheck: curl -fs -X POST http://localhost:8001/mcp -H 'Content-Type: application/json' -H 'Accept: application/json, text/event-stream' -d '{"jsonrpc":"2.0","id":1,"method":"tools/list"}' →
lists the four tools)
Integrating with Claude Code / Cursor
Claude Code
uvx (recommended — no Docker): add to your project .mcp.json
(or claude mcp add). First use auto-installs the package into an isolated
env (cached afterward); the server runs as an ordinary child process and is
cleaned up when the session ends:
{ "mcpServers": {
"webresearch": {
"command": "uvx",
"args": ["webresearcher-mcp-server[mcp]", "--transport", "stdio"]
} } }
Avoid the
docker run -i --rmper-session shape. It leaks orphaned containers: Claude Code closes the child's stdin pipe on exit, the container (owned by the Docker daemon) never receives a stop signal, so--rmnever fires and stray containers pile up. Known issue: anthropics/claude-code#29058.
Docker-HTTP (one detached container shared by all sessions): run the shared container above, then:
{ "mcpServers": { "webresearch": { "type": "http",
"url": "http://localhost:8001/mcp" } } }
Plain venv (no uv/uvx on the box): install the package in a venv, then:
{ "mcpServers": { "webresearch": {
"command": "/path/to/.venv/bin/python",
"args": ["-m", "webresearch_mcp"] } } }
Cursor
Same shapes in ~/.cursor/mcp.json (user) or .cursor/mcp.json (project).
uvx (no Docker):
{ "mcpServers": { "webresearch": {
"type": "stdio",
"command": "uvx",
"args": ["webresearcher-mcp-server[mcp]", "--transport", "stdio"] } } }
or the {"url": "http://localhost:8001/mcp"} form for the shared HTTP
container. See docs/integration.md for the full
walkthrough, including the optional cross-IDE plugin (plugin/): one
directory, two manifests (Claude Code + Cursor), bundling the stdio server +
an always-on research rule + a /research command + a web-researcher
subagent — no Docker needed.
Plain Python (no uv/uvx)
python3 -m venv .venv && . .venv/bin/activate
pip install "webresearcher-mcp-server[mcp]" # or: pip install -e ".[mcp]" from a clone
python -m camoufox fetch # one-time browser download
webresearcher-mcp-server # stdio server
webresearcher-mcp-server --transport streamable-http --port 8001
Configuration (env vars)
| Variable | Default | Effect |
|---|---|---|
WEBRESEARCH_MIN_CALL_INTERVAL_S |
10 |
Paces the start of search/scrape calls (polite rate-limiting). |
WEBRESEARCH_SUCCESS_TTL_S |
3600 |
Cache TTL for successful search/scrape results. |
WEBRESEARCH_CACHE_SIZE |
512 |
LRU cache size (failures cached 60 s regardless). |
WEBRESEARCH_CRAWL_PAGE_DELAY_S |
3 |
Inter-page delay inside crawls (kept small so multi-page crawls stay fast). |
WEBRESEARCH_CONSENT_AUTOACCEPT |
true |
Auto-click cookie-consent "accept" banners (Yahoo guce, OneTrust, Cookiebot). Set false to never click. |
TRADINGAGENTS_CAMOUFOX_HEADLESS |
true |
Headless browser (set false to watch it work). |
TRADINGAGENTS_WEB_SCRAPE_TIMEOUT_S |
120 |
Per-page load timeout (s). |
Notes: crawl state is in-memory (a restart loses in-flight crawls); crawls hold the single browser, so standalone scrapes queue behind an active crawl.
Development
pip install -e ".[mcp,test,lint]"
ruff check .
pytest -m "unit or smoke" # hermetic (CI runs exactly this)
pytest -m integration # live network + Docker (auto-skip if missing)
Layered markers: unit (pure helpers, patched seams), smoke (real MCP
protocol over stdio/HTTP against a hermetic fake upstream), integration
(live web + real browser + Docker image).
Responsible use
This is a research/reading tool for publicly available content, intended for personal and internal use.
- Respect each site's Terms of Service and
robots.txt. If a page or site blocks you, stop — don't keep trying to get past it. - Don't use it to access paywalled or protected content, or to harvest content or personal data at scale.
- You are responsible for how you use it: scraping may be restricted by the site you're reading and by the law in your jurisdiction.
License
Apache-2.0. Portions derived from TradingAgents (Apache-2.0) — see NOTICE.
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file webresearcher_mcp_server-0.1.1.tar.gz.
File metadata
- Download URL: webresearcher_mcp_server-0.1.1.tar.gz
- Upload date:
- Size: 39.9 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via:
uv/0.11.21 {"installer":{"name":"uv","version":"0.11.21","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
e2fcf85254b760951a29078118a20ce69870eaadfd8d9609aca32bf5d15b6a9f
|
|
| MD5 |
ce87196e6b6822e9f23f7846491bd1bf
|
|
| BLAKE2b-256 |
843d61c7617b200b4db98475ee4f29a58e5e94d49d63bb18e7ccb0f088d2b7a1
|
File details
Details for the file webresearcher_mcp_server-0.1.1-py3-none-any.whl.
File metadata
- Download URL: webresearcher_mcp_server-0.1.1-py3-none-any.whl
- Upload date:
- Size: 28.2 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via:
uv/0.11.21 {"installer":{"name":"uv","version":"0.11.21","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
f0fc962866664e9c6a2cd2075fe1dbae09b730534185b1c7b3cfa1aba8c199b2
|
|
| MD5 |
277bebc1e82664e2a4d89d2ef09d5c02
|
|
| BLAKE2b-256 |
9919aba758dacc37810e300ec6f8b87b35b09f643dcd3bb160e178b765ecf7ab
|