ClaudeCodeMod

All shelves / MCP servers

Stealthy Auto Browse

io.github.psyb0t/stealthy-auto-browse · 89 stars · Python · WTFPL

MCP server Self-hosted MCP server for stealth browser automation with human-like OS-level input.

Install

The repo has no one-line install. Follow its README.

Published as

  • Dockerdocker.io/psyb0t/stealthy-auto-browse:v3.0.2Streamable HTTP

From the server's entry in the official MCP Registry.

Open the repo

Files

README.md

docker-stealthy-auto-browse

CI version license Docker Pulls

Stealth browser automation that actually works. Runs Camoufox (custom Firefox) in Docker with zero Chrome DevTools Protocol exposure, real OS-level mouse and keyboard input via PyAutoGUI, and a JSON HTTP API + MCP server to control it all remotely. Watch it live via noVNC. Run a single instance or spin up a cluster behind HAProxy with Redis cookie sync, request queuing, and sticky sessions. Drive it with curl, pipe YAML scripts through stdin, send multi-step scripts via the API, use page loaders to auto-handle popups and paywalls, or connect AI agents directly via MCP. Optional Bearer token auth via AUTH_TOKEN.

The image avoids Chromium CDP signals and keeps its generated Linux font and WebGL fingerprints internally consistent. Detection results still depend on the site, network exit, timezone, browser build, and test date.

Table of contents

What's inside

Component What it does
Camoufox A custom build of Firefox with zero Chrome DevTools Protocol exposure. Bot detectors look for CDP signals, and this browser doesn't have any.
Xvfb Virtual framebuffer that lets the browser run with a full graphical display inside a container, no physical monitor needed. This matters because headless mode is another detection signal.
PyAutoGUI Generates real OS-level mouse movements and keystrokes. The browser receives these as genuine user input. It has no idea it's being automated.
noVNC Web-based VNC client so you can watch the browser in real time from your own browser. Great for debugging and seeing exactly what's happening.
Openbox Lightweight window manager. It adds title bars and resize handles to popup windows (OAuth dialogs, etc.) that would otherwise be too small to interact with. Zero stealth impact.
HTTP API A JSON API on port 8080 that lets you control everything: navigate pages, click elements, type text, take screenshots, manage tabs, handle cookies, and more.
MCP Server Model Context Protocol server at /mcp on the same port. AI agents (Claude, etc.) can drive the browser directly over MCP using Streamable HTTP.
ffmpeg x11grab against Xvfb for screen recording. Captures actual rendered pixels including the OS-level mouse cursor. See Screen Recording.

Pre-installed extensions: uBlock Origin (ads/trackers), LocalCDN (prevents CDN tracking), ClearURLs (strips tracking params), Consent-O-Matic (auto-handles cookie popups).

Quick start

docker run -d --name browser \
  -p 8080:8080 \
  -p 5900:5900 \
  psyb0t/stealthy-auto-browse

Port 8080 is the HTTP API, port 5900 is the VNC viewer (http://localhost:5900/).

# Navigate
curl -X POST http://localhost:8080 \
  -H "Content-Type: application/json" \
  -d '{"action": "goto", "url": "https://example.com"}'

# Get page text
curl -X POST http://localhost:8080 \
  -H "Content-Type: application/json" \
  -d '{"action": "get_text"}'

# Click by CSS selector (preferred: fast and reliable)
curl -X POST http://localhost:8080 \
  -H "Content-Type: application/json" \
  -d '{"action": "click", "selector": "button#submit"}'

# Screenshot (last resort; prefer get_text and always resize to save tokens)
curl "http://localhost:8080/screenshot/browser?whLargest=512" -o screenshot.png

Run multi-step scripts in one request:

curl -X POST http://localhost:8080 \
  -H "Content-Type: application/json" \
  -d '{
    "action": "run_script",
    "steps": [
      {"action": "goto", "url": "https://example.com", "wait_until": "domcontentloaded"},
      {"action": "sleep", "duration": 2},
      {"action": "get_text", "output_id": "text"},
      {"action": "eval", "expression": "document.title", "output_id": "title"}
    ]
  }'

