Skip to main content

MCP browser bridge that gives AI clients full access to a live browser environment.

Project description

mithwire-mcp

๐Ÿค– The LLM access layer for a fleet of anti-detect browsers.
An MCP server that lets your AI agents launch, drive, and manage many stealth Chromium browsers โ€” each with its own identity, proxy, and persistent state.

PyPI Python versions MCP License mithwire engine


๐Ÿค” What is mithwire-mcp?

mithwire is an anti-detect browser. mithwire-mcp is the layer that lets an LLM agent operate a whole fleet of them. ๐Ÿš€

It speaks the Model Context Protocol, so any MCP-capable client (Claude, Cursor, your own agentโ€ฆ) can ask it to spin up isolated browser sessions, navigate and interact with pages, and manage durable browser identities โ€” without you writing any browser-automation glue. Every session is a brand-new, isolated browser process; the MCP never touches a browser it didn't spawn.

Built for ๐Ÿ•ธ๏ธ autonomous agents, ๐Ÿงฒ scraping pipelines, ๐Ÿ‘ฅ multi-account workflows, ๐Ÿงช E2E prototyping, and production-style browser operations.

๐Ÿ’ก Want the lower-level Python engine instead of an MCP server? That's mithwire.

๐ŸŽฌ Demo

โ–ถ๏ธ Watch the demo video

โœจ Highlights

๐Ÿง  Manage a fleet, not a single browser

  • ๐Ÿ†• Launch fresh isolated sessions on demand with session_start โ€” each its own browser process.
  • ๐ŸŽฏ One simple choice per session: ephemeral (default) or a persistent managed profile. No flaky attach/clone paths.
  • ๐Ÿ” Full lifecycle control (session_start, session_list, session_get, session_stop, session_stop_all) with per-session action locking.
  • ๐Ÿ›Ÿ The MCP never attaches to, takes over, or kills a browser it didn't launch.

๐Ÿค– Built for autonomous agents

  • ๐ŸŽฎ Deterministic navigate / query / click / type / wait / evaluate / screenshot flows.
  • ๐Ÿ—๏ธ First-class for scraping pipelines, E2E prototyping, regression checks, and interactive debugging.
  • ๐Ÿ”ญ Live operational visibility: console output, request metadata, and CDP-level network capture.

๐Ÿ‘ค Durable identity & state

  • ๐Ÿ—‚๏ธ Reusable browser state in one place: profiles/ and configs/ under a single state root.
  • ๐Ÿช A managed profile persists cookies and storage natively across runs โ€” no separate cookie bookkeeping.
  • ๐ŸŽ›๏ธ Tune launch behavior: browser flags, executable path, headless/sandbox, and first-class proxy support (incl. authenticated HTTP/HTTPS).

๐Ÿ•ต๏ธ Stealth that stays consistent across the fleet

  • ๐Ÿฅท Powered by mithwire: no-WebDriver/no-Selenium Chromium control, with Cloudflare Turnstile solving (browser_solve_cloudflare).
  • ๐ŸŽญ Control the full identity โ€” IP (authenticated proxy via local relay), location (proxy-aligned timezone), language, and device profile (fingerprint spoofing) โ€” kept internally consistent across workers and headers.
  • ๐Ÿ”’ WebRTC leak protection stops the host's real IP from leaking around the proxy.
  • ๐Ÿ›ฐ๏ธ Built on direct CDP control for low-level precision and observability.

๐Ÿš€ Quick start

1๏ธโƒฃ Prerequisites

  • ๐Ÿ Python >=3.10 (3.14+ works with the latest mithwire)
  • ๐ŸŒ A Chromium-based browser installed (Chrome, Brave, or Edge)
  • ๐Ÿ“ฆ Optional but recommended: pipx for isolated CLI installs

2๏ธโƒฃ Install

Recommended (isolated, with pipx):

pipx install mithwire-mcp

Or into a virtual environment:

python3 -m venv ~/.venvs/mithwire-mcp
source ~/.venvs/mithwire-mcp/bin/activate
pip install mithwire-mcp

To track the latest unreleased changes, install straight from git:

pipx install "git+https://github.com/codeisalifestyle/mithwire-mcp.git"

Verify:

mithwire-mcp --help

3๏ธโƒฃ Add it to your MCP client

Most MCP-enabled clients accept a config shaped like this:

{
  "mcpServers": {
    "mithwire-mcp": {
      "command": "mithwire-mcp",
      "args": ["--transport", "stdio"]
    }
  }
}

If your client can't find the command, use an absolute path (which mithwire-mcp on macOS/Linux, where.exe mithwire-mcp on Windows) as the "command". With the venv install above, that's typically ~/.venvs/mithwire-mcp/bin/mithwire-mcp.

4๏ธโƒฃ Smoke test from your agent

After reloading your client, ask it to:

  1. Call session_start with default settings.
  2. Call browser_navigate to https://example.com.
  3. Call browser_snapshot.
  4. Call session_stop.

โœ… If those succeed, you're set.

๐Ÿงฉ Launching a session

The MCP always spawns a brand-new, isolated browser process. There are no "modes" to memorize โ€” session_start has exactly two shapes:

Goal Call What you get
๐Ÿ—‘๏ธ Throwaway browser (default) {} A fresh ephemeral browser with no saved state. Ideal for scraping and E2E.
๐Ÿ‘ค Persistent identity { "profile": "twitter_main" } A managed profile whose cookies/storage persist across runs.

Everything else is an optional flag on top of those two:

Option Default Purpose
headless false (headful) Run without a visible window (e.g. CI).
engine stock stock uses system Chrome with CDP/JS patches; stealth uses CloakBrowser (Linux only; see below).
proxy none Route traffic through an upstream proxy (see below).
fingerprint none Identity overrides โ€” timezone, locale/languages, geo, user agent, platform, hardware, screen, WebGL (see below).
webrtc_leak_protection auto Guard WebRTC against real-IP leaks: auto / filter / disable / off (see below).
start_url none Navigate here right after launch.
cookie_file none One-shot injection of cookies from a JSON file at launch.
sandbox true Keep Chromium's sandbox on (recommended; --no-sandbox is easily bot-detected).
preset none Apply a named preset (a shared bundle of launch settings stored under presets/<name>.json).
proxy_ref none Use a named entry from the proxy registry instead of inlining credentials in proxy.

๐ŸŒ Proxy support

proxy accepts several common spellings and normalizes them:

  • http://host:port or http://user:pass@host:port
  • the provider scheme:host:port:user:pass form
  • socks5://host:port
  • an object: { "server": "http://host:port", "username": "...", "password": "...", "rotation_url": "https://api.provider.com/rotate?token=..." }

๐Ÿ”„ rotation_url is optional โ€” a provider endpoint that rotates the upstream exit IP when hit. The MCP stores it on the proxy object at launch; call session_rotate_proxy to trigger a rotation โ€” it hits the endpoint, waits a short settle window, re-probes through the proxy to confirm the new egress, and (by default) re-aligns the browser identity (timezone, locale, languages, geolocation) to match. Anything you pinned via fingerprint (or session_set_fingerprint) keeps winning over the proxy-derived default. Rotation URLs frequently embed a secret token, so the URL is redacted anywhere it appears in session metadata or logs (userinfo and query stripped to ?***); the literal URL stays in-memory only.

๐Ÿ” Authenticated HTTP/HTTPS proxies are fully supported. Rather than answering the proxy challenge per request over CDP (which floods the event loop and stalls heavy page loads), the MCP starts a small local authenticating relay: Chromium is pointed at 127.0.0.1, and the relay injects the upstream Proxy-Authorization header and pipes bytes through to the real proxy. The browser never sees a 407. Unauthenticated HTTP/HTTPS and SOCKS proxies go straight to --proxy-server. Authenticated SOCKS is rejected up front (Chromium's --proxy-server can't carry SOCKS credentials) โ€” use the provider's HTTP/HTTPS endpoint instead.

๐Ÿฉบ Pre-launch proxy health check. A session that asks for a proxy is refused before any browser is spawned if that proxy is unreachable or rejects the credentials. The MCP issues a single absolute-form GET http://api.ipapi.is/ to the proxy (with Proxy-Authorization when present) and only proceeds on a clean 2xx with parseable JSON. There is no fallback to the host's direct connection โ€” that would silently leak the real IP into login flows and cross-contaminate any persistent profile. A bad proxy fails fast with an actionable error.

๐Ÿงญ Identity defaults aligned to the proxy egress. The same probe doubles as the egress lookup: when a proxy is set, the session defaults its identity โ€” timezone, locale, languages, Accept-Language, and geolocation โ€” to the proxy's egress IP so the two never disagree. Anything explicitly set in fingerprint={...} or in the profile's launch_options wins. SOCKS proxies get a TCP-only liveness check, with timezone alignment falling back to the in-browser ipapi.is lookup. The detected egress (ip, timezone, city, country, country_code) is recorded in session metadata under proxy_exit.

๐Ÿงฌ Engine modes

engine controls the stealth implementation:

Engine Binary Stealth approach Platforms
stock (default) System Chrome/Chromium CDP/JS overrides โ€” timezone, UA, platform, WebGL, WebRTC, device metrics, and more applied at runtime via Chrome DevTools Protocol and injected JavaScript. All (Linux, macOS, Windows)
stealth CloakBrowser C++ source-level patches โ€” fingerprint surfaces (canvas, WebGL, audio, fonts, GPU, screen, TLS, CDP detection) are modified directly in the Chromium source code before compilation. Zero runtime JS injection for covered surfaces. Linux only

When engine=stealth is requested on a non-Linux platform, it falls back to stock with a warning.

Install the stealth engine extras:

pip install mithwire-mcp[stealth]

The cloakbrowser package auto-downloads the binary on first use and caches it locally (~/.cloakbrowser/). CloakBrowser is a third-party dependency โ€” the binary is proprietary (free to use, not redistributable) while the Python wrapper is MIT-licensed. Users with a CloakBrowser Pro key can set CLOAKBROWSER_PRO_KEY for access to the latest binary builds. See the CloakBrowser project for details.

How stealth mode works. CloakBrowser generates a complete, internally consistent fingerprint from a single integer seed (--fingerprint=<seed>). When a managed profile is used, the seed is deterministic (derived from the profile name), so the same profile always presents the same canvas hash, WebGL renderer, audio context, etc. Without a profile, a random seed is chosen per session. The binary patches UA, platform, GPU, hardware concurrency, screen dimensions, canvas, audio, fonts, and more at the C++ level โ€” the page's JavaScript sees only native values with no detectable overrides.

In stealth mode, Mithwire still applies CDP timezone, locale, geolocation, and Accept-Language overrides (these complement rather than conflict with the binary's patches), and the full proxy integration (relay, health check, identity alignment, rotation) works identically.

Recommended stealth preset. Create a preset that combines the stealth engine with sensible defaults:

session_preset_set(
  preset_name="stealth-linux",
  values={
    "engine": "stealth",
    "headless": true,
    "webrtc_leak_protection": "filter"
  }
)

Then launch sessions with session_start(preset="stealth-linux") or attach it to profiles via session_profile_set(profile="my-identity", preset="stealth-linux").

๐ŸŽญ Fingerprint / identity spoofing

Pass a fingerprint object to session_start (or apply one to a live session with session_set_fingerprint) to control the identity the browser presents. All fields are optional; anything unset is left untouched:

  • ๐Ÿ• timezone_id, locale, languages, accept_language
  • ๐Ÿ“ latitude, longitude, geo_accuracy
  • ๐Ÿ–ฅ๏ธ user_agent, platform, hardware_concurrency, device_memory (GB)
  • ๐Ÿ“ screen โ€” width, height, device_scale_factor, mobile, max_touch_points
  • ๐ŸŽจ webgl_vendor, webgl_renderer

Overrides are applied at the engine level via CDP Emulation.* wherever Chromium supports it, so they propagate to Web Workers and HTTP request headers โ€” not just the main document โ€” keeping every signal internally consistent (a mismatched override is worse than none). The handful of properties with no CDP equivalent (navigator.deviceMemory, and the WebGL strings when requested) fall back to injected JS.

โš ๏ธ Two rules matter: keep overrides same-OS-family (don't claim a Windows UA on a macOS host), and if you spoof geo/timezone, back it with a matching proxy so the egress IP agrees.

๐Ÿ”’ WebRTC leak protection

WebRTC can open a STUN connection that reveals the host's real local and public IPs directly, bypassing the proxy entirely (UDP isn't proxied) โ€” the single biggest de-anonymization leak for a proxied browser. The webrtc_leak_protection option controls the guard:

Mode Behavior
auto (default) Filter leaky ICE candidates when a proxy is set; otherwise leave WebRTC intact.
filter Always drop public, non-egress ICE candidates and scrub SDP.
disable Remove RTCPeerConnection entirely (no WebRTC at all).
off No protection (real IP can leak).

๐Ÿ”ฌ Verifying stealth

scripts/verify_mcp.py launches a real session (the same BridgeBrowser path the MCP uses) against public bot-detection services and asserts the critical signals are clean โ€” useful as a regression check after touching launch/stealth/proxy code:

python3 scripts/verify_mcp.py --headless                 # deviceinfo + fingerprint
python3 scripts/verify_mcp.py --site deviceinfo
python3 scripts/verify_mcp.py --proxy "http://user:pass@host:port"  # also checks TZ alignment

๐Ÿช Cookies

A managed profile stores its cookies in Chromium's native cookie store, so they persist automatically โ€” nothing extra to manage. The only separate cookie operations are injection (cookie_file at launch, or browser_cookies_set at runtime) and export (browser_cookies_get / browser_cookies_save).

๐Ÿ—‚๏ธ cookie_file and browser_cookies_save's output_path resolve relative paths against the managed ~/.mithwire-mcp/cookies/ inbox: a profile can carry "cookie_file": "twitter.json" instead of an absolute path that only makes sense on one machine, and a browser_cookies_save with output_path: "exit.json" lands in the same place ready to be re-injected next session. Absolute and ~-prefixed paths are returned unchanged for full back-compat.

๐Ÿ—„๏ธ Centralized browser state store

mithwire-mcp keeps reusable browser state in one place:

  • ๐Ÿ“ Default root: ~/.mithwire-mcp
  • ๐ŸŒฑ Override with env var: MITHWIRE_MCP_HOME=/custom/path
  • ๐Ÿƒ Override per server run: mithwire-mcp --state-root /custom/path

Layout under that root:

~/.mithwire-mcp/
โ”œโ”€โ”€ profiles/<name>/        # Chromium user-data dir + profile.json metadata
โ”œโ”€โ”€ presets/<name>.json     # opt-in shared launch recipes (e.g. mac-us)
โ”œโ”€โ”€ proxies/<name>.json     # first-class proxy registry (creds + rotation_url)
โ””โ”€โ”€ cookies/                # one-shot cookie injection / export files

session_start resolves launch settings in four layers, lowest precedence first:

  1. Built-in defaults
  2. Effective preset values (the session's preset arg if given, else the profile's preset field โ€” never both at once)
  3. The profile's launch_options (per-profile overrides)
  4. Explicit session_start arguments

After merging, proxy_ref (if set anywhere in the chain) is expanded against the proxy registry โ€” unless a literal proxy was supplied at the same or higher layer, in which case the literal wins.

๐Ÿ›ฐ๏ธ Proxy registry

Several profiles can share one proxy (most common for residential / rotating endpoints). Store credentials once, reference them by name:

// proxies/oxy-us.json (written by session_proxy_set)
{
  "name": "oxy-us",
  "scheme": "http",
  "host": "us-pr.oxylabs.io",
  "port": 7777,
  "username": "...",
  "password": "...",
  "rotation_url": "https://api.example.com/rotate?token=...",
  "tags": ["residential", "us"]
}
// profiles/alice/profile.json
{
  "description": "Alice",
  "account_aliases": ["alice@example.com"],
  "preset": "mac-us",
  "launch_options": {
    "proxy_ref": "oxy-us",
    "fingerprint": { "timezone_id": "America/New_York" }
  }
}

The pre-launch proxy preflight (reachability + credentials probe) still runs on the resolved proxy; a failed probe refuses the session, regardless of whether the proxy came in as a literal or via proxy_ref. No half-launched browser, no silent fallback to the host's direct connection.

๐Ÿ”„ Migration from older layouts

On startup the state store transparently fixes up the previous schema:

  • ~/.mithwire-mcp/configs/ โ†’ ~/.mithwire-mcp/presets/ (file moves, no silent merge if the destination already exists).
  • profile.json: the old launch_config and launch_overrides keys are rewritten to preset and launch_options respectively, in place. Atomic and idempotent โ€” safe to re-run on every server start.

For an explicit report (what got renamed, which profiles were rewritten, whether a legacy presets/default.json is hanging around no-op, and which inlined proxies could be deduplicated into the registry) run the dedicated subcommand:

# Dry-run: simulate against a temp copy, print the report, change nothing.
mithwire-mcp migrate-state --dry-run --extract-proxies

# Apply for real, auto-name extracted proxy entries from host:port.
mithwire-mcp migrate-state --extract-proxies --auto-name

# Or omit --extract-proxies to leave inlined proxies alone for now โ€”
# they keep working; the report just surfaces them as candidates.
mithwire-mcp migrate-state

--extract-proxies collapses inlinings that share the same upstream credentials into a single proxies/<name>.json and rewrites every call-site to reference it by name. Idempotent: re-running after a successful migration is a no-op.

Note on default.json. In the old layout, configs/default.json auto-applied as a baseline for every session. After migration, the renamed presets/default.json is not applied implicitly โ€” link it from a profile via "preset": "default", or move its values inline. The migration command logs this once so the behavioural difference is never silent.

๐Ÿงฐ Tools exposed

๐Ÿ” Session lifecycle

session_start, session_list, session_get, session_state_paths, session_profile_list, session_profile_get, session_profile_set, session_profile_delete, session_preset_list, session_preset_get, session_preset_set, session_preset_delete, session_proxy_list, session_proxy_get, session_proxy_set, session_proxy_delete, session_set_fingerprint, session_rotate_proxy, session_set_policy, session_get_policy, session_set_download_dir, session_trace_start, session_trace_stop, session_trace_get, session_trace_export, session_trace_replay, session_stop, session_stop_all

๐ŸŽฎ Browser actions

browser_url, browser_navigate, browser_back, browser_forward, browser_reload, browser_tab_list, browser_tab_new, browser_tab_switch, browser_tab_close, browser_tab_current, browser_snapshot, browser_query, browser_click, browser_type, browser_handle_dialog, browser_set_file_input, browser_scroll, browser_wait, browser_wait_for_selector, browser_wait_for_url, browser_wait_for_text, browser_wait_for_function, browser_wait_for_network_idle, browser_html, browser_console_messages, browser_network_requests, browser_network_capture_start, browser_network_capture_get, browser_network_capture_stop, browser_network_capture_status, browser_downloads, browser_cookies_get, browser_cookies_set, browser_cookies_save, browser_cookies_clear, browser_storage_get, browser_storage_set, browser_storage_clear, browser_take_screenshot, browser_evaluate, browser_solve_cloudflare

๐Ÿ› ๏ธ Development

mithwire-mcp is a standalone repo that depends on the published mithwire engine. It uses uv:

git clone https://github.com/codeisalifestyle/mithwire-mcp.git
cd mithwire-mcp
uv sync                       # creates .venv and installs the package + dev deps
uv run pytest -q
uv run mithwire-mcp --transport stdio

Point your client at the dev binary with an absolute path:

{
  "mcpServers": {
    "mithwire-mcp": {
      "command": "/absolute/path/to/mithwire-mcp/.venv/bin/mithwire-mcp",
      "args": ["--transport", "stdio"]
    }
  }
}

To hack on the engine and the MCP together, install the engine editable into this venv: uv pip install -e /path/to/mithwire.

๐Ÿ“ Architecture notes live in docs/architecture.md.

๐Ÿฉน Troubleshooting

  • command not found: mithwire-mcp โ€” run pipx ensurepath, restart your terminal and client, then retry; or use the absolute path from which mithwire-mcp.
  • Python version mismatch โ€” use Python >=3.10 and reinstall/upgrade the package in your MCP environment.
  • Browser fails to launch in restricted environments โ€” try session_start with sandbox=false.
  • Client shows no tools โ€” confirm the JSON is valid, transport is stdio, and fully restart the client after editing the config.

โš ๏ธ Safety notes

  • Use this only on sites and accounts you're authorized to access.
  • Respect website Terms of Service and local regulations.
  • Be cautious with high-frequency automation.

๐Ÿ“„ License

MIT โ€” see LICENSE. Built on mithwire (AGPL-3.0).

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_mcp-0.3.1.tar.gz (149.0 kB view details)

Uploaded Source

Built Distribution

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

mithwire_mcp-0.3.1-py3-none-any.whl (107.5 kB view details)

Uploaded Python 3

File details

Details for the file mithwire_mcp-0.3.1.tar.gz.

File metadata

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

File hashes

Hashes for mithwire_mcp-0.3.1.tar.gz
Algorithm Hash digest
SHA256 1c6e0066e2ac0daae22cfb9aa4573b73a5cd0f98b0dc99226808ccbb1ed87691
MD5 2c9e356ffac110cf49a7a2d62beae273
BLAKE2b-256 99222fd888e33af6dba6ffc371978c1e6c8e7075ea9135215e2ff0c903efcf14

See more details on using hashes here.

Provenance

The following attestation bundles were made for mithwire_mcp-0.3.1.tar.gz:

Publisher: release.yml on codeisalifestyle/mithwire-mcp

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_mcp-0.3.1-py3-none-any.whl.

File metadata

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

File hashes

Hashes for mithwire_mcp-0.3.1-py3-none-any.whl
Algorithm Hash digest
SHA256 f264a001e840c3b26b6baee57d144f7fd27b7d6bf09cb1436b5476df9f070437
MD5 b3974a180037207b5c4276574b0e6e14
BLAKE2b-256 845be3950f2277b60b343855d5c5657ac241f02478a2e69d0168d323636b115e

See more details on using hashes here.

Provenance

The following attestation bundles were made for mithwire_mcp-0.3.1-py3-none-any.whl:

Publisher: release.yml on codeisalifestyle/mithwire-mcp

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