invisible-playwright-mcp
A stealth Firefox, exposed as an MCP server, so an AI agent can browse and act on real websites without being blocked by anti-bot systems.
It wraps invisible-playwright,
a real Firefox patched at the C++ source, and hands your client the usual browser
tools: navigate, click, type, read, screenshot. The fingerprint is set inside the
engine, not bolted onto the page.
First, one prerequisite
Python 3.11 or newer, on Windows (x86_64) or Linux (x86_64, arm64) - macOS
is not supported, the last engine build for it was firefox-20. Then
uv, because the command below starts with uvx:
curl -LsSf https://astral.sh/uv/install.sh | sh # Linux
irm https://astral.sh/uv/install.ps1 | iex # Windows
Then pick one
Do you already use an AI assistant that can run tools? If yes, it brings the model and you add this browser to it. If no, or if you would rather watch the work happen, there is an interface that brings a model too.
| 1. Add it to the assistant you have | 2. Run the interface instead |
|---|---|
|
This package. Your assistant brings the model. Claude Code, once for every project on the machine: claude mcp add -s user stealth -- uvx invisible-playwright-mcp
Drop Check it took, before trusting it: claude mcp list
For Claude Desktop, Cursor and the rest, see Config file below. |
AIHawk. No assistant needed. Bring an OpenRouter account and its key, get a page with the chat on the left and the live browser on the right. uvx aihawk ui --openrouter-key sk-or-...
Open http://127.0.0.1:8765. It is a client of this server like any other, with no private path to the page - which is the reason to believe the tools below are enough to build on. |
The download nobody warns you about
The browser is about a quarter of a gigabyte and it is not fetched when the server is installed, nor when it starts. It arrives on the first tool call that needs a page, so the first thing you ask your assistant to do sits there, and on a slow connection you get a timeout that says nothing about a download.
Get it over with first, in a terminal where you can watch it:
uvx invisible-playwright fetch
Cached afterwards, and shared with anything else that uses this engine. Per-platform sizes are in the engine's README.
Config file
For any client that takes a JSON block rather than a command:
{
"mcpServers": {
"stealth": {
"command": "uvx",
"args": ["invisible-playwright-mcp"]
}
}
}
Where it goes:
| Client | File |
|---|---|
| Claude Desktop (macOS) | ~/Library/Application Support/Claude/claude_desktop_config.json |
| Claude Desktop (Windows) | %APPDATA%\Claude\claude_desktop_config.json |
| Cursor | ~/.cursor/mcp.json, or .cursor/mcp.json in the project |
| Claude Code | ~/.claude.json, but use claude mcp add above instead |
Settings from the table below go in the same block, under env:
{
"mcpServers": {
"stealth": {
"command": "uvx",
"args": ["invisible-playwright-mcp"],
"env": {
"STEALTHFOX_PROXY": "http://user:pass@proxy.example.com:8080",
"STEALTHFOX_SEED": "4242"
}
}
}
}
Settings
Environment variables, all optional. A proxy is the one worth adding: without it the exit IP, timezone and locale are your own machine's, which is a real gap between what the browser says it is and where it appears to be.
| Variable | Meaning |
|---|---|
STEALTHFOX_PROXY |
Proxy URL, e.g. http://user:pass@proxy.example.com:8080 or socks5://proxy.example.com:1080. Host and port are both required. Bring your own. With it set, the session's timezone, locale and egress are derived from the proxy. |
STEALTHFOX_SEED |
Integer seed for a deterministic fingerprint (same seed, same identity). |
STEALTHFOX_PROFILE_DIR |
A directory for a persistent profile, so logins survive across runs. |
STEALTHFOX_BINARY |
Path to an engine binary you already have. It must be the build the packaged seal pins, or startup refuses. |
STEALTHFOX_HEADLESS |
0 to run headed; headless by default. |
STEALTHFOX_MCP_TRANSPORT |
http to serve over streamable HTTP instead of stdio. Default is stdio, which is what MCP clients expect. |
STEALTHFOX_MCP_HOST |
Bind address for the HTTP transport. Default 127.0.0.1. |
STEALTHFOX_MCP_PORT |
Port for the HTTP transport. Default 8765, which is also the AIHawk interface's default: change one of the two if you run both. |
Tools
session_new_page, session_list_pages, session_select_page, session_close_page, browser_navigate, browser_read_text, browser_snapshot, browser_read_html, browser_click, browser_click_at, browser_type, browser_press_key, browser_evaluate, browser_take_screenshot, browser_select_option.
The order to try them in
The server hands every client this ladder, because a model that cannot find a way down it invents one:
- A named tool with a selector -
browser_click,browser_type,browser_select_option,browser_press_key.browser_snapshotsupplies the selector. - Coordinates - the snapshot reports
at: [x, y]for every element, andbrowser_click_atmoves the pointer there. For a canvas, a slider, a map, a widget built out of divs. - A screenshot -
browser_take_screenshot, thenbrowser_click_aton what you can see. For what the snapshot does not list at all. browser_evaluate, to READ what none of the above can see.
browser_evaluate reads; it will not act. Assigning to value, checked or
selected, or calling click(), dispatchEvent() or submit(), is refused,
and the refusal names the tool to use instead. Script reaches the page with no
keystroke and no pointer, so the event carries isTrusted false, which is the
clearest signal a page can collect that nobody is really there. Reading those
properties is fine.
browser_click_at takes viewport coordinates instead of a selector, moves the
pointer there rather than teleporting, and optionally holds before releasing -
for a slider, a canvas-drawn challenge, or a press-and-hold.
browser_snapshot returns the title, the url and the visible interactive
elements rather than the accessibility tree. Every element carries at: [x, y],
its centre in the viewport, and a selector as well when one can reach it -
pass that straight to browser_click or browser_type. browser_read_html
returns reduced markup instead, for when the structure is what matters.
Tool names mirror the Microsoft Playwright MCP, so prompts written for it work here too.
Why each of those returns what it does, with the measurements behind it: docs/tool-design.md.
More than one client on the same browser
Over stdio the browser belongs to the client that opened it. Set
STEALTHFOX_MCP_TRANSPORT=http and it does not: the session is owned by the
server, so a second client can attach to the browser the first one left open,
and closing a client no longer kills the browser.
To SEE the browser rather than share it, use AIHawk from column 2, which shows the live page beside the conversation.
STEALTHFOX_MCP_TRANSPORT=http uvx invisible-playwright-mcp
Notes
- Bring your own proxy. The engine does the stealth; a residential proxy gives you the matching IP and geography. Without one it still runs, but the exit is your own address.
- This is a browser, not a captcha solver. It does not solve or bypass challenges for you; it makes an ordinary Firefox session look like a real one.
License
MIT, the same as the engine it wraps.
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file invisible_playwright_mcp-0.10.0.tar.gz.
File metadata
- Download URL: invisible_playwright_mcp-0.10.0.tar.gz
- Upload date:
- Size: 77.7 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via:
twine/7.0.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
bc25432029b051358828cefb3d557c700910ed145a3e537639aae7a029453981
|
|
| MD5 |
4d62f90d228766cee7b4089f1c772451
|
|
| BLAKE2b-256 |
00905ae49b152ef795a22b357f440ce7da5d893ec0bfe5f1a7e89eb9bbffa090
|
Provenance
The following attestation bundles were made for invisible_playwright_mcp-0.10.0.tar.gz:
Publisher:
publish.yml on feder-cr/invisible-playwright-mcp
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
invisible_playwright_mcp-0.10.0.tar.gz -
Subject digest:
bc25432029b051358828cefb3d557c700910ed145a3e537639aae7a029453981 - Sigstore transparency entry: 2698486543
- Sigstore integration time:
-
Permalink:
feder-cr/invisible-playwright-mcp@e5a0bfe15986749a63c57f00a8c867f685b3c4ee -
Branch / Tag:
refs/tags/v0.10.0 - Owner: https://github.com/feder-cr
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@e5a0bfe15986749a63c57f00a8c867f685b3c4ee -
Trigger Event:
push
-
Statement type:
File details
Details for the file invisible_playwright_mcp-0.10.0-py3-none-any.whl.
File metadata
- Download URL: invisible_playwright_mcp-0.10.0-py3-none-any.whl
- Upload date:
- Size: 40.1 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via:
twine/7.0.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
c2c19a71d4e86adbb913050ad555276eeb7b5ff8d80915e26b080114e0744a50
|
|
| MD5 |
08eea97b45cf70e2e2ae9cd37b0199a1
|
|
| BLAKE2b-256 |
a040b49cad436c960deed8cf465a590dac4c4e022019d7374516899165f1228a
|
Provenance
The following attestation bundles were made for invisible_playwright_mcp-0.10.0-py3-none-any.whl:
Publisher:
publish.yml on feder-cr/invisible-playwright-mcp
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
invisible_playwright_mcp-0.10.0-py3-none-any.whl -
Subject digest:
c2c19a71d4e86adbb913050ad555276eeb7b5ff8d80915e26b080114e0744a50 - Sigstore transparency entry: 2698486590
- Sigstore integration time:
-
Permalink:
feder-cr/invisible-playwright-mcp@e5a0bfe15986749a63c57f00a8c867f685b3c4ee -
Branch / Tag:
refs/tags/v0.10.0 - Owner: https://github.com/feder-cr
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@e5a0bfe15986749a63c57f00a8c867f685b3c4ee -
Trigger Event:
push
-
Statement type: