invisible_playwright_mcp

io.github.feder-cr由 GitHub 帳號 feder-cr 發布v0.73.0更新於 Oct 29, 2026

AI browser agent: browses, clicks, types, and reads real web pages from plain-English instructions.

概覽

AI 產生的概覽

本機 MCP 伺服器,為助理提供一個隱蔽的 Firefox 瀏覽器,以擬人化的滑鼠與鍵盤操作導覽、點擊、輸入並讀取真實網頁。

可以做什麼
  • 開啟、關閉並檢視一個或兩個瀏覽器身分(main 與 support),各自可帶種子、代理或設定檔
  • 導覽至網址並讀取 HTTP 狀態碼與實際落地的位址
  • 以可見文字、清理後的 HTML 或含選擇器與座標的互動元素快照讀取頁面
  • 操作頁面:依選擇器或座標點擊、輸入文字、選擇下拉選項、按鍵、上傳本機檔案
  • 截圖與即時視窗畫面,並執行唯讀 JavaScript 取得其他工具看不到的值
適用情境
需要真實瀏覽器而非 API 的任務:填寫表單、逐日查詢票價或空位、讀取被同意視窗遮住的頁面,或操作選擇器無法觸及的元件。當網站阻擋一般自動化、需要更像真人的瀏覽器時也適用。
執行需求
透過 uvx 以 stdio 在本機執行(Python 套件 invisible-playwright-mcp),需先安裝 uv。首次啟動會從 GitHub 發行頁下載瀏覽器引擎,設定代理時還會下載 GeoIP 資料庫。選用參數或環境變數:獨立網頁介面用的 OPENROUTER_API_KEY、INVISIBLE_MCP_MODEL、代理、種子、設定檔目錄、引擎二進位路徑、本機儲存用的 INVISIBLE_MCP_HOME。
安裝前請注意
此伺服器明確以對反機器人系統不可見為目標,可能與網站條款衝突;README 要求遵守條款與速率限制。它能輸入、上傳檔案並送出表單,送出的內容都是真實的。獨立介面會以你的金鑰把對話與頁面內容傳送給 OpenRouter;以命令列參數傳入金鑰會留在 shell 歷史與行程清單中。變更介面監聽位址會暴露一個沒有身分驗證的介面。每次啟動都會連線 GitHub,對方可見你的 IP。

安裝

在 SourceWeft 中

  1. 開啟 儀表板中的 invisible_playwright_mcp,將其新增到工作區。
  2. 為需要使用其工具的對話啟用該服務。

Desktop only,透過 STDIO。 STDIO 服務會啟動本機處理程序,因此需要 SourceWeft 桌面主機。

其他 MCP 客戶端

參照 儲存庫 中的啟動說明。

README


Two ways to use this browser agent

1. From your assistant, over MCP

Windows, in PowerShell:

powershell
powershell -ExecutionPolicy ByPass -c "irm https://astral.sh/uv/install.ps1 | iex"$env:Path = "$env:USERPROFILE\.local\bin;$env:Path"

Linux:

bash
curl -LsSf https://astral.sh/uv/install.sh | shsource $HOME/.local/bin/env

Then install it in your assistant.

Claude Code:

bash
claude plugin marketplace add feder-cr/invisible_playwright_mcpclaude plugin install invisible-playwright-mcp@feder-cr

Codex:

bash
codex plugin marketplace add feder-cr/invisible_playwright_mcpcodex plugin add invisible-playwright-mcp@feder-cr

Gemini CLI:

bash
gemini extensions install https://github.com/feder-cr/invisible_playwright_mcp

2. Standalone: the web UI

We bring the interface, you bring an OpenRouter key. Chat on the left, the live browser on the right.

Windows, in PowerShell:

powershell
powershell -ExecutionPolicy ByPass -c "irm https://astral.sh/uv/install.ps1 | iex"$env:Path = "$env:USERPROFILE\.local\bin;$env:Path"uvx invisible-playwright-mcp ui --openrouter-key sk-or-...

Linux:

bash
curl -LsSf https://astral.sh/uv/install.sh | shsource $HOME/.local/bin/envuvx invisible-playwright-mcp ui --openrouter-key sk-or-...

Then open http://127.0.0.1:8765 and type the same thing.


What to ask a web browsing agent

Anything that needs real web automation: a browser rather than an API, and a person's judgement about what is on the page.

