fetchaller-mcp
Pinned MCP fetch/search server for domains that block Claude Code's built-in
WebFetch/WebSearch — Reddit and 15 other sites. dev-guard's
BLOCKED_URL_RULES redirects WebFetch/WebSearch calls to these domains
toward this server's tools instead. Live-verified during planning against
real Reddit content and a web.archive.org snapshot (both cases built-in
WebFetch cannot reach at all).
Backed by Averyy/fetchaller-mcp
(MIT), which uses wafer-py[browser]'s TLS-fingerprint impersonation and
Patchright (patched Playwright) browser automation to clear bot challenges.
Setup
Requires:
uv(already required by this marketplace's other Python tooling)- A local Chrome install (
wafer-py[browser]'sBrowserSolverdrives real Chrome via Patchright — it validates the configured executable is branded Google Chrome, not Chromium)
Add BROWSER_EXECUTABLE_PATH to ~/.claude/settings.json's env block,
pointing at a dedicated, separate Chrome install (e.g. a Chrome for
Testing binary) — never your daily-driver browser (see the WARNING below):
{
"env": {
"BROWSER_EXECUTABLE_PATH": "/Applications/Google Chrome for Testing.app/Contents/MacOS/Google Chrome for Testing"
}
}
Linux and Windows paths differ (e.g. /usr/bin/google-chrome on most Linux
distributions, C:\Program Files\Google\Chrome\Application\chrome.exe on
Windows) — adjust accordingly.
BROWSER_EXECUTABLE_PATH is required, not optional. If it's left unset,
fetchaller-mcp omits the executable_path argument when constructing
wafer-py's BrowserSolver (confirmed directly in fetchaller-mcp's
server.py at the pinned commit), and BrowserSolver itself then falls back
to its own OS-standard Chrome auto-detection — confirmed directly in
wafer-py's own browser/_solver.py at the pinned 0.4.4 version
(_browser_executable() calls _system_chrome_executable() when no
executable path was configured). On macOS that fallback checks exactly
/Applications/Google Chrome.app/Contents/MacOS/Google Chrome. If a Chrome
install exists there — true for most users — it is used silently, with
no warning or error: the same daily-driver-Chrome exposure the WARNING below
describes, triggered by leaving this unset rather than by misconfiguring it.
Always set this variable explicitly.
WARNING — use a dedicated, logged-out Chrome install or profile, not your
daily-driver browser. fetchaller-mcp/wafer-py[browser] runs real
browser-automation against BROWSER_EXECUTABLE_PATH with no profile
isolation option. We investigated this directly (checked fetchaller-mcp's
README at the pinned commit, its documented environment variables, and
wafer-py's BrowserSolver API on PyPI): fetchaller-mcp exposes no
CHROME_ARGS-style environment variable or launch-flag passthrough, and
wafer-py's BrowserSolver only accepts executable_path, headless,
idle_timeout, and solve_timeout — no user_data_dir or equivalent
profile-isolation parameter, even though the underlying Patchright library
supports one internally. There is currently no way to force an isolated
profile through this integration. If BROWSER_EXECUTABLE_PATH points at your
normal Chrome, this MCP server's automation runs against a profile carrying
your live cookies, active sessions, and saved passwords for every site
you're logged into — a session-hijacking-adjacent exposure if fetchaller-mcp
or its dependencies were ever compromised or misbehaved. Install a second,
separate Chrome (or Chrome for Testing) that you never log in with, and point
BROWSER_EXECUTABLE_PATH at that instead.
Pinned version
Pinned to commit
a74501c7e
(a74501c7eac721a0604782f73ccfef5cab1975df) rather than tracking main,
because:
- Solo maintainer, project is ~7 months old
- No PyPI release for the MCP server itself — only installable via
uvx --from git+<url>@<sha> - Its
[browser]extra depends onwafer-py, a fast-churning, trust-critical dependency (TLS-fingerprint impersonation + bot-challenge solving) with no upper version bound in fetchaller-mcp's ownpyproject.toml
.mcp.json additionally pins --with "wafer-py[browser]==0.4.4" — the exact
version fetchaller-mcp's own uv.lock resolves to at this commit. This
second pin is required, not redundant with the commit SHA: uvx --from git+
does not consult the target repository's own uv.lock when resolving
dependencies (confirmed by a uv maintainer,
astral-sh/uv#13414), so
without the explicit --with pin, wafer-py would re-resolve to whatever
the latest PyPI release compatible with >=0.4.4 is at install time —
silently drifting on every fresh uv cache miss despite the commit SHA
pin holding fetchaller-mcp's own code fixed.
The combined uvx --from git+...@a74501c7e --with "wafer-py[browser]==0.4.4" fetchaller-mcp invocation was independently live-verified (via --help) to
resolve and start cleanly before this pin was merged (2026-08-01). This
verification is also a permanent regression test —
dev-guard/tests/test_plugin_integrity.py::TestFetchallerMcpConfigSecurity::test_live_uvx_invocation_resolves_and_starts,
opt-in via make test-live.
Manual update process
Never auto-track main. To bump the pin:
- Diff
git log <old-sha>..<new-sha>onAveryy/fetchaller-mcpand review every commit. - Diff
uv.lockbetween the two commits on GitHub, specifically forwafer-py/wreqversion changes. - Update
.mcp.json's--with "wafer-py[browser]==<version>"to match the new commit's resolvedwafer-pyversion. - Re-verify against a real Reddit URL and a
web.archive.orgURL (this plugin's live-test pattern from planning) against the candidate SHA before updating.mcp.json. - Redo the Tool capability audit below against the new commit (new or
removed tools, changed schemas, the
fetchparameter-name check). Update the audit section anddev-guard/hooks/mcp_constants.py'sMCP_READ_ONLYlist accordingly if anything changed.
Security posture
- stdio-only.
.mcp.jsonnever passes--http(hosted mode). A structural test (dev-guard/tests/test_plugin_integrity.py::TestFetchallerMcpConfigSecurity) asserts--httpnever appears in the checked-in args and that the git ref is always a full 40-character commit SHA. - No vendored source, no auto-sync. The server runs directly from the
pinned upstream commit via
uvx --from git+; nothing is copied into this marketplace, and there is no scheduled job re-pinning to a newer commit. - Runs arbitrary third-party Python with local browser-automation
privileges. Mitigated by the commit-SHA pin, the
wafer-pyversion pin, and the manual (never automatic) update process above. - Optional further hardening (not implemented by this plugin): an
OS-level network-egress restriction (a
sandbox-execprofile on macOS, or a firewall rule) limiting this MCP server's outbound connections, as defense-in-depth on top of the mitigations above. Left undone for this personal, single-user setup — revisit if this ever runs in a shared/multi-tenant environment.
Tool capability audit (performed at the pinned commit,
a74501c7eac721a0604782f73ccfef5cab1975df, and re-required on every future
SHA bump per the manual update process above):
fetchis a generic authenticated HTTP client —tools/fetch.pyallows both GET and POST, up to 32 custom headers (includingauthorization), and request bodies up to 1MB. Because of that, it is deliberately not included indev-guard's blanketMCP_READ_ONLYallow-list. Instead,tool-selection-guard.pygates each call: a plain GET with no custom headers or body is auto-approved (the normal blocked-domain-redirect case this plugin exists for), while a POST or any call carrying custom headers/body asks for confirmation first. This gate depends on fetchaller'sfetchtool using exactly the parameter namesmethod/headers/body— if a future commit renames these, the gate silently fails open (auto-approves) instead of failing closed. Re-verify these parameter names againsttools/fetch.py's actual signature on every SHA bump.searchis a plain HTTP GET against Google or DuckDuckGo (search/google.py,search/ddg.py) — no browser automation, no state. It's included inMCP_READ_ONLY, and is in fact a safer tool than several others already on that list.- The remaining 10 tools (
browse_reddit,search_reddit,search_marketplace,search_realtor,search_linkedin_jobs,get_linkedin_job,get_aliexpress_product,search_aliexpress,get_alibaba_product,search_alibaba) are read-only at the MCP schema level — only query/filter/ID parameters,additionalProperties: False, no mutation-shaped fields anywhere. Facebook Marketplace was additionally spot-checked at the source level (facebook_marketplace/graphql.py) and confirmed to use only hardcoded, non-mutating GraphQL query IDs, not caller-controlled queries. All 10 are included inMCP_READ_ONLY.