chrome-web-mcp
Allows performing Google web searches through a JavaScript-rendered Chrome instance, returning readable markdown results with language/region hints and CAPTCHA status.
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., "@chrome-web-mcpSearch Google for 'MCP server best practices' and extract text from the first result"
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.
chrome-web-mcp
Supported OS: Linux only. Windows and macOS are not supported. Using Docker does not change the host OS support policy.
Single-session use only: one client process, one browser, sequential searches.
Normal use never trips the limiter:
pace_warningfires only at 15+ searches per minute. Sequential or lightly-parallel use stays far below it.A warning is advisory, not a block: searches keep running. But Google counts total volume too — sustained barrages end in CAPTCHA regardless of pacing (observed after dozens of searches in one session). When challenged: wait a few minutes and retry in headless (
xvfb) mode — it usually clears by itself; in visible (xephyr) mode, solve the challenge in thechrome-web-mcpwindow yourself, then retry.last_captcha_atinhealth_checkshows the last hit.opencode + subagents: SAFE. All agents share one server process and one browser. Concurrent searches are pooled in a shared query queue (per-process lock + SQLite pacing) and executed sequentially — verified with 3 simultaneous subagents and 35 rapid searches, no CAPTCHA.
Separate processes at once are NOT absorbed: each process spawns its OWN browser, so parallel CLIs look like fresh users hitting Google together. Each one can be CAPTCHA-challenged independently (observed). Stagger starts by seconds or raise
CW_MIN_DELAY/CW_MAX_DELAY.
A stdio Model Context Protocol server that exposes focused browser tools:
google_search— Google search through a JavaScript-rendered Chrome instance.fetch_url— JavaScript-rendered readable text extraction for public HTTP(S) URLs.health_check— display, browser, queue, and CAPTCHA status.
The server is designed to be configured by any MCP client that can launch a stdio command.
Features
JS-rendered Google search + public URL fetch through a real (non-headless) Chrome on a private virtual display (Xephyr when visible, Xvfb when hidden) — harder to bot-detect than
--headless.Shaped markdown by default (
trafilatura+html2text, pure-Python, no extra service), with full-text fallback and follow-up link targets.Language/region hints (
hl/gl) for reproducible JA/EN results.Parallel-safe: concurrent searches and fetches serialize only where the browser lifecycle requires it; each fetch uses its own tab.
Fail-closed fetching: private networks, metadata hosts, and credential-bearing URLs are blocked, including post-redirect targets.
Shared SQLite pacing for Google searches across MCP processes.
health_checkfor display/browser/queue/CAPTCHA observability.Linux only (Xvfb/Xephyr,
fcntl, process groups).
Related MCP server: MCP WebSearch Server
Requirements
Python 3.10 or newer
Google Chrome, Google Chrome for Testing, or Chromium
Xephyron Linux for the default visible-window modeXvfbon Linux for hidden-window modexpraon Linux if interactive CAPTCHA recovery is desired
The Python dependencies are installed with the package. Chrome and Xvfb remain host prerequisites because they are external browser processes.
Non-headless Chrome on Xvfb is intentional: --headless is easier to
bot-detect, so Xvfb is kept as a requirement even though it is heavier.
Platform support
Linux only. Windows and macOS are not supported: this server depends on
Xvfb/Xephyr, Chromium with --ozone-platform=x11, fcntl.flock, and
process-group signaling (killpg), none of which work as-is on Windows.
A Windows/macOS port would need headless Chrome plus a different locking
scheme. Docker helps only on a Linux host with an X server for xephyr mode.
Headless environments
On a headless server, CI runner, SSH session without X forwarding, or any
machine without a desktop display, set show_browser to false:
{
"show_browser": false
}Edit ~/.config/chrome-web-mcp/config.json (or the file specified by
CW_CONFIG) and restart the MCP client. This forces Chrome onto hidden Xvfb
and avoids the Xephyr startup error caused by the lack of a user DISPLAY.
The built-in default is true for desktop use, so do not omit this setting on
a headless machine. A Docker installation already sets
CW_DISPLAY_MODE=xvfb inside the container, so it normally needs no host
display or extra setting.
Linux quickstart (Debian/Ubuntu, copy-paste)
# 1. System dependencies
sudo apt update && sudo apt install -y chromium xvfb xserver-xephyr x11-utils python3-venv
which chromium || which google-chrome || which chromium-browser
which Xvfb
python3 --version # 3.10+
# 2. Create an environment and install the package (either one)
python3 -m venv .venv
. .venv/bin/activate
python3 -m pip install -e '.'
# or: python -m pip install chrome_web_mcp-0.2.0-py3-none-any.whlMCP handshake check (TOOLS: ['fetch_url', 'google_search', 'health_check'] expected):
uv run python -c "
import asyncio
from mcp import ClientSession, StdioServerParameters
from mcp.client.stdio import stdio_client
async def main():
params = StdioServerParameters(command='uv', args=['run','--directory','/absolute/path/to/chrome-web-mcp','chrome-web-mcp'])
async with stdio_client(params) as (read, write):
async with ClientSession(read, write) as session:
await session.initialize()
print('TOOLS:', [t.name for t in (await session.list_tools()).tools])
asyncio.run(main())
"opencode (~/.config/opencode/opencode.json, Linux path example):
{
"mcp": {
"chrome-web": {
"type": "local",
"command": ["uv", "run", "--directory", "/home/user/projects/chrome-web-mcp", "chrome-web-mcp"],
"enabled": true
}
}
}Notes:
Replace
/absolute/path/to/chrome-web-mcpwith your checkout path.CW_CHROME=/usr/bin/chromiumonly if auto-detection misses your binary.Concurrent calls within one server are supported. Searches are serialized; fetches use independent tabs and share a serialized browser startup.
xephyrmode needs a real desktopDISPLAYplusxserver-xephyr; on a headless host or over SSH without X forwarding it will not start. Defaultxvfbmode needs noDISPLAY.
Install and run
From a built wheel:
python -m pip install chrome_web_mcp-0.2.0-py3-none-any.whl
chrome-web-mcpFor a published package, an MCP client can let uvx install it on demand:
{
"mcpServers": {
"chrome-web": {
"command": "uvx",
"args": ["--from", "chrome-web-mcp==0.2.0", "chrome-web-mcp"]
}
}
}For a local checkout:
{
"mcpServers": {
"chrome-web": {
"command": "uv",
"args": ["run", "--directory", "/absolute/path/to/chrome-web-mcp", "chrome-web-mcp"]
}
}
}The same server can be registered in Hermes with YAML:
mcp_servers:
chrome-web:
command: /absolute/path/to/python
args:
- -m
- chrome_web_mcp
timeout: 120
connect_timeout: 60
enabled: trueFor a wheel installation, use the Python interpreter from the environment where the wheel was installed. MCP clients should launch the process over stdio and must not add shell-specific quoting around the arguments.
Docker
The image bundles Python, dependencies, Chromium, Xvfb (plus Xephyr/xdotool for visible-window mode), so users only need Docker:
docker build -t chrome-web-mcp .{
"mcpServers": {
"chrome-web": {
"command": "docker",
"args": ["run", "-i", "--rm", "chrome-web-mcp"]
}
}
}For opencode, add a separate entry to ~/.config/opencode/opencode.json so
you can keep the local and Docker versions available side by side:
"chrome-web-docker": {
"type": "local",
"command": ["docker", "run", "--rm", "-i", "chrome-web-mcp:latest"],
"enabled": true,
"timeout": 120000
}The Docker image uses hidden Xvfb by default. This is separate from the local
chrome-web entry, whose show_browser setting can display a desktop window.
Restart opencode after adding or changing the entry.
Hidden mode is normally sufficient. If Google returns
captcha_required: true, the CAPTCHA is displayed inside the Docker
container's browser, so the local chrome-web window cannot solve it. Start
the Docker MCP server in visible Xephyr mode instead and retry the search:
docker run -i --rm \
-e DISPLAY=$DISPLAY \
-e CW_DISPLAY_MODE=xephyr \
-e XAUTHORITY=$XAUTHORITY \
-v /tmp/.X11-unix:/tmp/.X11-unix \
-v "$XAUTHORITY":"$XAUTHORITY":ro \
chrome-web-mcpThis requires a Linux desktop X display and Xephyr. On Wayland/Mutter, the
working Xauthority file may be a .mutter-Xwaylandauth.* file under
$XDG_RUNTIME_DIR rather than $XAUTHORITY; mount that directory and set
XAUTHORITY to the matching path inside the container. The resulting
chrome-web-mcp window belongs to the Docker process, and you must retry using
that same Docker MCP session. For the simplest CAPTCHA recovery, use the local
chrome-web entry with show_browser: true from the beginning.
Image size is about 1.5 GB (mostly Chromium and fonts).
Update
For a local checkout, pull and re-sync (restart the MCP client afterwards):
git -C /absolute/path/to/chrome-web-mcp fetch origin
git -C /absolute/path/to/chrome-web-mcp reset --hard origin/mainreset --hard is used instead of pull --ff-only because history is
occasionally force-pushed; stash local changes first if you have any
(git stash). uv run --directory ... chrome-web-mcp picks up the new
dependencies automatically on next launch — no reinstall step needed.
For a dedicated venv (e.g. ~/.local/share/chrome-web-mcp/venv), reinstall
after pulling:
uv pip install --python ~/.local/share/chrome-web-mcp/venv/bin/python -U -e /absolute/path/to/chrome-web-mcpFor a wheel install, rebuild and reinstall:
python -m build --wheel --outdir /absolute/path/to/chrome-web-mcp/dist /absolute/path/to/chrome-web-mcp
python -m pip install -U /absolute/path/to/chrome-web-mcp/dist/chrome_web_mcp-*.whlFor Docker, just rebuild — no Dockerfile edit is needed (it copies
pyproject.toml + src/ and runs pip install ., so code and dependency
changes are picked up automatically):
docker build -t chrome-web-mcp /absolute/path/to/chrome-web-mcpVerify after update:
uv pip list --python ~/.local/share/chrome-web-mcp/venv/bin/python | grep -E "chrome-web|trafilatura"Install size (rough, Debian host)
Python environment (
.venv, incl. trafilatura/html2text): ~100 MBThis package source: under 1 MB
Chromium set: ~485 MB
Xvfb + x11-utils: ~5 MB
Total: ~600 MB, dominated by Chromium. No new system packages were added for markdown shaping (pure-Python dependencies only).
Tools
Workflow: first google_search, then fetch_url on interesting result URLs
for full text. health_check reports server state without starting a browser.
google_search
Input:
{"query": "search terms", "limit": 5, "hl": "ja", "gl": "jp"}limit is an integer from 1 to 20. Results are returned as structured JSON
with title, url, description, and position fields, plus waited_ms
(the shared rate-limiter queue wait). hl/gl are optional Google language
(ja/en) and region (jp/us) hints; defaults preserve Japanese results.
query is required, max 512 chars. Searches run one at a time per process
and are paced across processes (see Runtime configuration). Bursts of 15+
searches per minute return a pace_warning — slow down or batch queries to
avoid a Google CAPTCHA; health_check reports the recent count.
fetch_url
Input:
{"url": "https://example.com", "char_limit": 15000, "format": "markdown"}Only public http:// and https:// URLs without embedded credentials are
accepted. Localhost, private IP ranges, metadata hosts, and non-public DNS
resolutions are rejected. A mandatory local HTTP proxy validates every upstream
connection and connects to the validated numeric address, including redirects,
subresources, WebSockets, and DNS changes. Chrome's loopback proxy bypass, QUIC,
and non-proxied WebRTC UDP are disabled. IPv6 translation/tunneling addresses
(NAT64, 6to4, Teredo) are also rejected. char_limit is an integer from 100 to
200000. HTML/text snapshots are read in chunks, with a 16Mi-character extraction
limit independent of the returned text limit. Text is cut at a
sentence boundary when possible. Returns requested_url, final_url,
redirected, total_chars, and truncated alongside title and content
(url mirrors final_url for compatibility). format defaults to
markdown: shaped readable markdown with boilerplate removed
(trafilatura, html2text fallback). The response reports formatted: true
and the extraction method, so agents can tell it was shaped — if content
looks missing, retry with format: "text" for the full rendered text.
format: "links" adds follow-up link targets. Concurrent fetches are
parallel-safe; each uses its own tab. url is required, max 2048 chars.
Failures return {"success": false, "error": "..."} (plus
"captcha_required": true for Google challenges). This shaping reuses the same
trafilatura + html2text approach as a self-hosted jina-compatible Reader,
without needing the extra service.
health_check
Input: {} (no arguments).
Returns display_mode, browser/process liveness, the rate-limiter queue wait,
its min/max delays, and the last CAPTCHA time.
Runtime configuration
Most users only need the JSON file:
mkdir -p ~/.config/chrome-web-mcp
cp examples/config.json examples/config.md ~/.config/chrome-web-mcp/Then edit ~/.config/chrome-web-mcp/config.json and restart the MCP client.
The settings most people change are:
{
"show_browser": true,
"hl": "ja",
"gl": "jp"
}show_browser:trueshows the browser window;falsehides it.hl: Google interface language.jais Japanese andenis English.gl: Google result region.jpis Japan andusis the United States.
The built-in search defaults are hl: ja and gl: jp, but each user can set
their own values in this file. For example, use "hl": "en" and
"gl": "us" for English/US-oriented Google results. See
examples/config.md for all settings and examples.
The JSON file is optional. If it is absent, the built-in defaults are used and
the browser window is shown (show_browser: true). Set it to false to hide
the window. JSON comments are not supported, so keep config.json as plain
JSON.
Optional environment variables:
CW_CONFIG— path to an optional JSON config file for window on/off and tool defaults. When unset,~/.config/chrome-web-mcp/config.jsonis used if it exists. Explicit environment variables below win over the file. Settings become tool defaults when the caller omits them. Unknown keys warn on stderr; invalid values warn and keep the built-in default per key.CW_CHROME— explicit Chrome/Chromium executable path.CW_PROFILE_DIR— explicit browser profile directory. By default, each server process uses an isolated per-PID temporary profile.CW_LOCK_PATH— explicit lock-file path whenCW_PROFILE_DIRis set.CW_RATE_LIMIT_DB— shared SQLite path for the Google-search start-slot queue. By default it is/tmp/chrome-web-mcp/search-rate-limit.sqlite3, so separate MCP processes of the same user share one limiter.CW_MIN_DELAY/CW_MAX_DELAY— randomized gap (seconds) between Google search starts. Defaults1.0/2.5.CW_DISPLAY_MODE— advanced environment override forshow_browser:xvfbruns Chrome on a private, fully hidden display, whilexephyrruns Chrome inside a nestedXephyrwindow titledchrome-web-mcpon your desktop. The latter is visible, minimizable, and movable, but tool calls can never pop a window to the front outside of it. RequiresXephyr(xserver-xephyr) and a userDISPLAY.When
CW_DISPLAY_MODE=xephyr, the server preserves an explicitXAUTHORITYor automatically discovers Mutter's.mutter-Xwaylandauth.*file underXDG_RUNTIME_DIR, then falls back to~/.Xauthority. This lets stdio MCP clients that filter their inherited environment still connect to the user's Xwayland display.CW_XPRA_EXPOSE— set to1to re-enable automatic Xpra attach when a CAPTCHA appears. Off by default: automatic attach once crashed the desktop session, so the server only reports the CAPTCHA and leaves the browser where it is.
The default per-process profile prevents separate MCP clients from contending for one Chrome profile. Do not share a profile between live server processes unless that is intentional.
Google-search calls also reserve a slot in the shared SQLite queue. Separate
MCP processes therefore start searches one at a time with a 1.0–2.5 second
randomized gap. A reserved slot is not retried automatically if Google returns
an error; the tool returns the error to the MCP client. fetch_url is not put
through this Google-search queue.
Security and scope
The server exposes no arbitrary page-context JavaScript tool.
Browser HTTP(S)/WebSocket connections are restricted to public addresses by a local validating proxy; private destinations are rejected before connecting. Input URLs also reject embedded credentials and recognizable secret patterns. This does not classify every possible secret in arbitrary page URLs/content.
Each server owns and cleans up its Chrome and Xvfb process groups.
Chrome is explicitly forced onto the private X11/Xvfb display, even when the host desktop session uses Wayland; the user desktop should not be surfaced.
SIGTERM and SIGINT trigger browser and temporary-profile cleanup before exit. Startup recovers recorded Chrome/display processes using PID and start time, not broad process-name matching. Legacy profiles without process records can recover Chrome by exact profile argument; their Xvfb cannot be safely identified.
Google result URLs are normalized and deduplicated before returning.
This package does not bypass authentication or CAPTCHA challenges. It is a browser-backed search/fetch MCP server, not a general remote browser-control API.
When Google presents a CAPTCHA during google_search, the server returns
captcha_required: true. With show_browser: true (Xephyr), solve the
challenge in the chrome-web-mcp window on your desktop, then retry the same
search. With show_browser: false (Xvfb), wait a while and retry. Automatic
Xpra attach is disabled unless CW_XPRA_EXPOSE=1 is set, because it once
crashed the desktop session.
Development
See CONTRIBUTING.md for setup, test (pytest -q -m 'not live' for deterministic only), and build steps.
Tool Schema Changelog
Recent tool additions, removals, and schema changes observed during successful MCP inspections. Dates show when Glama detected each change.
No tool schema history has been recorded yet.
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.
Related MCP Connectors
Crawl, scrape, search the web, and automate browsers at scale with anti-bot bypass.
Hosted real Google Chrome MCP with per-user persistent state. Navigate, click, type, screenshot.
Stealth scraping & search. Bypasses Cloudflare, DataDome & LinkedIn via Cyborg HITL approach.
Run multi-step tasks in a real Chrome browser: persistent environments, live view, human takeover.
Related MCP Servers
- AlicenseNot gradedqualityNot gradedmaintenanceProvides Google search capabilities, web content extraction, and screenshot functionality with advanced bot detection avoidance through the MCP protocol.276-
- AlicenseNot gradedqualityDmaintenanceEnables web searching via Google Search and AI Mode, plus advanced web scraping capabilities including content extraction in multiple formats, link extraction, and batch scraping of multiple URLs.16MIT
- AlicenseNot gradedqualityBmaintenanceFetches web pages with JavaScript rendering, pierces Shadow DOM, and enables interactive actions like clicking and form filling using a real Chrome browser.2MIT
- AlicenseNot gradedqualityCmaintenanceEnables agents to take screenshots and extract text from web pages using a persistent Chrome browser via CDP, with built-in SSRF protection.MIT
Latest Blog Posts
- Who's Calling? MCP Hosts Are an Identity Blind Spot (And the Spec Knows It)By Om-Shree-0709 on .mcpAgent IdentityOAuth 2.1
- 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/kuraneko1/chrome-web-mcp'
If you have feedback or need assistance with the MCP directory API, please join our Discord server