Go to <paste the URL>. One way, Milan to Lisbon, economy, one checked bag, one adult. Check every date from the 12th to the 16th of next month, one at a time, and read the cheapest fare for each day. The date field is a calendar widget, so click the days rather than typing them. If a date has no availability, say so. Do not guess a number.

It drives the page the way a person would: the pointer moves, keys are pressed.

Options: proxy, profile, seed

  • --openrouter-key Your key, or the OPENROUTER_API_KEY variable.
  • --model An OpenRouter model id, or INVISIBLE_MCP_MODEL. Defaults to z-ai/glm-5.3-flash.
  • --proxy Optional. http://user:[email protected]:8080 or socks5://proxy.example.com:1080. Host and port are both required. The timezone, locale and egress follow it.
  • --binary An engine binary you already have. It must be the build the seal pins, or startup refuses: this skips the download, not the version check.
  • --seed An integer. Same seed, same browser identity, every run.
  • --profile-dir A directory to keep the profile in, so logins and cookies survive restarts.
  • --headed Show the browser window. The interface shows you the page anyway.
  • --host, --port 127.0.0.1 and 8765. Changing the host exposes an interface that has no authentication.

A .env beside the command

Rather than retyping the key and the binary path, put them in a .env in the directory you run from:

OPENROUTER_API_KEY=sk-or-...STEALTHFOX_BINARY=/path/to/firefox

It is read at startup, and on the way in it never overrides something already set, so the order is --flag > the environment > .env > the default. Only the directory you are in is read - there is no search upwards, so running from a subfolder cannot silently pick up a different key. The startup line names the variables it applied and never prints their values.

Passing --openrouter-key puts the key in your shell history, and on Linux in the process list. OPENROUTER_API_KEY in the environment or in a .env avoids both.

The wiki: AI browser-agent guides

The reading room around the agent lives in the wiki: the AI browser-agent landscape: browser-use, Operator-style and computer-use agents compared, what to check when an agent gets blocked, and what happened to OpenAI Operator, among others. Worked examples, transcripts and their outputs live in articles/.

The rest of the family: engine, core

The MCP server from option 1 ships inside this package: invisible_playwright_mcp with no subcommand is the server, invisible-playwright-mcp ui the interface. Its config blocks for clients that take a file, its settings and its tools are on the wiki page The MCP server.

  • invisible_playwright The engine, as a Python library, for writing code instead of prompts. The API is Playwright's.
  • invisible_core Seed to fingerprint to preferences, proxy and geolocation.

Using it responsibly

This automates a browser under your control. Read the terms of the sites you point it at, respect their rate limits, and do not submit anything a human has not read.

Privacy Policy

This browser agent runs on your machine and has no server of its own. What leaves your computer, and to whom:

  • The sites you visit see the browser, as they would any Firefox.
  • Your model provider. The web UI sends the conversation and what the agent reads on the page to OpenRouter under your key. Over MCP, the client you plugged it into does the same with whichever model it uses.
  • GitHub. The engine is downloaded from a GitHub release the first time the server or the interface starts, and a GeoIP database is when a proxy is set. Each browser launch also fetches a one-line counter file from a GitHub release, which is how launches are counted: the request carries no identifier and nothing of yours, and GitHub sees what any HTTPS request shows, your IP address.

Nothing else is collected and nothing is sent to the author. Sessions, profiles and screenshots are stored locally, under INVISIBLE_MCP_HOME if set and otherwise in the application-data directory of your system, and are yours to delete; nothing is retained anywhere else. Questions go to the issues.

License

MIT. Everything distributed before 2 September 2026 was released under AGPL-3.0 and stays under it.

來源:README.md,提交 e60cdf2

工具

17

browser_open

Open a browser

Open `main` or `support`, or reopen one as somebody else. `support` is yours to manage: open it when the task needs a second identity, and close it with browser_close as soon as the task no longer needs it, before you answer. It is not saved. Called on a browser that is already up, this REOPENS it with the settings given, and what it held is gone. seed the identity; same seed, same fingerprint. Left out, one is drawn. profile a directory keeping cookies, logins and the seed between opens; "" means none. It keeps the SEED too, so a login does not come back on different hardware every visit. proxy the exit, `http://user:pass@host:port` or `socks5://host:port`; "" means this machine's own address; left out for `support`, it shares the exit `main` has. A profile does NOT pin its exit, and a login arriving from a new country is as visible as one arriving on new hardware.

輸入參數seedproxybrowserprofile

