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 分