Skip to main content
Glama
kuraneko1
by kuraneko1

chrome-web-mcp

日本語版 README

Supported OS: Linux only. Windows and macOS are not supported. Using Docker does not change the host OS support policy.

CAUTION

Single-session use only: one client process, one browser, sequential searches.

  • Normal use never trips the limiter: pace_warning fires 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 the chrome-web-mcp window yourself, then retry. last_captcha_at in health_check shows 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_check for 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

  • Xephyr on Linux for the default visible-window mode

  • Xvfb on Linux for hidden-window mode

  • xpra on 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.whl

MCP 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-mcp with your checkout path.

  • CW_CHROME=/usr/bin/chromium only 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.

  • xephyr mode needs a real desktop DISPLAY plus xserver-xephyr; on a headless host or over SSH without X forwarding it will not start. Default xvfb mode needs no DISPLAY.

Install and run

From a built wheel:

python -m pip install chrome_web_mcp-0.2.0-py3-none-any.whl
chrome-web-mcp

For 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: true

For 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-mcp

This 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/main

reset --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-mcp

For 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-*.whl

For 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-mcp

Verify 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 MB

  • This 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.

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: true shows the browser window; false hides it.

  • hl: Google interface language. ja is Japanese and en is English.

  • gl: Google result region. jp is Japan and us is 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.json is 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 when CW_PROFILE_DIR is 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. Defaults 1.0 / 2.5.

  • CW_DISPLAY_MODE — advanced environment override for show_browser: xvfb runs Chrome on a private, fully hidden display, while xephyr runs Chrome inside a nested Xephyr window titled chrome-web-mcp on your desktop. The latter is visible, minimizable, and movable, but tool calls can never pop a window to the front outside of it. Requires Xephyr (xserver-xephyr) and a user DISPLAY.

  • When CW_DISPLAY_MODE=xephyr, the server preserves an explicit XAUTHORITY or automatically discovers Mutter's .mutter-Xwaylandauth.* file under XDG_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 to 1 to 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.

Maintenance

ActivityMaintained
ResponsivenessNo issues

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

Related MCP Servers

  • A
    license
    Not graded
    quality
    D
    maintenance
    Enables 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.
    16
    MIT
  • A
    license
    Not graded
    quality
    B
    maintenance
    Fetches web pages with JavaScript rendering, pierces Shadow DOM, and enables interactive actions like clicking and form filling using a real Chrome browser.
    2
    MIT
  • A
    license
    Not graded
    quality
    C
    maintenance
    Enables 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

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