browser_close

Close a browser

Close one browser and free what it was holding. The page it had is gone with it. The other browser is not touched. Who it was is kept: browser_open with no arguments brings the same person back. To be somebody else, pass a seed, a proxy or a profile.

輸入參數browser

browser_list

List the browsers

Which of the two browsers are open, where each one is, and which one you are working in. Answers JSON: `focus`, the browser your last command acted in, or "" when none is open; `note`, which says how many are open and that a command naming no browser goes to `main`; and `browsers` - each row `id`, `url` (the page it is on) and `urls` (every page it holds, which is more than one only when a site opened one). Only open browsers are listed, so every row is one you can act on. Starts nothing: it reports what is open, so asking is free.

無需輸入參數。

browser_status

Who is browsing

Who is browsing right now: the identity, the exit, the profile and the page. Ask whenever you need to know which person the browser currently is, or from where its traffic leaves. The seed is what you would pass to `browser_open` to become this person again, so this is also how you record an identity worth repeating. It starts nothing: a browser that is not open, or gone, is answered with the sentence that says which.

輸入參數browser

browser_navigate

Go to a URL

Go to a url in this browser's page, opening it if none exists. Answers with the HTTP status the server gave and the url actually landed on, which is not always the one asked for: a redirect to a login wall or a regional domain shows up here. Read the status before trusting the page - a 404 or a 403 still has a document, and reading it as content is the mistake this reply exists to prevent. If the page you were on navigates itself at that moment (a redirect on a timer), Firefox can follow it instead, and the answer is an error saying "interrupted by another navigation to" a url. The browser is on that url, and you can navigate again from there. wait_until is "domcontentloaded" by default, which returns as soon as the markup is parsed. Use "load" when the page needs its images and stylesheets, or "networkidle" for a single-page app that fetches its content after load.

輸入參數url必填browserwait_until

browser_read_text

Read the page text

The visible text of an element, with the markup gone, whole. The cheapest way to read a page. Narrow the selector when you know where the answer is; use browser_read_html instead when the structure matters, or browser_snapshot when you need something to click.

輸入參數browserselector

browser_snapshot

Snapshot the page

Title, url, and the interactive elements that are actually visible. Each element carries a `selector` when one can reach it: pass that string to browser_click or browser_type VERBATIM rather than writing your own. It is built to match exactly ONE element, which the obvious selector often does not, and the driver acts on the first match - so a caller aiming at the third of five identical links would silently hit the first and be told it succeeded. Elements with no `selector` carry `at`, the centre coordinates, for browser_click_at. It lists what a caller can act on, and it is not the accessibility tree: one country `<select>` would otherwise fill the answer with its options before the form you were looking for appears.

輸入參數browser

browser_read_html

Read the page HTML

The page's HTML, cleaned down to what is worth reading. Use this when the STRUCTURE matters - a form and its labels, a table, what a control is wired to. `browser_snapshot` gives a flat inventory of things to click; this keeps the markup and the relationships inside it. mode="form" keeps the interactive surface and the text explaining it, mode="text" returns the prose alone, mode="full" keeps the structure with the noise and the attribute soup removed. Unlike browser_read_text this is NOT capped: it returns the whole reduced page, tens of thousands of characters on a large one. Cutting markup in the middle leaves tags that mean nothing, so it is not cut - but the answer can be long. Reach for browser_snapshot when you only need something to click.

輸入參數modebrowser

browser_take_screenshot

Take a screenshot

One screenshot of this browser's page, on demand.

輸入參數browser

browser_watch

Watch the browser window

The whole browser window as a person at the machine sees it: tab strip, address bar, the page and the pointer, from a live capture kept running on that page. For watching the work, not for acting on it: the picture is window pixels, so do not feed its coordinates to browser_click_at; use browser_take_screenshot for that. Starts nothing. A browser that is not open has no window, so this answers the sentence that says so, and the live panes - which call this many times a second - read that sentence as the idle pane.

輸入參數browser

browser_click

Click an element

Click the first element matching a CSS selector. Scrolls it into view and waits for it to be clickable. When no selector can describe the target, use browser_click_at with coordinates from browser_snapshot.

輸入參數browserselector必填

browser_click_at

Click at a point