Also accepts "yaml": "..." with the same YAML format used in script mode. In single-instance mode, the server serializes requests automatically. Send multiple scripts in parallel and they queue up.

See docs/api.md for all actions and the full API reference.

Navigation uses app-owned controls, not a hidden browser-library timeout. Each attempt gets 30 seconds by default, one timeout retry, and a one-second retry delay. Pass timeout, retry_count, and retry_delay with goto, refresh, or new_tab when a workflow needs different bounds. retry_count: 0 disables retries. See navigation controls for the limits and retry behavior.

Two input modes

There are two ways to interact with pages. System input uses PyAutoGUI to generate real OS-level mouse and keyboard events. The browser cannot tell these apart from a real human. Playwright input uses CSS selectors and DOM event injection. It's easier, but theoretically detectable by behavioral analysis. Use system input on any site with bot protection.

System input is modeled on how people actually move and type, fitted to published human datasets: curved mouse paths with Fitts's-law timing and overshoot-and-correct, ~100 ms button holds on off-centre click points, key-pair-dependent typing rhythm with real key holds and Shift before capitals, optional self-correcting typos, and wheel gestures with reading pauses. The defaults look like one consistent person per container, and the main timings can be overridden per request. See Humanized Input.

Full breakdown and usage guide: docs/stealth.md

Virtual camera & microphone

Mount test media read-only at /media and set VIRTUAL_CAMERA_FILE and/or VIRTUAL_MICROPHONE_FILE. Pages that call navigator.mediaDevices.getUserMedia() receive tracks captured from those files, so camera and microphone checks can run without host hardware.

docker run -d -p 8080:8080 \
  -v ./media:/media:ro \
  -e VIRTUAL_CAMERA_FILE=camera.webm \
  -e VIRTUAL_MICROPHONE_FILE=microphone.wav \
  psyb0t/stealthy-auto-browse

Sources must remain inside /media; restart the browser after changing them. A request for a kind without a configured virtual source fails with NotFoundError rather than falling back to hardware. Virtual tracks use the source file's native format, so pages must not require incompatible exact media constraints. This virtualizes getUserMedia() only, not enumerateDevices().

To switch sources during an authorized test without replacing an already acquired camera or microphone track, enable VIRTUAL_MEDIA_DYNAMIC=true. Dynamic mode is off by default. Use set_virtual_media_source to choose an existing relative file name under VIRTUAL_MEDIA_DIR, or upload_virtual_media to add bounded base64 content and optionally activate it. An upload filename is only a safe, type-matching media name; the service generates a collision-safe stored basename, returns it, and never overwrites an existing named source. Before storage or activation, the service checks the decoded upload with ffprobe for a stream matching the requested camera or microphone kind. The media directory must be writable for uploads; VIRTUAL_MEDIA_UPLOAD_MAX_BYTES defaults to 50 MiB. Existing page streams keep their track identities while the source changes.

Dynamic mode accepts files from the configured media directory only. It does not accept arbitrary host paths, remote URLs, WebSocket streams, or other live ingress. Both actions use the normal API authentication. When AUTH_TOKEN is set, send the usual Authorization: Bearer <token> header. See docs/api.md#virtual-camera-and-microphone and docs/configuration.md for the action contract and writable-volume setup.

MCP server

AI agents can control the browser over the Model Context Protocol via Streamable HTTP at /mcp on the same port 8080. The server exposes all browser actions as MCP tools: navigation, screenshots, clicking, typing, JavaScript evaluation, cookies, and more.

For authorised test flows that need a human review when a verification widget appears, use detect_challenge. It is read-only. It reports a best-effort absent, present, or unknown status with bounded vendor/location evidence, but never clicks, enters a frame, or solves a challenge. Pass scroll_into_view: true to bring the first visible detected frame or widget into the viewport for VNC handoff; it still never clicks or focuses it. In cluster mode, include it as a run_script step. See the API reference.

Connect any MCP-compatible client (Claude Desktop, Claude Code, custom agents) to http://localhost:8080/mcp/ and start browsing.

Works in both standalone and cluster mode.

Agent integrations

The skill works in any agent that reads .agents/skills/ (such as peen), and installs natively in the clients below.

Claude Code

claude plugin marketplace add psyb0t/agents
claude plugin install stealthy-auto-browse@psyb0t

Claude Code prompts for the stealthy-auto-browse URL and, if auth is enabled, the token, which it stores in your OS keychain.

Codex

codex plugin marketplace add psyb0t/agents
codex plugin add stealthy-auto-browse@psyb0t

Installed via the marketplace, the skill invokes as $stealthy-auto-browse:stealthy-auto-browse. Codex also picks the skill up automatically with no install in any repo containing .agents/skills/, where it invokes as plain $stealthy-auto-browse.

OpenClaw

The skill is published to ClawHub on every release:

openclaw skills install @psyb0t/stealthy-auto-browse

For MCP clients that speak local stdio, the @psyb0t/stealthy-auto-browse plugin bridges to the service's /mcp endpoint:

openclaw plugins install clawhub:@psyb0t/stealthy-auto-browse

Then set STEALTHY_AUTO_BROWSE_URL (and AUTH_TOKEN if the server requires auth).

Script mode

Pipe a YAML script into the container, get JSON results on stdout, container exits. No HTTP server. Good for CI, cron jobs, one-shot scraping.

cat my-script.yaml | docker run --rm -i \
  -e TARGET_URL=https://example.com \
  psyb0t/stealthy-auto-browse --script > results.json

Full docs: docs/script-mode.md

Script mode also supports explicit if branches plus bounded repeat and while loops. Conditions can inspect elements, visible text, URLs, JavaScript booleans, and prior output_id values; see the control-flow reference.

Page loaders

Define URL patterns + action sequences in YAML files. Mount them at /loaders. Whenever goto matches a pattern, the loader runs automatically. It removes popups, waits for content, and cleans up the page. Greasemonkey for the HTTP API.

Full docs: docs/page-loaders.md

Screen recording

Record the browser as MP4 with mouse cursor visible. ffmpeg x11grab against Xvfb writes to a mounted /recordings volume. Three modes: window (full Camoufox window), viewport (chrome cropped at the page position from mozInnerScreenX/Y, measured when recording starts), desktop (entire Xvfb screen). You provide the slug at stop time, so you name the file after the run completes. Path-traversal-safe, collision-safe, crash-safe.

mkdir -p ./recordings
docker run -d -p 8080:8080 -v ./recordings:/recordings psyb0t/stealthy-auto-browse
curl -X POST http://localhost:8080 \
  -H "Content-Type: application/json" \
  -d '{"action": "start_recording", "mode": "viewport", "fps": 20}'

# … do stuff …

curl -X POST http://localhost:8080 \
  -H "Content-Type: application/json" \
  -d '{"action": "stop_recording", "slug": "my-flow"}'
# → ./recordings/my-flow.mp4

Also works inside run_script (cluster-mode safe: start and stop must live in the same run_script so both hit the same instance). Full action table + script-mode example + notes in docs/api.md#screen-recording.

Cluster mode

Run multiple browser instances behind HAProxy with a request queue, sticky sessions, and Redis cookie sync (default 5, configurable via NUM_REPLICAS). Download the compose file and HAProxy config, then start:

curl -LO https://raw.githubusercontent.com/psyb0t/docker-stealthy-auto-browse/main/docker-compose.cluster.yml
docker compose -f docker-compose.cluster.yml up -d

Cookies set on any instance propagate to all others instantly via Redis PubSub. Log in once, the whole fleet is authenticated.

Each browser defaults to a 5GB memory limit. Set BROWSER_MEMORY_LIMIT and BROWSER_MEMORY_RESERVATION when your fleet or display resolution needs a different budget; see cluster mode.

Script-only enforcement (v1.0.0+): When NUM_REPLICAS > 1, both the HTTP API and MCP server restrict to run_script only (plus ping and sleep). They reject individual actions to prevent stale content bugs from cross-instance routing. All actions remain available as steps inside run_script.

Full docs: docs/cluster-mode.md

Authentication

Set AUTH_TOKEN to require a Bearer token on all requests (except /health):

