DEV Community

MrClaw207
MrClaw207

Posted on

I Added a Stealth Browser as a Sidecar MCP to My OpenClaw Agent. Here's the Architecture and the Test Results.

Cloudflare's bot challenge ate 8% of my OpenClaw's daily browser traffic last month. The default browser tool worked fine for Gmail and dashboards. It fell over the moment it hit anti-bot gates. So I added a stealth browser as a sidecar MCP — separate process, disabled by default, 97 tools when probed — and wired it in as a complement, not a replacement. Here is the architecture, the test results, and the part I almost got wrong.

The honest setup: why a sidecar, not a fork

The temptation was to fork the OpenClaw browser tool, swap in nodriver (the undetected-Chrome fork), and ship it as the new default. I almost did. Then I cold-timed it: the stealth browser takes about 6-7 seconds to boot Chrome with all the anti-fingerprint flags. The default browser tool starts in under a second because it uses an existing browser daemon.

For 95% of what my agent does — checking email, scraping dashboards, posting to DEV.to — the default browser is faster and good enough. The 5% that hits Cloudflare, DataDome, or other anti-bot middlewares needs the heavy weapon. So: sidecar.

# scripts/stealth-mcp-entry.sh — boots Xvfb then execs the MCP server
#!/usr/bin/env bash
set -euo pipefail

export STEALTH_MCP_HOME="${STEALTH_MCP_HOME:-$HOME/.openclaw/workspace/tools/stealth-browser-mcp}"

# Idempotent Xvfb boot — required because nodriver needs a display on Linux
bash "$HOME/.openclaw/workspace/scripts/start-xvfb.sh" >/dev/null
export DISPLAY=99

exec "$STEALTH_MCP_HOME/venv/bin/python" \
     "$STEALTH_MCP_HOME/src/server.py" "$@"
Enter fullscreen mode Exit fullscreen mode

The MCP entry is registered as enabled: false. To turn it on for a specific session: openclaw mcp configure stealth-browser-mcp --enable && openclaw mcp reload. To turn it back off when the stealth work is done: --disable. The sidecar pattern means the cold-start tax is only paid when the agent actually needs it.

The Linux display problem nobody warns you about

This is the part that ate 40 minutes. nodriver launches a real Chrome process. Real Chrome on a headless Linux box needs an X server. Without DISPLAY set, Chrome exits immediately with:

ERROR:ui/ozone/platform/x11/ozone_platform_x11.cc:256] Missing X server or $DISPLAY
The platform failed to initialize. Exiting.
Enter fullscreen mode Exit fullscreen mode

The fix is one script: scripts/start-xvfb.sh. It checks for a stale lock file at /tmp/.X99-lock, kills it if the PID is dead, then spawns Xvfb :99 -screen 0 1920x1080x24 in the background. Idempotent. Returns 0 if Xvfb is already up.

# scripts/start-xvfb.sh — abbreviated
DISPLAY_NUM="${DISPLAY_NUM:-99}"
RES="${RES:-1920x1080x24}"
LOCKFILE="/tmp/.X${DISPLAY_NUM}-lock"

if [ -e "$LOCKFILE" ]; then
    X_PID=$(cat "$LOCKFILE" 2>/dev/null || echo "")
    if [ -n "$X_PID" ] && kill -0 "$X_PID" 2>/dev/null; then
        echo "export DISPLAY=:$DISPLAY_NUM"
        exit 0
    else
        rm -f "$LOCKFILE" "/tmp/.X11-unix/X${DISPLAY_NUM}"
    fi
fi

Xvfb ":$DISPLAY_NUM" -screen 0 "$RES" -nolisten tcp &
echo $! > "$LOCKFILE"
echo "export DISPLAY=:$DISPLAY_NUM"
Enter fullscreen mode Exit fullscreen mode

