Skip to main content

Stealth browser automation framework that talks directly to Chrome via CDP (no webdriver, no chromedriver). A maintained fork of nodriver.

Project description

mithwire

🥷 An anti-detect browser for Python.
Drive real Chrome straight over the DevTools Protocol — no WebDriver, no Selenium, no chromedriver — with stealth and Cloudflare Turnstile solving built in.

PyPI Python versions License mithwire-mcp


🤔 What is mithwire?

mithwire is an anti-detect browser automation framework. It launches a normal Chromium-based browser (Chrome, Brave, Edge…) and controls it by talking directly to the Chrome DevTools Protocol (CDP).

There's no automation driver bolted on the side — so the tell-tale signals anti-bot systems look for to spot WebDriver/Selenium simply aren't there. You get a browser that behaves like a real one, a short and pleasant async API, and the practical stealth fixes you actually need against modern anti-bot stacks.

✨ Features

🥷 Anti-detect by design — speaks raw CDP, so there's no navigator.webdriver, no chromedriver binary, and no Selenium surface to fingerprint

🌐 Real Chromium browsers — works with Chrome, Chromium, Brave, and Edge

👁️ Headful or headless — run with a visible window, or invisibly on a server (pair with Xvfb when you need a real display)

🎭 Identity & fingerprint control — full CDP Emulation access to shape what the browser presents: timezone, locale, geolocation, user agent, device metrics, and more

🌍 Proxy support — route traffic through upstream HTTP/SOCKS proxies

🛡️ Cloudflare Turnstile bypass — one call (tab.verify_cf()) solves the checkbox challenge, light and dark mode

🔎 Smart DOM access — find elements by text, CSS selector, or XPath, including inside iframes — lookups double as wait conditions

📡 Full network & event access — the entire CDP surface is yours: requests, responses, events, interception

🍪 Stateful when you want it — cookies, localStorage, multi-tab & multi-window, screenshots, and external-debugger attach

Tiny, async API — up and running in ~2 lines, with best-practice defaults and automatic profile cleanup


🤖 mithwire-mcp — give your AI agents a browser fleet

mithwire-mcp

One mithwire browser is great for a script. But what about an AI agent that needs to run dozens of them? That's mithwire-mcp — a Model Context Protocol server that turns mithwire into a tool your LLM can drive directly.

  • 🧠 Agents launch & control their own browserssession_start, navigate, click, type, screenshot, evaluate — all as MCP tools
  • 🚀 Fleet management — spin up many isolated sessions at once, each its own browser process, with full lifecycle control
  • 👤 Durable identities — persistent profiles with their own cookies, proxies, and fingerprints that survive across runs
  • 🕵️ Consistent stealth at scale — proxy-aligned timezone/locale/geo, fingerprint spoofing, and WebRTC leak protection kept in sync per worker

➡️ Get started with mithwire-mcp →


📦 Install

pip install mithwire

You'll need a Chromium-based browser (Chrome/Brave/Edge) installed, ideally in the default location. On a headless server, run under Xvfb or use headless mode. To upgrade:

pip install -U mithwire

🚀 Quick start

import mithwire as uc

async def main():
    browser = await uc.start()
    page = await browser.get("https://www.nowsecure.nl")
    await page.save_screenshot()

uc.loop().run_until_complete(main())

💡 Use uc.loop().run_until_complete(...) instead of asyncio.run(...) for reliable event-loop handling.

🧭 Usage

⚙️ Custom launch options

from mithwire import start

browser = await start(
    headless=False,
    user_data_dir="/path/to/profile",   # pass one and it won't be auto-cleaned on exit
    browser_executable_path="/path/to/some/other/browser",
    browser_args=["--some-flag=true"],
    lang="en-US",
)
tab = await browser.get("https://example.com")

Or configure via a Config object:

from mithwire import Config

config = Config()
config.headless = False
config.user_data_dir = "/path/to/profile"
config.browser_args = ["--some-flag=true"]

🔎 Finding things on the page

# by text — returns the closest match by text length, not the first hit
accept = await tab.find("accept all", best_match=True)
await accept.click()

# by CSS selector (retries until found or <timeout>, so it doubles as a wait)
email = await tab.select("input[type=email]")
imgs = await tab.select_all("a[href] > div > img")

# by XPath
node = await tab.xpath("//button[contains(., 'Next')]", timeout=2.5)

🛡️ Solving Cloudflare Turnstile

page = await browser.get("https://site-behind-turnstile.example")
await page.verify_cf(max_retries=3, timeout=20, retry_interval=2)

Bundled with light- and dark-mode widget templates and HiDPI-correct coordinates. Requires opencv-python (pip install opencv-python).

🧰 Handy tab helpers

Method Does
tab.get_content() current page HTML
tab.save_screenshot() screenshot to a temp file
tab.scroll_down(n) / tab.scroll_up(n) scroll the page
tab.get_local_storage() / tab.set_local_storage(dict) read/write localStorage
tab.add_handler(event, cb) subscribe to CDP events (cb(event) or cb(event, tab))
tab.bypass_insecure_connection_warning() click through invalid-cert warnings
tab.open_external_debugger() inspect a tab without breaking your connection

A fuller, runnable script (automating account creation end to end) lives in the example/ folder. 📂

⚖️ How it compares

Most "stealth" tools either drive the browser through WebDriver/Selenium (which leaks an obvious automation surface) or patch over it with injected JavaScript (which is itself detectable). mithwire skips both: it speaks the browser's own debug protocol directly, so there's no driver to fingerprint and no injected shim to catch — while still giving you the full power of CDP for low-level control.

🙏 Credits & license

mithwire is a maintained fork of nodriver by UltrafunkAmsterdam, itself the successor to undetected-chromedriver. It's distributed under the GNU AGPL-3.0, the same license as upstream. Original copyright and license are preserved in LICENSE.txt; attribution details are in NOTICE. mithwire is not affiliated with or endorsed by the original author.

This fork addresses real-world limitations hit when running against modern anti-bot systems; fixes land as they're discovered during active use.

⚠️ Use responsibly. Only automate sites and accounts you're authorized to use, respect Terms of Service and local law, and avoid abusive request rates.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

mithwire-0.50.8.tar.gz (393.8 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

mithwire-0.50.8-py3-none-any.whl (415.3 kB view details)

Uploaded Python 3

File details

Details for the file mithwire-0.50.8.tar.gz.

File metadata

  • Download URL: mithwire-0.50.8.tar.gz
  • Upload date:
  • Size: 393.8 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for mithwire-0.50.8.tar.gz
Algorithm Hash digest
SHA256 34d82374b857964d992e5ba904898a8ee0a6facdc34dfc9b2be83e0745926fb2
MD5 7e4618d3a9474d439fc0f55efd12359c
BLAKE2b-256 bc16d0d9559483abeb2ea89a447d2da585e8742270fd3b2c055dec4fa86545ca

See more details on using hashes here.

Provenance

The following attestation bundles were made for mithwire-0.50.8.tar.gz:

Publisher: release.yml on codeisalifestyle/mithwire

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file mithwire-0.50.8-py3-none-any.whl.

File metadata

  • Download URL: mithwire-0.50.8-py3-none-any.whl
  • Upload date:
  • Size: 415.3 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for mithwire-0.50.8-py3-none-any.whl
Algorithm Hash digest
SHA256 df346fbcf90e1164609052bb23ba35573385a9adc9e98325319c78107b7a93f7
MD5 8488c25be0ba0fcffb069531f1346055
BLAKE2b-256 6d5e62082514e9c0aa720f6d342a3e277775595c6082f1cc8d1694983e2940ca

See more details on using hashes here.

Provenance

The following attestation bundles were made for mithwire-0.50.8-py3-none-any.whl:

Publisher: release.yml on codeisalifestyle/mithwire

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page