docker run -d -p 8080:8080 -e AUTH_TOKEN=your-token-here psyb0t/stealthy-auto-browse

Pass the token in the Authorization header:

# Header
curl -H "Authorization: Bearer your-token-here" http://localhost:8080 ...

Examples

See .agents/skills/stealthy-auto-browse/scripts/ for ready-to-use scripts:

  • websearch.py: Multi-engine parallel web search (Brave, Google, Bing) with structured results and AI overview extraction. Outputs JSON with title, URL, and snippet for each result.

Configuration

Full environment variables table, proxy setup (including a working private pr0xteus HTTP-proxy example), persistent profiles, browser extensions, and VNC access: docs/configuration.md

Development

Docker is the only development dependency. The Makefile builds a local browser base, then runs linting, tests, and security tools in a separate disposable dev container. Only the complete test target receives the Docker socket because it starts its own browser and fixture containers.

make build
make lint
make test
make test-real
make sec

make sec writes sec.sarif for GitHub Security. It reports findings without blocking a release. Use make help for the complete command list.

make test-real is an opt-in live check against liarjs.dev, so it requires outbound network access and does not run in the deterministic default suite.

Bot detection results

These are observed results, not a promise that every version of each service will return the same result. Test the exact image, proxy, timezone, and target flow you plan to use.

Service Observed result What they check
CreepJS Pass Canvas/WebGL fingerprint consistency, lies detection, worker comparison
liarjs.dev 82/100, no critical findings Font, canvas, worker, GPU, network, and behavioral consistency
BrowserScan Pass WebDriver flag, CDP signals, navigator properties
Pixelscan Pass Fingerprint coherence, timezone/IP match, WebRTC leaks
Cloudflare Pass Challenge pages, Turnstile, bot management
SannySoft Pass Intoli + fingerprint scanner tests
Incolumitas Pass Modern detection techniques
Rebrowser Pass CDP leak detection, webdriver, viewport analysis
BrowserLeaks WebRTC Pass WebRTC IP leak detection
DeviceAndBrowserInfo Pass 19 checks, all green, "You are human!"
IpHey Pass "Trustworthy" rating
Fingerprint.com Pass Identified as normal Firefox, no bot flags

Why it works: docs/stealth.md

License

WTFPL (Do What The Fuck You Want To Public License)

Facts

Kind
MCP server
Repo
psyb0t/docker-stealthy-auto-browse
Group
Uncategorized
Stars
89
License
WTFPL
Language
Python
Last push
2026-10-10
Forks
15
MCP Registry
io.github.psyb0t/stealthy-auto-browse
Homepage
ciprian.51k.eu/docker-stealthy-auto-browse-the-browser-that-doesnt-know-its-being-automated
Topics
automated, automation, browser, camoufox, container, docker, http-api, playwright, stealth, pyautogui, system-automation, python, api-browser, mcp, mcp-browser, mcp-server, stealth-browser

More on this shelf

  1. 1REAio.github.morluto/reaReverse engineer anything from your terminal or agent with one CLI and MCP server.125.3k
  2. 2com.browser-use/browser-usecom.browser-use/browser-useControl a real Chrome browser to complete any task: fill forms, extract data, book flights.117.7k
  3. 3World Monitorapp.worldmonitor/mcpLive markets, conflicts, country risk, chokepoints, energy, and China decision signals. 93 tools.88.3k
  4. 4Scrapling MCP Serverd4vinci/scrapling🕷️ An adaptive Web Scraping framework that handles everything from a single request to a full-scale crawl! Don't be shy, join here: https://discord.gg/EMgGbDceNQ and follow here for daily tips and tricks: https://x.com/Scraplingdev86.9k
  5. 5Everythingmodelcontextprotocol/serversThis MCP server attempts to exercise all the features of the MCP protocol. It is not intended to be a useful server, but rather a test server for builders of MCP clients. It implements prompts, tools, resources, sampling, and more to showcase MCP capabilities.85.8k
  6. 6Fetchmodelcontextprotocol/serversA Model Context Protocol server that provides web content fetching capabilities. This server enables LLMs to retrieve and process content from web pages, converting HTML to markdown for easier consumption.85.8k