Click (or press-and-hold) a raw viewport coordinate instead of a selector - for targets a selector cannot reliably reach: a slider track, a canvas-drawn captcha, a precise point inside a wider element. Moves the pointer there first (no teleport), then down, then up, holding first if hold_seconds is set. Returns a screenshot taken right after release. Coordinates are relative to the VIEWPORT, not to the page, so the ones in a snapshot go stale the moment anything scrolls. Nothing raises when that happens: the click lands on whatever is at that spot now. Take a fresh snapshot after anything that could have moved the page, and prefer browser_click with the element's `selector` whenever it has one.

輸入參數x必填y必填browserhold_seconds

browser_type

Type into a field

Type into a field, replacing what it holds, key by key at a human pace. The answer says what the field kept: all of it, a maxlength's cut, the page's reformatting, nothing (the page took it out), or one box of a code the page spreads over several. Nothing is retyped on its own. Text too long to finish within one answer goes on in the background; until it ends, actions on that browser are refused with its progress, and reads work.

輸入參數text必填browserselector必填

browser_select_option

Choose a dropdown option

Choose an option in a dropdown (`<select>`), by its visible label or by its value. Use this rather than clicking the dropdown and pressing arrow keys: a click plus arrows cannot tell you which row it landed on, and setting the value through browser_evaluate changes it without the page seeing a real interaction.

輸入參數value必填browserselector必填

browser_upload_files

Upload files

Attach local files to a file input, the way a person picks them. `selector` is the `<input type=file>` itself, or the button or label that opens its chooser: that is clicked with the real pointer and the chooser answered with `paths`. A hidden input is opened through its label; with none, pass the button that opens it. Several files need an input that takes several; otherwise upload them one call at a time. Each path is absolute and names a regular file inside a directory listed in INVISIBLE_MCP_UPLOAD_DIRS, through no hidden directory; with none listed, uploads are off. Never use browser_evaluate to set `files`.

輸入參數paths必填browserselector必填

browser_press_key

Press a key

Press a key on whatever has focus: "Enter", "Tab", "Escape", "ArrowDown", "Control+a", or a single character.

輸入參數key必填browser

browser_evaluate

Read the page with JavaScript

READ from the page with JavaScript and get the result as JSON. Last, for what other tools cannot see: a computed style, a value held in a framework's state, the length of a list. Text, a heading or a price is browser_read_text's, markup browser_read_html's, a control browser_snapshot's: a script runs in the page, and they do not. Acting on the page is refused, and the refusal names the tool to use. Assigning to `value`, `checked` or `selected`, or calling `click()`, `dispatchEvent()`, `submit()` or `requestSubmit()`, changes the page without a real keystroke or pointer, and a page can tell. Use browser_click, browser_type or browser_select_option instead; they do the same thing through the pointer and the keyboard. Reading any of those properties is fine. The refusal catches the obvious spellings, not every possible one. A script that slips past it is still the wrong way to do the thing: report it in your answer rather than using it.

輸入參數browserexpression必填

版本歷史

20
  1. v0.73.0最新Oct 21, 2026
  2. v0.72.3Oct 17, 2026
  3. v0.72.2Oct 13, 2026
  4. v0.72.1Oct 12, 2026
  5. v0.72.0Oct 12, 2026
  6. v0.71.2Oct 11, 2026
  7. v0.71.1Oct 11, 2026
  8. v0.71.0Oct 10, 2026
  9. v0.70.17Oct 10, 2026
  10. v0.70.16Oct 9, 2026
  11. v0.70.15Oct 9, 2026
  12. v0.70.14Oct 9, 2026
  13. v0.70.13Oct 8, 2026
  14. v0.70.12Oct 6, 2026
  15. v0.70.11Oct 5, 2026
  16. v0.70.10Oct 4, 2026
  17. v0.70.9Oct 3, 2026
  18. v0.70.8Oct 2, 2026
  19. v0.70.2Sep 25, 2026
  20. v0.70.0Sep 23, 2026

評分

已滿足適用檢查項的 90%

該評分衡量此收錄條目的完整程度,依據註冊中心條目、端點與公開來源計算得出。它不是對伺服器本身的評價,也不表示伺服器安全或目前可用:可連通性是帶日期的檢查結果,評分也不包含安裝量或使用者評價。

  • 近 30 天內可連通25 分
  • 已宣告認證方式10 分

    請在註冊中心條目中宣告憑證請求標頭,或保持端點能夠回應探測,以便判定其認證方式。

  • 已列出工具必需15 分
  • 描述必需15 分
  • 圖示10 分
  • 文件15 分
  • 授權條款10 分