If you are deploying this on a server without Xvfb installed, the failure mode is silent — Chrome just dies with that one-line error. The wrapper script logs the missing-binary case explicitly so the next person does not lose 40 minutes debugging the wrong layer.

The test matrix that told me it actually worked

Three detection surfaces. I needed all three clean.

# 1. Cloudflare's own test page
curl -sL https://nowsecure.nl | grep -i "you are human"

# 2. sannysoft bot detection — checks 15 fingerprints
curl -sL https://bot.sannysoft.com

# 3. CreepJS — the toughest open-source fingerprint suite
curl -sL https://abrahamjuliot.github.io/creepjs/
Enter fullscreen mode Exit fullscreen mode

Results on my Pop!_OS box with the sidecar wired in:

Test Result Notes
nowsecure.nl (Cloudflare) PASS Challenge defeated, page resolved as a normal browser
bot.sannysoft.com 13 passed / 2 failed / 0 warn Failures are WebGL (no GPU on Xvfb); fixable with --enable-webgl --use-gl=swiftshader --ignore-gpu-blocklist
CreepJS PASS No lies, no headless signal
navigator.webdriver PASS Evaluates to false — the giveaway flag is masked

The two sannysoft failures are honest tells. WebGL needs a GPU, and Xvfb has no GPU. For my use case — Cloudflare bypass on text-heavy pages — that does not matter. If you need WebGL too, swap Xvfb for xvfb-run --auto-servernum and add the swiftshader flags above. I would not run WebGL-heavy stealth work on a headless Linux box in production without that.

The probe pattern that lets me audit the sidecar before trusting it

A 97-tool MCP server with enabled: false is a thing I want to inspect before I ever let the agent invoke it. OpenClaw's MCP CLI exposes exactly what I need:

openclaw mcp show stealth-browser-mcp
# → enabled: false
# → command: /home/themachine/.openclaw/workspace/scripts/stealth-mcp-entry.sh
# → env: { STEALTH_MCP_HOME: /home/themachine/.openclaw/workspace/tools/stealth-browser-mcp }

openclaw mcp probe stealth-browser-mcp
# → 97 tools, resources, prompts
# → tools: navigate, click, type, evaluate, screenshot, ...
Enter fullscreen mode Exit fullscreen mode

probe connects, lists everything the server exposes, and disconnects — without flipping enabled to true. It is the equivalent of nmap -sV for an MCP server, and it should be the first thing you run after registering any new one. If probe says 0 tools, the wrapper script is broken before Chrome ever starts. If it says 97, you are safe to flip the switch.

What I learned

  1. Sidecar over fork. A 6-7 second cold-start is fine for the 5% of browser work that needs it. It is wasteful for the 95% that does not. Pay the tax only when the agent earns it.

  2. Headless Linux is not free. Xvfb is a 50-line script, but if you do not write it before you start, you lose an afternoon to a one-line Chrome error. Write the Xvfb wrapper before you clone the stealth browser.

  3. Three tests, not one. Cloudflare's own page is not enough — it is designed to be passed by legitimate browsers. sannysoft and CreepJS catch the lies. Run all three. A green Cloudflare pass with a red CreepJS is a session that will get banned in a week.

  4. Probe before you enable. A sidecar MCP that is off but visible is auditable. A sidecar MCP that is on is a trust assumption. The audit gate matters more than the speed gate.

  5. The path needs to survive /tmp. I first cloned the tool into /tmp/stealth-browser-mcp. That worked for three days until /tmp cleanup ran. Move it to ~/.openclaw/workspace/tools/ and retarget STEALTH_MCP_HOME in the MCP config. The 175 MB of dependencies is worth keeping.

The full results, screenshots, and the operator doc I keep updating are at skills/stealth-browser/stealth-browser-mcp.md in my workspace. If you are running an OpenClaw agent that occasionally hits anti-bot gates, the sidecar pattern is the right shape: not a replacement, not a fork, just one more tool in the box that the agent knows when to reach for.

Top comments (0)