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.5.tar.gz (379.3 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.5-py3-none-any.whl (401.9 kB view details)

Uploaded Python 3

File details

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

File metadata

  • Download URL: mithwire-0.50.5.tar.gz
  • Upload date:
  • Size: 379.3 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.5.tar.gz
Algorithm Hash digest
SHA256 804c45b9b0cac27e3779592cb4331f160c7aff754b21490f13051fb973e9f1e2
MD5 54b92f7b8e2071ad7b5794c419f22520
BLAKE2b-256 1cdc5601c5fceafe4382276b59284d8e2cf2383658620273216fc34789a2f624

See more details on using hashes here.

Provenance

The following attestation bundles were made for mithwire-0.50.5.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.5-py3-none-any.whl.

File metadata

  • Download URL: mithwire-0.50.5-py3-none-any.whl
  • Upload date:
  • Size: 401.9 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.5-py3-none-any.whl
Algorithm Hash digest
SHA256 f4b3e0bca471c35346966a962c8113e47d2d09461e310aac4f1b60ac56204691
MD5 8893abcd34f64031fc12a2754e36c9e1
BLAKE2b-256 a2514aea84f8314bb060a48fc879f1e1fe710bae389bb8fdaee2ce863616ffbb

See more details on using hashes here.

Provenance

The following attestation bundles were made for mithwire-0.50.5-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