moonlighting-mcp
Moonlighting MCP lets an agent drive a real Chrome/Edge browser inside a hidden Windows desktop — snapshot, click, type, navigate, and fill saved credentials without passwords ever entering the model context.
Inspect pages:
browser_snapshot(accessibility tree, better than screenshots),browser_find(search snapshot by text/regex, cheap element locating),browser_take_screenshot,browser_console_messages,browser_network_requests/browser_network_request(headers and bodies).Interact with elements:
browser_click,browser_hover,browser_drag,browser_type,browser_fill_form(multi-field),browser_press_key,browser_select_option,browser_drop(files/MIME data onto an element).Navigate and manage tabs:
browser_navigate,browser_navigate_back,browser_tabs(list / new / close / select),browser_wait_for(text appear, disappear, or a delay).Control the browser:
browser_close,browser_resize,browser_handle_dialog(accept/dismiss, prompt text),browser_emulate_media(dark mode, print, contrast, reduced motion),browser_file_upload.Run custom code:
browser_evaluate(JS on page or element),browser_run_code_unsafe(arbitrary Playwright snippets — RCE-equivalent, use with caution).Use stored credentials:
cred_list(handles/labels/origins only, never passwords) andcred_fill(decrypts via the local DPAPI broker and injects into the page with origin + read-back verification; the model only sees lengths and status codes).
Enables browser automation and credential handling for GitHub, including replaying GitHub login flows, filling saved GitHub credentials bound to https://github.com, and tracking GitHub account metadata such as read/post permissions in the account ledger.
Click on "Deploy Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@moonlighting-mcp Log into my email and summarize my unread messages"
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
Moonlighting
你把号建好,AI agent 替你管。 一个桌面 = 一个平台 = 一个账号:agent 在属于自己的隐藏 Windows 桌面里登录、操作、切换标签页,账号密码存进该桌面独立的凭据库,永远不进模型上下文。 邮箱也能交给它——用它去各平台把号申请下来,验证码自己收、自己填。
English · 中文
┌─ 你的桌面 ─────────────────────────────────┐
│ Hermes / Claude / Codex / 任何 MCP 客户端 │
└───────────────┬────────────────────────────┘
│ MCP (stdio) 或 本地 HTTP
┌───────────────▼────────────────────────────┐
│ 隐藏桌面 "HermesDesk" │
│ ┌──────────────────────────────────────┐ │
│ │ 真实 Chrome / Edge + 持久化 profile │ │
│ │ (登录态、钱包、扩展都在里面) │ │
│ └──────────────────────────────────────┘ │
│ 凭据代理(DPAPI 加密、绑定 origin) │
└─────────────────────────────────────────────┘你看不到任何窗口闪出来,agent 也看不到任何密码。
它解决什么
让 agent 操作浏览器,通常死在三个地方:
翻车点 | 常见做法 | 这里的做法 |
找元素 | 截图 + 猜坐标,差 11px 就点空 | 一次无障碍树快照,按 |
丢状态 | 每次都是全新 profile,登录态归零 | 私有桌面上的持久 profile,关掉重开还在 |
传密码 | 把密码贴进 prompt | 你自己在掩码窗里输 → DPAPI 加密 → 代理注入,模型只拿到长度 |
42KB 的页面快照压到 6KB 再喂模型,20 步不会烧掉 20 万 token。
Related MCP server: Browser Control
快速开始
需要 Windows 10/11、Python 3.10+、Node 18+,以及任意 Chromium 系浏览器(Chrome 或 Edge)—— Windows 自带 Edge,所以干净的机器直接能用。浏览器不打包,用你系统里那个。
pip install moonlighting
moonlighting-setup --home "D:\moonlighting" # 浏览器数据放哪
moonlighting # 起推流 / 凭据代理 / 浏览器驱动启动后会打印预览地址并自动打开窗口:
preview: http://127.0.0.1:8765/ctl画面是实时的,可以直接在上面点、在上面打字——输入会反向送进隐藏桌面。
浏览器查找顺序:HERMES_CHROME → 注册表 App Paths → 常见安装路径 → PATH;
一个都没有时给一句看得懂的话,不抛 traceback。
接进 agent
一行配置(pip 安装后)
{ "mcpServers": { "browser": { "command": "moonlighting-mcp" } } }moonlighting-mcp、moonlighting-setup、moonlighting-session、moonlighting
都是装包时生成的命令,没有任何绝对路径要填。
暴露 27 个工具:25 个 browser_*(快照、点击、输入、导航、标签页、求值…)
加 cred_list / cred_fill。Codex CLI 用 ~/.codex/config.toml:
[mcp_servers.browser]
command = "moonlighting-mcp"
startup_timeout_sec = 60
tool_timeout_sec = 120从源码跑
git clone https://github.com/xinyuzjj/moonlighting.git
cd moonlighting && pip install -r requirements.txt
python scripts/svc.py
python scripts/install_mcp.py # 打印带真实路径的配置
python scripts/install_mcp.py --write all # 直接写进 Claude / Codex(自动备份原文件)凭据怎么走
用户 ──掩码弹窗──► DPAPI 加密存储 ──handle──► agent
│
cred_fill(handle) │ origin 校验 + 读回校验
▼
填进页面密码不进模型上下文。 agent 只拿到
{ok, verified, submitted, id_len, pwd_len}。绑定 origin。 给
https://github.com存的凭据,在别的 origin 上会被直接拒绝。每次注入都有审计。 往
<home>/audit.jsonl追加时间、动作、origin、handle、结果码,永不记明文。token 门禁。 首个请求前生成
token.txt;/health、/status保持开放,预览不会被卡住。不绕人机识别。 驱动的是真实浏览器,不改指纹、不打码、不硬闯风控。
多套实例并存
默认值(HermesDesk、8765/8897/8899/18763、一个全局指针文件)是按「一台机器一套」设计的。
机器上已经有一套在跑时,第二套给个自己的身份——全是环境变量:
export HERMES_BROWSER_HOME="E:\\browser\\junjunni" # 端口表 / token / 凭据库(每套必须独立)
export HERMES_PROFILES="D:\\Hermes浏览器" # profile 根,可共用,每个桌面一个子目录
export HERMES_DESK=junjunni
export HERMES_STREAM_PORT=8766 HERMES_BROKER_PORT=8900 HERMES_DRIVER_PORT=8898代码靠这几条把它们隔开:
home 每套独立。
ports.json和token.txt是按 home 命名的单文件,共用会互相覆盖端口表。认领端口要验身份。 服务在
/health、/status里报home+desk;端口上的服务报的是另一套身份,就当别人的跳过,而不是「它能回答所以是我的」。指针文件绝不静默改写。
setup.py --home发现settings.json已指向别处会直接拒绝,--force先备份再覆盖。环境变量穿得过 detach。 服务用 WMI 起(为了活过 agent 收尾),而 WMI 不继承调用者环境,所以
svc.py把隔离变量写进.cmd包装脚本。缺这一步,隔离配置会静默丢失,数据写回默认目录。选过 home 就不回退旧路径。
profiles/、vault/都在它下面解析。凭据库跟着 home 走。 A 桌面的 handle 在 B 桌面的 broker 上会返回 not found,跨实例填不进去。
账号台账
一个桌面 = 一个平台 = 一个号。台账把这件事记下来,省得号多了靠人脑记:
python scripts/accounts.py add --desktop junjunni --platform github --account me@example.com --cdp 18764 --actions read,post
python scripts/accounts.py list
python scripts/accounts.py remove --desktop junjunni桌面 平台 账号 权限 状态 新增
junjunni github me@example.com read,post unknown 2026-10-18 11:15台账只存元数据,不存密码——密码在各桌面自己的 vault 里。状态列现在是 unknown,
等登录态体检做出来才会填 ok / expired。文件默认在
%LOCALAPPDATA%\moonlightingccounts.json(MOONLIGHTING_ACCOUNTS 可覆盖)。
Agent 闭环(实验,agent/)
scripts/ 是手,agent/ 是套在手上的闭环——不在 wheel 里,从 clone 里跑。
cd agent
python run.py ../tasks/github-login-replay.json # 步骤 + 断言 + 重试 + events.jsonl文件 | 作用 |
|
|
| 断言 DSL: |
| 11 个原子动作,硬白名单 |
| 每步 3 次重试、连败截图、milestone 回滚、 |
| 站点记忆:host+path → 成功动作序列 |
每条步骤都必须带断言,没有 check 的任务会被拒跑——这是「点了没反应还继续走」的唯一根治点。
完成判定只认 done_when,不认模型的自然语言声明。还没有 planner:任务是显式步骤列表。
实测数字
项 | 数字 |
快照压缩 | GitHub 仓库页 56.2KB → 6.0KB(-89%);MDN 42.4KB → 6.0KB(-86%) |
单步耗时 | 快照 83–250ms、click 39–1136ms、goto 586–1797ms |
空点击检出 | 故意点不存在的元素 → 动作 FAIL + 断言 FAIL,10/10 命中 |
失败恢复 | 重试 3 次 → 第 2 次自动截图 → 断言失败 → 回滚 milestone(URL + 表单值) |
内核 | Chrome 155 与 Edge 148 均实测通过,CDP 同一套协议 |
隔离 | 两套实例并存:8765/8899/8897 与 8766/8900/8898, |
目录结构
scripts/
mcp_server.py 标准 MCP stdio server(browser_* + cred_*)
browser_daemon.py 常驻驱动:快照 / 点击 / 输入 / 标签页 / 求值
desk_stream.py 隐藏桌面实时画面(CDP screencast)+ 点击键盘反向通道
vault_broker.py 凭据代理:DPAPI、origin 校验、选择器填充 + 读回校验
cred_dialog.py 用户输入用的掩码弹窗
login_flow.py 完整登录闭环:弹窗 → 填充 → 校验 → 提交 → 失败重弹
desk_chrome.py 在指定私有桌面启动/关闭浏览器(自动找 Chrome 或 Edge)
private_desktop.py Win32 桌面原语(CreateDesktopW、WM_SETTEXT、PrintWindow)
svc.py 静默启停三个服务,启动后自动打开预览
ports.py 端口分配 + 身份校验(默认端口被占就自动换)
auth.py token 文件与请求头
install_mcp.py 给 Claude / Codex / Hermes 写 MCP 配置
templates/ 预览页(/ctl 可操作、/view 只读)
agent/ 闭环层(obs / act / verify / run / mem)
tasks/ 示例任务,含故意失败用例
docs/SKILL.md 长篇实战手册:每个坑都带实测数字平台要求
只支持 Windows,这是设计选择:私有桌面(CreateDesktopW)、DPAPI、静默启动都是 Win32 原语。
浏览器控制这一层本身是可移植的——移植等于重写 private_desktop.py 和 DPAPI 那几处。
鸣谢
站在这些项目上:
项目 | 用在哪 |
无障碍树快照引擎。 | |
playwright-mcp 底层的浏览器驱动。(Apache-2.0) | |
驱动、推流、凭据代理三处的 CDP 传输。 | |
实时画面的 JPEG 处理,以及画在画面上的光标。 |
借鉴了设计思路(未拷贝代码):
browserbase/stagehand —
observe()返回真实选择器而不是像素。这正是本项目坚持用 ref 定位、绝不用坐标的原因。browser-use/browser-use — agent 循环的形态:快照、动作、校验。
web-infra-dev/midscene — 纯 canvas 界面的视觉兜底思路(刻意不默认开启,因为慢)。
支持本项目
Moonlighting 免费、无广告、不收集任何数据,也不会因为没打赏就少更新一次。
如果它帮你省下了来回登录、切换平台、复制验证码的功夫,可以请作者喝杯咖啡 👇
许可证
MIT。playwright-mcp 与 Playwright 的 Apache-2.0 许可适用于对应组件。
Available Tools
27 toolsbrowser_clickBDestructive
Perform click on a web page
Inputs: target (required), button, element, modifiers, +1 more.
browser_closeBDestructive
Close the page
Inputs: no named parameters.
browser_console_messagesCRead-only
Returns all console messages
Inputs: level (required), all, filename.
browser_dragADestructive
Perform drag and drop between two elements
Inputs: endTarget (required), startTarget (required), endElement, startElement.
browser_dropADestructive
Drop files or MIME-typed data onto an element, as if dragged from outside the page. At least one of "paths" or "data" must be provided.
Inputs: target (required), data, paths, element.
browser_emulate_mediaADestructive
Emulate CSS media features for the page, for example switch between the light and dark color scheme. Omitted parameters are left unchanged; null clears an override.
Inputs: media, contrast, colorScheme, forcedColors, +1 more.
browser_evaluateCDestructive
Evaluate JavaScript expression on page or element
Inputs: function (required), target, element, filename.
browser_file_uploadBDestructive
Upload one or multiple files
Inputs: paths.
browser_fill_formBDestructive
Fill multiple form fields
Inputs: fields (required).
browser_findARead-only
Search the accessibility snapshot of the current page for text or a regular expression. Returns matching snapshot nodes with a few lines of surrounding context (like search snippets), each shown under its path from the root of the tree, which is cheaper than capturing the whole snapshot when you only need to locate an element and its ref.
Inputs: text, regex, filename.
browser_handle_dialogCDestructive
Handle a dialog
Inputs: accept (required), promptText.
browser_hoverADestructive
Hover over element on page
Inputs: target (required), element.
browser_network_requestARead-only
Returns full details (headers and body) of a single network request, or a single part if part is set. Use the number from browser_network_requests.
Inputs: index (required), part, filename.
browser_network_requestsARead-only
Returns a numbered list of network requests since loading the page. Use browser_network_request with the number to get full details.
Inputs: static (required), filter, filename.
browser_press_keyBDestructive
Press a key on the keyboard
Inputs: key (required).
browser_resizeBDestructive
Resize the browser window
Inputs: width (required), height (required).
browser_run_code_unsafeADestructive
Run a Playwright code snippet. Unsafe: executes arbitrary JavaScript in the Playwright server process and is RCE-equivalent.
Inputs: code, filename.
browser_select_optionBDestructive
Select an option in a dropdown
Inputs: target (required), values (required), element.
browser_snapshotARead-only
Capture accessibility snapshot of the current page, this is better than screenshot
Inputs: boxes, depth, target, filename.
browser_tabsADestructive
List, create, close, or select a browser tab.
Inputs: action (required), url, index.
browser_take_screenshotARead-only
Take a screenshot of the current page. You can't perform actions based on the screenshot, use browser_snapshot for actions.
Inputs: scale (required), type, target, element, +2 more.
browser_typeBDestructive
Type text into editable element
Inputs: text (required), target (required), slowly, submit, +1 more.
browser_wait_forBRead-only
Wait for text to appear or disappear or a specified time to pass
Inputs: text, time, textGone.
cred_fillA
把已保存的账号密码填进当前页面并可选提交。密码由本机凭据代理解密并注入,不会出现在返回值或模型上下文里。origin 必须与保存时绑定的一致。
Inputs: origin (required), url, handle, cdp_port, +3 more.
cred_listA
列出已保存的凭据(只有 handle/label/origin,没有密码)。
Inputs: no named parameters.
Tool Schema Changelog
Recent tool additions, removals, and schema changes observed during successful MCP inspections.
Loading tool schema changelog…
TDQS
Scored across 27 tools
Most tools map to a distinct browser action (navigate, click, type, hover, drag, screenshot), and paired tools like browser_network_requests/browser_network_request and browser_snapshot/browser_find are clearly differentiated by description. However, some boundaries blur: browser_evaluate vs browser_run_code_unsafe both execute JS, and browser_drop vs browser_drag both involve drag semantics, which could cause occasional misselection.
Names follow a consistent snake_case browser_<verb|noun> pattern (browser_navigate, browser_click, browser_take_screenshot, browser_network_requests) with predictable prefixes. The only deviation is the two cred_* tools, which use a different domain prefix but still keep the same verb_noun style, so it remains readable.
At 27 tools the set sits above the 25-tool 'heavy' threshold, and a few pairs (network_requests/network_request, evaluate/run_code_unsafe) could be consolidated. Still, browser automation is a genuinely broad domain where most tools earn their place, so it is heavy rather than bloated.
The surface covers the full browser lifecycle: navigation, element interaction, forms, tabs, dialogs, file upload, network/console inspection, snapshots, screenshots, media emulation, and credential filling. Minor gaps exist (no explicit cookie/storage-state management or download handling), but core workflows are well covered.
Maintenance
Related MCP Connectors
Let AI agents use your real, signed-in Chrome: tabs, clicks, forms, screenshots and video.
Give AI agents a real cloud Chrome: open sessions, navigate, read pages, click, type and screenshot.
Let your AI agent operate any website through a real, logged-in browser session.
Let AI agents use any website you're logged in to. Sessions stay encrypted on your machine.
Related MCP Servers
- AlicenseNot gradedqualityCmaintenanceEnables AI agents to securely control a user's existing signed-in Chrome browser through isolated tab groups, with strict per-session ownership and no cookie or token exposure.4 npmISC
- AlicenseNot gradedqualityCmaintenanceEnables AI agents to drive a real headful Chrome browser using vision-first computer interactions, with support for multi-agent tab groups, password-manager-filled logins, and human handoff.6 npmMIT
- AlicenseNot gradedqualityBmaintenanceEnables AI agents to control a real, authenticated Chrome browser through native tools for navigation, DOM interaction, form filling, scraping, screenshots, and human-in-the-loop checkpoints while minimizing tokens and latency.MIT
- AlicenseNot gradedqualityBmaintenanceEnables a language model to see and act in the operator's own Chromium-based browser session through the Chrome DevTools Protocol, using the same screenshots and accessibility tree while keeping credentials encrypted and requiring human confirmation for payments, irreversible deletions, and secret changes.MIT