browser-relay-mcp
Unlimited, session-authenticated web search and fetch for AI tools using your own browser. Supports authenticated/paywalled pages without API keys.
README
browser-relay-mcp
Unlimited, session-authenticated web search + fetch for your AI tools — driven through your own browser, behind a local MCP server.
No API keys, no per-query billing, no rate-limit walls. Searches and page fetches run in a real browser that is already logged into the sites you use, so paywalled / authenticated pages come back as clean readable text just like you'd see them.
Two interchangeable drivers sit behind one MCP surface:
relay(default) — drives your logged-in Chrome via a loaded extension. The extension polls the local relay athttp://localhost:15552, runs the search/fetch in a tab, and posts the result back. Your sessions, your cookies.cloak— an embedded patchright stealth browser, launched in-process by the relay. For bot-protected or unattended pages where you don't want to (or can't) use your attended Chrome. Requirespatchright install chromiumonce.
Every MCP tool takes a driver parameter, so a caller can pick per-request.
Architecture
┌─────────────────────────────────────────┐
MCP client │ browser-relay-mcp │
(Claude, STORM, …) │ │
│ │ MCP server (stdio / FastMCP) │
│ search/fetch/ │ │ │
│ search_and_ │ │ HTTP │
│ fetch/resume/ │ ▼ │
└───────────────► │ relay (FastAPI @ localhost:15552) │
tool call │ │ │
│ ├── driver=relay ─► job queue ──────┼──┐
│ │ │ │ poll /pending
│ │ │ │ post /result
│ └── driver=cloak ─► patchright ─────┼─┐│
└─────────────────────────────────────────┘ ││
││
┌────────────────────────────────────────────────┐ ││
│ embedded stealth Chromium (in-process) ◄────────┼──┘│
└────────────────────────────────────────────────┘ │
┌────────────────────────────────────────────────┐ │
│ YOUR logged-in Chrome + Browser Relay ext ◄───┼───┘
│ (polls localhost:15552, runs job in a tab) │
└────────────────────────────────────────────────┘
The MCP server auto-starts the relay backend on first use (a local FastAPI process on 127.0.0.1:15552). The relay driver needs the Chrome extension loaded and pointed at that URL; the cloak driver needs patchright install chromium.
Install
1. The MCP server
Run it with uv — no checkout required:
uvx browser-relay-mcp
Add it to your MCP client config:
{ "mcpServers": { "browser-relay": { "command": "uvx", "args": ["browser-relay-mcp"] } } }
2. The Chrome extension (for the relay driver)
- Open
chrome://extensions, enable Developer mode. - Load unpacked → select the
extension/directory (or unzip a packaged build — see Packaging). - Open the extension's options and set the Relay server URL to
http://localhost:15552. - The popup status should read Relay OK / connected.
The extension only acts on pages the relay explicitly tells it to drive — see docs/privacy-policy.md.
3. The cloak browser (for the cloak driver)
One-time, to download the stealth Chromium build patchright uses:
patchright install chromium
If you only ever use driver: "relay", this step is optional. When it's missing, /health reports the cloak driver as unavailable instead of failing your relay calls.
Tools
All tools return a JSON string. The driver parameter selects which backend runs the request.
| Tool | Signature | Returns |
|---|---|---|
search |
search(query, k=10, engine="bing", driver="relay") |
{status, query, engine, driver, count, results:[{title, url, snippet}]} |
fetch |
fetch(url, include_html=False, driver="relay") |
{status, url, driver, title, text, excerpt, length[, html]} |
search_and_fetch |
search_and_fetch(query, k=5, engine="bing", driver="relay") |
{status, query, engine, driver, count, results:[{title, url, snippet, text, length, fetch_error}]} |
resume |
resume(resume_token) |
the completed result, action_required again if still blocked, or error if the token expired |
health |
health() |
relay + extension + cloak connectivity and queue depth (see below) |
search_and_fetch runs the search, then fetches the top-k results in parallel. A page that fails to fetch records its own fetch_error and text=""; the batch still returns. Per-result escalation inside the batch is non-interactive — a blocked result is reported as fetch_error: "action_required: <action>", and you re-drive it with an individual fetch(url).
health shape
{
"status": "ok",
"extension_connected": true,
"extension_status": "connected",
"last_poll_age_seconds": 2.1,
"search_queued": 0,
"fetch_queued": 0,
"in_flight": 0,
"max_fetch_tabs": 5,
"engine": "bing",
"pending_actions": [],
"version": "0.1.0",
"drivers": {
"relay": { "extension_connected": true, "extension_status": "connected", "last_poll_age_seconds": 2.1 },
"cloak": { "available": true, "profile_path": "…/browser-relay/cloak-profile", "pages_open": 0 }
}
}
extension_status is one of connected, stale (extension was seen but hasn't polled within ~75s), or never_seen. The cloak block reports available: false with an error when patchright's browser isn't installed.
Escalation flow (CAPTCHA / login)
When a search or fetch hits a CAPTCHA or a login wall, the call doesn't fail — it pauses and surfaces the blocked browser to you:
search/fetch ──► status: "action_required"
{ driver, action: "solve_captcha" | "login",
message, resume_token, query|url }
│
you solve the CAPTCHA / sign in
in the surfaced browser window
│
▼
resume(resume_token) ──► status: "ok" (completed result)
status: "action_required" (still blocked — solve again, same token)
status: "error" (token expired, default TTL 300s)
relay: the extension holds the tab open; you solve it in your Chrome.cloak: the embedded browser window is non-headless on purpose so you can solve the challenge there.
resume reuses the held tab/page rather than starting over, and the same resume_token stays valid across repeated action_required rounds until it succeeds or the TTL (BROWSER_RELAY_ACTION_TTL, default 300s) expires.
STORM adapter
adapters/storm.py maps a search_and_fetch result into the STORM / dspy retriever shape, so browser-relay can back STORM's knowledge-curation retrieval:
from adapters.storm import to_storm
# `result` is the parsed dict from search_and_fetch(...)
sources = to_storm(result)
# -> [{ "url", "title", "description", "snippets": [chunks of the page text] }, ...]
Only results that actually came back with text are included; page text is chunked (1000 chars/chunk) into snippets.
Run the acceptance checks
The PRD acceptance criteria are split between a live human-attended script and unit tests.
Live (C1–C4) — requires Chrome with the extension loaded and signed in:
# 1. start the relay
cd server && uv run browser-relay-mcp --backend --port 15552
# 2. in Chrome: load unpacked extension/, set server URL to http://localhost:15552 (status: connected)
# 3. run the checks
uv run --with httpx python tests/acceptance/run_acceptance.py
Expected output ends with ALL ACCEPTANCE CRITERIA PASSED. See tests/acceptance/README.md for what each criterion verifies (C3 is the headline test: 50 sequential searches with 0 errors and 0 silent-empty result sets).
STORM adapter (C5) — unit test:
cd adapters && python -m pytest tests/test_storm.py
Server + cloak suites:
cd server && uv run pytest # full server suite
BROWSER_RELAY_RUN_CLOAK_TESTS=1 uv run pytest # include live cloak tests
Extension unit tests:
npm test
Packaging the extension
bash scripts/package-extension.sh
# wrote dist/browser-relay-extension.zip
This regenerates the injected bundles (npm run build:inject) and zips extension/ (tests excluded) for the Chrome Web Store or manual install. dist/ is gitignored.
Configuration
| Env var | Default | Purpose |
|---|---|---|
BROWSER_RELAY_URL |
http://127.0.0.1:15552 |
Relay base URL the MCP server talks to |
BROWSER_RELAY_FETCH_CAP |
5 |
Max parallel fetch tabs |
BROWSER_RELAY_SEARCH_CONCURRENCY |
1 |
Concurrent searches (near-serial by design) |
BROWSER_RELAY_SEARCH_MIN_SPACING_MS |
500 |
Minimum spacing between search dispatches |
BROWSER_RELAY_ACTION_TTL |
300 |
Seconds a paused (action_required) request stays resumable |
BROWSER_RELAY_DEFAULT_ENGINE |
bing |
Default search engine |
BROWSER_RELAY_CLOAK_PROFILE_DIR |
user cache dir | Persistent profile for the cloak browser |
推荐服务器
Baidu Map
百度地图核心API现已全面兼容MCP协议,是国内首家兼容MCP协议的地图服务商。
Playwright MCP Server
一个模型上下文协议服务器,它使大型语言模型能够通过结构化的可访问性快照与网页进行交互,而无需视觉模型或屏幕截图。
Magic Component Platform (MCP)
一个由人工智能驱动的工具,可以从自然语言描述生成现代化的用户界面组件,并与流行的集成开发环境(IDE)集成,从而简化用户界面开发流程。
Audiense Insights MCP Server
通过模型上下文协议启用与 Audiense Insights 账户的交互,从而促进营销洞察和受众数据的提取和分析,包括人口统计信息、行为和影响者互动。
VeyraX
一个单一的 MCP 工具,连接你所有喜爱的工具:Gmail、日历以及其他 40 多个工具。
graphlit-mcp-server
模型上下文协议 (MCP) 服务器实现了 MCP 客户端与 Graphlit 服务之间的集成。 除了网络爬取之外,还可以将任何内容(从 Slack 到 Gmail 再到播客订阅源)导入到 Graphlit 项目中,然后从 MCP 客户端检索相关内容。
Kagi MCP Server
一个 MCP 服务器,集成了 Kagi 搜索功能和 Claude AI,使 Claude 能够在回答需要最新信息的问题时执行实时网络搜索。
e2b-mcp-server
使用 MCP 通过 e2b 运行代码。
Neon MCP Server
用于与 Neon 管理 API 和数据库交互的 MCP 服务器
Exa MCP Server
模型上下文协议(MCP)服务器允许像 Claude 这样的 AI 助手使用 Exa AI 搜索 API 进行网络搜索。这种设置允许 AI 模型以安全和受控的方式获取实时的网络信息。