Skip to main content
Glama
Browser MCP Gateway

Browser MCP Gateway

Browser MCP Gateway

Windows gateway around hangwin/mcp-chrome for using an existing Microsoft Edge session from ChatGPT through an OAuth-protected HTTPS endpoint such as Tailscale Funnel.

This repository wraps the upstream project, adds Microsoft Edge Native Messaging registration on Windows, and exposes it through an independent local OAuth sidecar.

ChatGPT plugin icon

The ready-to-upload BMG MCP plugin icon is assets/bmg-mcp-icon.png (128 x 128 PNG, 9,183 bytes). Upload this file manually when creating or editing the ChatGPT MCP plugin; the icon upload must stay below 10,000 bytes. It is a static local/repository asset, not an image URL or an MCP endpoint, and does not change the BMG server's public tool schemas.

Related MCP server: page-bridge

Browser MCP approval

The OAuth-protected public MCP endpoint has a separate BMG Sidecar approval gate before browser dispatch. It covers public upstream tools, dynamic flows, and the BMG show/hide workspace tools. The grant ID is removed before forwarding to the browser. The loopback-only bmgctl local management channel retains its separate local-secret requirement.

BMG adopts the already accepted WCM approval state model with BMG browser-wide scope:

Mode

Behavior

timed (default)

An unapproved browser call freezes its tool name and arguments. request_approval binds a user approval card; approval dispatches the frozen owner call once.

totp

Without a live approval_id calls fail closed. A user-provided six-digit phone code is redeemed through bmg_unlock.

either (recommended after pairing)

Choose independently: approve a frozen action through a card; redeem a user-provided code with bmg_unlock; or tap the paired webpage's temporary unlock button and claim it with bmg_claim_web_unlock. Only one method is needed.

off

No extra BMG approval is needed for public browser calls; OAuth remains mandatory.

One approved approval_id authorizes all public BMG browser tools for a limited time (6 hours by default, configurable from 60 seconds to 7 days), not just the owner call. Several IDs may be active independently. Phone-code and webpage grants never run an outstanding frozen action: after claiming a grant, explicitly retry the browser action using its approval_id. A card-approved frozen action runs once and must not be retried. An unbound request expires after 15 minutes. A bound card has a separate 72-hour decision window; the grant starts only when approved. Changing policy revision revokes pending actions, paired-browser privileges, and all grants. Grants and pending actions are memory-only and vanish on Sidecar restart. Bounded archived decision receipts persist 90 days for display only.

Mobile one-tap web unlock

Bookmark https://<your BMG Funnel host>/bmg/unlock on your phone. On the first visit from each browser, enter one fresh code from your existing BMG authenticator to pair the browser (the code goes only to this BMG webpage, never into ChatGPT). Once paired, just select a duration from 1 minute to 7 days (default 6 hours) and tap 临时解锁 BMG. Return to ChatGPT and say “已解锁,继续”. After explicit permission to use BMG, the model calls bmg_claim_web_unlock with no arguments, claims exactly one owner-issued grant, and passes that approval_id to subsequent BMG browser calls. The page then returns to a ready state without revoking the issued grant. Claiming does not automatically execute a previously frozen browser action.

A freshly issued but unclaimed webpage grant waits at most 5 minutes, and overlapping button presses cannot create extra unclaimed grants. A browser pairing lasts at most 90 days and is revoked by a policy revision; at most eight paired browsers are retained. Pairing tokens are stored only as SHA-256 hashes in ignored .state/bmg-web-browsers.json; cookies are Secure, HttpOnly, and SameSite=Lax. The fixed path uses matching unpredictable CSRF cookies and hidden form tokens, with a one-use phone TOTP at initial pairing. Mobile proxies can alter Origin / Referer, so those headers are not the authorization check; missing or mismatched CSRF/paired-browser cookies fail closed. The page disallows framing and caching. Protect your paired phone/browser. This page only works in either mode and never disables approval or exposes anonymous unlocking. The user must first have enrolled BMG TOTP locally.

scripts/configure-funnel.ps1 -Apply adds the exact /bmg/unlock HTTPS route, alongside the existing BMG OAuth paths; it never creates a wildcard public proxy. The matching disable script removes it. Source changes to the web route require a scoped Sidecar restart; the new bmg_claim_web_unlock tool also requires a ChatGPT plugin tool-schema refresh. The trusted node.exe --test tests\sidecar.test.mjs suite exercises local real-HTTP browser pairing and grant semantics with fake OTP in isolated servers, never production authenticator codes. Run node.exe scripts\check-bmg-web-unlock-public.mjs afterward to check the public HTTPS page and reject a missing-cookie POST without using a real code or issuing a grant.

If ChatGPT blocks or redacts a BMG phone-authenticator approval code, the model should stop requesting the code and immediately call request_approval using the already-frozen browser action's approval_id in either mode. If the intended browser action has not yet produced an ID, first call that browser tool without approval_id to create a pending approval, then display its card. The user still approves or denies the card manually. Do not invent IDs or replay card-executed actions. This fallback does not replace website login SMS/CAPTCHA verification, and does not work in totp-only mode. The corresponding guidance is present in the MCP tool descriptions and approval_required message; changing those descriptions requires ChatGPT BMG Refresh tools.

The MCP App card lives at ui://bmg/approval-v2.html. Card-specific hidden nonce verification, optional Host-session binding, and live state revalidation on remount prevent a reopened card from dispatching a past action. Card continuation never reruns an approved owner call. Public tools: bmg_claim_web_unlock, bmg_unlock, approval_status, request_approval, and app-only get_approval_status / resolve_pending_action. Each exposed browser tool accepts an optional Router-only approval_id.

When an approved owner call returns an upstream tool error, the approval is still consumed and its time-limited grant remains usable; check it through approval_status and issue a separate new browser call only if needed. The original frozen action is never replayed. The Sidecar writes approved_owner_tool_failed events to logs/bmg-upstream-diagnostics.jsonl with a fixed failure kind and route (and HTTP status for invalid upstream responses), without retaining tool arguments, browser output, URLs, or error messages. An archived approval card or its saved widget snapshot is not an authoritative live status check; the card explicitly indicates when embedded live verification is unavailable, which does not by itself imply Sidecar downtime.

Phone pairing (private 6v terminal only)

Use a SHA-1 phone TOTP app with six-digit 30-second codes. Under the Windows account that runs BMG, run the following yourself in a private local terminal. Never run Init or ReadForRouter through model-controlled tools or paste the setup key into ChatGPT.

& 'C:\Users\Songjx\Documents\ChatGPT\browser-mcp-gateway\scripts\manage-bmg-totp.ps1' -Action Init
& 'C:\Users\Songjx\Documents\ChatGPT\browser-mcp-gateway\scripts\manage-bmg-totp.ps1' -Action Confirm -Code '123456'

Replace the sample code with a live phone code. Confirm switches BMG to either mode, revoking previous grants if the policy changes. The permanent 20-byte seed is encrypted using DPAPI CurrentUser outside Git at %LOCALAPPDATA%\BMG\secrets\totp.dpapi. Each code can unlock only once; the accepted clock window matches the WCM five-minute lookback plus one future step, with temporary lockout after repeated failures.

Private terminal policy management:

& 'C:\Users\Songjx\Documents\ChatGPT\browser-mcp-gateway\scripts\set-bmg-approval-mode.ps1' -Status
& 'C:\Users\Songjx\Documents\ChatGPT\browser-mcp-gateway\scripts\set-bmg-approval-mode.ps1' -Mode Either
& 'C:\Users\Songjx\Documents\ChatGPT\browser-mcp-gateway\scripts\set-bmg-approval-mode.ps1' -RevokeAll

The policy file config/approval-policy.json is ignored by Git and monitored by the Sidecar. Before phone pairing, keep timed mode; never enable phone-code authorization without local pairing. With a paired phone, choose either mode. Policy changes are loaded on the next approval call and revoke all grants. When public tool descriptions change, select Refresh tools in ChatGPT plugin management; reconnect only if that refresh is unavailable or fails. The user-only URL and OAuth key print block is in the plugin/app tool refresh section below.

BMG OAuth sidecar

当前正式并行架构如下:

ChatGPT
    |
    | OAuth + MCP
    v
https://your-machine.your-tailnet.ts.net/bmg/mcp
    |
    v
127.0.0.1:18007  BMG OAuth sidecar
    |
    v
127.0.0.1:12306  upstream mcp-chrome
    |
    v
Edge Native Messaging + extension

sidecar 只绑定 127.0.0.1:18007,不修改 upstream mcp-chrome 核心实现,也不拥有 Edge 或 12306 的生命周期。其 issuer 是 https://your-machine.your-tailnet.ts.net/bmg,resource 是 https://your-machine.your-tailnet.ts.net/bmg/mcp。OAuth state、upstream session state、approval secret、日志和 config/.env 均由 BMG 独立保存。access token 默认 1 小时有效;授权码兑换会同时签发 30 天 refresh token,refresh token 每次使用都会轮换,为新会话或 access token 过期后的无人工批准续期提供凭证。

启用并启动 sidecar:

.\scripts\enable-oauth.ps1 -PublicBaseUrl https://your-machine.your-tailnet.ts.net
.\scripts\start.ps1

安装或更新当前用户登录自启(无需管理员权限):

.\scripts\install-autostart.ps1

计划任务 BMG Sidecar 在当前用户登录 Windows 时执行 start.ps1 -EnsureWorkspace。它先启动或复用 BMG sidecar,再检查专用浏览器工作区。工作区准备失败时,即使当前会话已经存在无关 Edge 进程,恢复流程仍会新建一个隐藏的、带一次性 nonce 的 bootstrap Edge 窗口;普通 Edge 窗口和 profile 不会被复用、移动、关闭或改作 BMG 工作区。bootstrap claim 在 30 秒 grace 内保持待认领状态,重复恢复会复用这份 pending state,不覆盖 nonce,也不再创建第二个 bootstrap 窗口。扩展随后可重新连接 native bridge,sidecar 只认领通过 ownership 校验的 bootstrap 窗口。

需在 config/.env 中设置 BMG_WORKSPACE_MODE=1,并在所用 Edge 配置中安装、启用 BMG 扩展及其自动连接。工作区检查失败后依次等待 10、20、40 秒重试;最终失败会让计划任务报错,由任务按每分钟一次、最多三次的设置重试。日志位于 logs/bmg-workspace-startup.log。每次任务触发都会重新核对工作区是否存在;任务完成后不持续监控窗口。Windows 重启后需登录当前用户才触发任务。

手动执行同一启动流程:

.\scripts\start.ps1 -EnsureWorkspace

本机工作区检查接口 /internal/ensure-workspace 仅接受匹配本机地址、无浏览器 Origin 且携带本机认证密钥的 POST 请求,使用 sidecar 自身的共享上游会话。密钥从本地文件读取,不出现在启动参数或日志中。更新该功能后,已运行的 sidecar 需重启以加载新接口。

Local automation client

bmgctl is the supported local boundary for external automation such as CCM. It keeps BMG's approval secret and workspace routing inside the BMG repository/process and exposes JSON-only commands:

.\bmgctl.cmd health
.\bmgctl.cmd workspace
.\bmgctl.cmd call chrome_navigate --args '{"url":"https://chatgpt.com"}'
.\bmgctl.cmd show
.\bmgctl.cmd hide

call uses /internal/tool-call, which has the same loopback/no-Origin/local-secret protection as /internal/ensure-workspace. Page operations are rewritten through the workspace router and therefore target the dedicated BMG GPT workspace rather than an arbitrary normal Edge tab. Ref-based automation tools such as chrome_read_page and CDP-backed chrome_computer are routed through the same dedicated workspace. External consumers should call bmgctl; they should not read BMG OAuth state, the approval-secret file, or private /internal/* implementation details directly.

Required post-maintenance health acceptance

After every maintenance/deployment or restart, verify the entire live browser chain, not just the Sidecar /health response or unit tests: check Sidecar health and that port 18007 has its expected listener/PID; verify independent port 12306 is LISTENING with a live upstream process and that the Sidecar can reach it; inspect the passive upstream/error diagnostics; confirm bmgctl workspace reports the owned workspace; and successfully perform a harmless read-only browser tool call through bmgctl call to prove the extension/native messaging/upstream/Sidecar path works. Report each checked layer and any failures. A bound port alone does not establish browser tool health.

Release gate (all mandatory, no partial-success handoff): distinguish repository/commit success, Sidecar deployment, both process-backed TCP listeners, OAuth reachability, owned workspace, and real browser tool success. Check nested MCP result.isError and content-level failure, not just HTTP status, an outer success flag, or the command's ability to print JSON. Test chrome_get_web_content (textContent: true), chrome_read_page (default), and chrome_read_page with explicit depth: 0. Re-run any previously failing tool with its exact original meaningful arguments. Do not silently omit problematic parameters, call a different tool, or count a successful fallback as proof that the original path works. Transient or intermittent failures remain failed until repeat validation confirms recovery. Never use a workspace recovery action to disguise an unhealthy listener.

If any required check remains failing, skipped, timed out, or unverifiable, classify the release not delivered / degraded even if source changes passed tests, were committed, pushed and Sidecar restarted. Clearly state each passed and failed check, the exact outstanding repair and whether a ChatGPT plugin schema refresh is still required. Do not announce completion or ask for a schema refresh as a replacement for repairing browser functionality. Only a post-repair full acceptance pass permits a healthy/completed conclusion.

Run the maintained scripts/check-bmg-live.mjs live gate on 6v after deployment; it reports only fixed diagnostic categories and never prints browser page content. It exits nonzero if any required listener, OAuth, workspace or tool case fails. The gate explicitly rejects nested MCP tool errors and a chrome_read_page response that succeeds only through the interactive-elements fallback. Its two accessibility tests must keep both their default-argument and explicit depth: 0 variants; they cannot be replaced by unrelated content reads. Unit tests validate that error and fallback responses fail this gate, but unit tests cannot replace the actual live run.

稀疏页面的验收:chrome_read_page 主树成功但内容较少时,上游会保留主树正文,并补充交互元素。验收仅在 reason=sparse_tree、sparse=true、补读来源为 get_interactive_elements、主树正文非空且节点统计为有效正整数时通过,并明确报告 primary_tree_with_interactive_supplement。主树失败、超时或缺少主树证据的备用读取仍然失败;默认参数与显式 depth: 0 两项检查均保留。

With CCM's restricted shell, execute the full live gate via the trusted node.exe --test tests\live-acceptance.test.mjs runner (on 6v), which allows its child-process checks. A restricted-shell listener_probe_unavailable failure indicates an inconclusive probe, not proof that port 12306 is absent. Do not use an inconclusive check to declare either success or upstream failure.

Current live validation (2026-11-06): PASSED. After restarting Edge, the SHA256-verified patched extension was active and BMG's owned workspace was recovered as a hidden window (visible=false). The final trusted live gate passed: 12306 and 18007 had separate live listeners, Sidecar health and public OAuth metadata were reachable, chrome_get_web_content (textContent:true) succeeded, chrome_read_page with default arguments returned a valid primary tree supplemented with interactive elements (primary_tree_with_interactive_supplement), and explicit depth:0 returned the primary tree without fallback. Listener PIDs remained unchanged during the check. The maintained patch in scripts/patch-extension-content-cdp.mjs invokes the original accessibility-tree helper directly through isolated-world chrome.scripting.executeScript instead of sending a content-script message for the primary read. No public MCP schema change was made, so a ChatGPT Refresh tools action is not required.

Workspace recovery finding (2026-11-06): The existing 2026-11-05 timeout adjustment was still active: the Edge/bootstrap and claim subprocesses have a 60-second execution budget, while the inner Win32 nonce-title search is limited to 30 seconds. Following the Edge restart, upstream 12306 recovered but the managed workspace initially did not. The failed claim was not simply caused by an unchanged short outer timeout: the then-current bootstrap nonce had no matching Edge window, although two older BMG-titled windows existed. A one-shot launch of the exact existing bootstrap URL using its persisted nonce produced one matching window; BMG then verified its ownership, claimed and hid it through the normal path. Merely lengthening the timeout cannot fix a missing matching window. The reason the original PowerShell Start-Process launch did not produce that title remains unverified; do not claim it was conclusively identified. Unverified older windows must not be commandeered or closed.

For patch-only builds on the live host, scripts/setup.ps1 -ExtensionOnly -Stage verifies the pinned upstream ZIP and writes a fully patched candidate to .cache/extension-staged. It intentionally skips global npm installation and Edge Native Messaging registration, and does not modify extension/ or reload Edge. Deployment and the full post-deployment live gate are separate required steps; a successful staged build alone does not establish browser health.

Do not restart Edge, the upstream 12306 host, or the Sidecar during health checks. A restart must be justified by a diagnosed failure and restricted to the affected component; verify the full chain again afterward. If 12306 refuses to listen, preserve failure logs and investigate its actual upstream/Native Messaging lifecycle instead of repeatedly restarting 18007 or terminating unrelated Edge processes. This acceptance rule is also recorded in AGENT.md for future project maintenance.

The repository also declares bmgctl as its package bin, so npm link can expose the command on PATH. Consumers that do not use PATH may point directly at bmgctl.cmd.

如需移除:

.\scripts\uninstall-autostart.ps1

停止 sidecar 只校验 BMG PID file、18007 精确 loopback listener 和本地 health,不会停止 12306、Node、Edge 或 Native Messaging:

.\scripts\stop.ps1

本地端到端检查(需要 upstream 12306 和 Edge extension 已运行):

node scripts\test-bmg-e2e.mjs

该检查连续运行两轮 OAuth/PKCE,并验证两轮共享同一个 upstream MCP session。每轮的下游 DELETE 只关闭客户端视角的 session,不会关闭共享 upstream transport;sidecar 停止时关闭自身监听并保留该 session 的非敏感元数据,重启后继续复用。这样兼容 upstream 的 singleton transport 生命周期。

workspace 模式使用 mcp-chrome-bridge@1.0.31 / chrome-mcp-shared@1.0.2 的当前完整公开工具目录,并保留 bridge 动态发现的 flow.<slug> 录制流程工具。BMG 不再维护上游工具 schema 副本;sidecar 只追加 bmg_show_workspace / bmg_hide_workspace,并把所有页面型工具强制路由到 ownership-verified workspace tab/window。上游仍依赖 active/currentWindow 的工具通过 scripts/patch-extension-upstream-tools.mjs 获得显式 target;动态 flow 在执行期把查询、建标签、切换标签和关闭标签都限制在同一 BMG workspace window。chrome_upload_file 的 filePath、fileUrl、base64Data 三种模式由 1.0.31 native file_operation 支持。Windows 原生 File System Access / Save As 对话框仍不在这些工具的控制范围内,必要时使用 bmg_show_workspace 人工处理。

configure-funnel.ps1 和 disable-funnel.ps1 默认只输出预览;本阶段不实际修改 Tailscale Funnel。未来若明确需要应用,才显式使用 -Apply,脚本也只处理 BMG 自己的精确 OAuth/MCP 路径。

Architecture

ChatGPT Web
    |
    | OAuth + HTTPS / MCP
    v
Tailscale Funnel
    |
    v
https://your-machine.your-tailnet.ts.net/bmg/mcp
    |
    v
127.0.0.1:18007  BMG OAuth sidecar
    |
    | local HTTP proxy
    v
127.0.0.1:12306  upstream mcp-chrome
    |
    | Native Messaging
    v
Edge extension + normal Edge tabs

sidecar、upstream、Native Messaging 和 Edge extension 分属独立生命周期。登录启动脚本负责在需要时启动 Edge 并准备工作区;sidecar 通过扩展操作已有浏览器,不创建专用浏览器 profile,也不修改 upstream 核心实现。停止 sidecar 保留 Edge 和工作区窗口。

Upstream versions currently pinned

  • Source: hangwin/mcp-chrome commit f48e71751e00bc09725c7e173423cff4f2ccd12a

  • Native bridge: mcp-chrome-bridge@1.0.31

  • Shared tool catalog: chrome-mcp-shared@1.0.2

  • Extension release: v1.0.0

  • Extension archive SHA256: e0f7edfe84b64fd452deec048fc202cfa33585943da63a06c08e2bbc97770f6a

The source checkout is kept under ignored upstream/mcp-chrome/ for inspection only. Runtime uses the upstream npm bridge package and release extension rather than a locally modified fork.

The pinned bridge tracks the current upstream npm release used by this repository. BMG exposes the full chrome-mcp-shared@1.0.2 public catalog plus dynamic flow.<slug> tools from the bridge. Workspace safety is enforced below the schema layer, so BMG can follow upstream tool additions without duplicating their public definitions.

In workspace mode, BMG appends a short login guide only to the public chrome_read_page tool description in MCP tools/list. It directs the model to prefer verification-code login, ask the user for SMS codes after filling the phone number and requesting the code itself, and share graphical CAPTCHAs for user recognition. For CAPTCHA sharing with CCM, capture the relevant image using chrome_screenshot with savePng=true and storeBase64=false, then publish the saved image using CCM's publish_worker_file. All other upstream tool descriptions and input schemas remain unchanged. This is model guidance, not an automatic login or image-sharing service; it requires both BMG and CCM to be available. After a tool description changes, use Refresh tools in the ChatGPT plugin management panel once the live Sidecar has passed acceptance.

Requirements

  • Windows 10/11

  • Microsoft Edge

  • Git

  • Node.js 20+; Node.js 22 is the preferred fallback for mcp-chrome-bridge@1.0.31 if Node.js 24 cannot compile its better-sqlite3 dependency locally.

  • npm

  • Tailscale with Funnel enabled for this device/tailnet

Install

If GitHub CLI already works on your machine, the easiest clone command is:

gh repo clone beifangzhishi-ops/browser-mcp-gateway
cd browser-mcp-gateway

Plain Git also works when Git itself has network access:

git clone https://github.com/beifangzhishi-ops/browser-mcp-gateway.git
cd browser-mcp-gateway

Then run:

.\scripts\setup.ps1

or double-click:

安装.cmd

Setup will:

  1. Verify GitHub CLI authentication and resolve the configured npm proxy.

  2. Install mcp-chrome-bridge@1.0.31 globally with npm.

  3. Register the installed Native Messaging host directly for Microsoft Edge.

  4. Download and SHA256-verify the pinned upstream extension release into extension/.

It does not register or configure Google Chrome.

Windows 命令说明

仓库脚本在 Windows 上显式调用 gh.exe、node.exe、npm.cmd 和 mcp-chrome-bridge.cmd,避免 PowerShell 执行策略拦截 npm 生成的 .ps1 shim;当 PATH 中存在多个同名程序时取第一个匹配项,状态.cmd 会直接显示这些程序的版本。当前仓库固定 mcp-chrome-bridge@1.0.31,Node.js 要求为 20+;该版本同时提供 doctor / report、native file_operation 和动态 flow 工具发现。排查时不要直接运行 mcp-chrome-bridge.ps1。

Proxy handling

setup.ps1 automatically tries to make command-line downloads follow the same Windows proxy/PAC route used by desktop applications.

Proxy priority is:

  1. -Proxy parameter

  2. BROWSER_MCP_PROXY environment variable

  3. existing HTTPS_PROXY / HTTP_PROXY environment variables

  4. Windows system proxy/PAC resolved separately for GitHub and npm

The resolved proxy is injected only into the setup process for Git, npm, and the extension download. It is not written into your global Git or npm configuration.

CI checks the Windows target-specific proxy lookup and npm handoff behavior directly, rather than requiring a particular helper function name.

Examples:

.\scripts\setup.ps1 -Proxy http://127.0.0.1:7890

or:

$env:BROWSER_MCP_PROXY = "http://127.0.0.1:7890"
.\安装.cmd

When auto-detection works, setup prints a line such as:

Network route for https://github.com/ : http://127.0.0.1:7890 [Windows system proxy/PAC]

Load the extension in Edge

  1. Open Edge normally from the desktop/taskbar.

  2. Visit edge://extensions/.

  3. Enable Developer mode.

  4. Click Load unpacked.

  5. Select this repository's extension directory.

  6. Open the extension and connect it to the native bridge.

The upstream Native Messaging host name is com.chromemcp.nativehost. The default extension ID expected by upstream is hbdgbgagpkpjffpklnamcljpakneikee.

If Edge shows a different unpacked extension ID, rerun:

.\scripts\register-edge.ps1 -ExtensionId YOUR_EDGE_EXTENSION_ID

After the extension connects, the upstream MCP endpoint should be:

http://127.0.0.1:12306/mcp

Tailscale Funnel

After the Edge extension is connected, local port 12306 is listening, and the sidecar health is 200, use an elevated PowerShell only when a Funnel change is explicitly approved:

.\scripts\configure-funnel.ps1
.\scripts\configure-funnel.ps1 -Apply

The first command is preview-only. The second command applies only the BMG route list. The ChatGPT custom MCP server URL is https://your-machine.your-tailnet.ts.net/bmg/mcp.

ChatGPT plugin/app tool refresh

After public MCP tools/list, tool names, descriptions, or input schemas change, Refresh tools in the ChatGPT plugin management panel after confirming the new BMG runtime is healthy. Check that the new tool, such as bmg_claim_web_unlock, is actually present. Reconnect only if the refresh control is unavailable or still fails to load the current schema.

On this machine, BMG keeps the public resource in:

C:\Users\Songjx\Documents\ChatGPT\browser-mcp-gateway\config\.env

and the current OAuth approval key in:

C:\Users\Songjx\Documents\ChatGPT\browser-mcp-gateway\.state\bmg-approval-secret.txt

Only when reconnection is needed, run the following directly in the current PowerShell session to print the two values required by the ChatGPT connection UI. Do not wrap this block inside another powershell.exe -Command "...", because an outer double-quoted command can expand the $... variables before the inner PowerShell receives them.

$bmgKey = (Get-Content -LiteralPath 'C:\Users\Songjx\Documents\ChatGPT\browser-mcp-gateway\.state\bmg-approval-secret.txt' -Raw).Trim()
$bmgUrl = ((Get-Content -LiteralPath 'C:\Users\Songjx\Documents\ChatGPT\browser-mcp-gateway\config\.env' | Where-Object { $_ -match '^BMG_RESOURCE=' } | Select-Object -First 1) -replace '^BMG_RESOURCE=', '').Trim().Trim('"')
Write-Output ("BMG key: " + $bmgKey)
Write-Output ("BMG URL: " + $bmgUrl)

The printed key is local secret material. Enter it only in the plugin/app OAuth approval flow; do not paste it into chat, logs, issues, or committed files. The repository defaults BMG_APPROVAL_SECRET_FILE to .state/bmg-approval-secret.txt; if that configuration is intentionally changed, use the configured path instead.

Status

.\scripts\status.ps1

This checks Node/npm, the installed bridge, Edge Native Messaging registration, local MCP port 12306, and Tailscale Funnel status.

Important security note

GPT dedicated browser workspace

Recovery timeout and managed tab protection (2026-11-05)

The Edge bootstrap subprocess and the owned-window claim subprocess each have a 60-second process execution timeout. Claiming a new HWND allows up to 30 seconds for the nonce-marked window to appear; ordinary inspect/show/hide helper timeouts are unchanged. Startup recovery uses the same 60-second bootstrap subprocess limit. A recovery attempt may involve multiple sequential operations, so 60 seconds is a per-subprocess budget, not a guarantee that the entire recovery cycle completes in 60 seconds.

In workspace mode, public chrome_close_tabs requests are rejected before browser dispatch, regardless of their supplied tab IDs or URL. This preserves the managed workspace anchor tab and prevents a page-cleanup action from closing the only Edge window supporting the bridge. Pause video playback with a targeted page action, or navigate the managed tab to another page instead; the existing internal idle cleanup may still close verified extra tabs while retaining the anchor. No Edge extension rebuild or public MCP schema change is required.

Upstream disconnection diagnostics (2026-11-02)

The BMG Sidecar writes logs/bmg-upstream-diagnostics.jsonl to help investigate why the browser bridge on local port 12306 becomes unavailable while the OAuth Sidecar on 18007 remains healthy. After Sidecar startup, a passive TCP-connect probe runs every 30 seconds; it neither executes browser tools nor launches Edge. monitor_started and monitor_stopped mark Sidecar lifecycle. upstream_state_changed is written on the first observation and on later up/down transitions (including the previous state and duration). On Windows a transition also captures machine-wide msedge.exe and node.exe PID/count snapshots when permitted; these are contextual evidence not proof of native-host ownership or an exit reason. Failed process inspection is reported with a code and does not interrupt monitoring.

The same log includes upstream_request_failed with stage and safe error code/HTTP status, upstream_session_rejected / upstream_session_invalidated, and workspace_recovery_* / workspace_bootstrap_* events. Read these alongside logs/bmg-workspace-startup.log and logs/bmg-window-transitions.jsonl. TCP failures show the failure window to approximately 30 seconds but do not establish whether Edge closed normally, Native Messaging exited, Windows killed a process, or a restart removed the listener. Very short outages between probes may be missed; a listening TCP port alone is not an MCP tool-health check. Event data intentionally excludes page URLs, titles, tool arguments, authentication material, nonce/ownership markers, and raw exception messages. The log is ignored by Git and remains across Sidecar restarts, rotating at 10 MiB to one .jsonl.1 backup (at most about 20 MiB total). Monitoring is observation-only: idle cleanup and probe ticks do not recover/relaunch Edge. No public MCP schema change or ChatGPT tool refresh is required for diagnostics alone.

For a quick local read of recent events, run:

Get-Content -LiteralPath 'C:\Users\Songjx\Documents\ChatGPT\browser-mcp-gateway\logs\bmg-upstream-diagnostics.jsonl' -Tail 80

Window transition diagnostics (2026-10-28)

The sidecar appends one JSON object per event to logs/bmg-window-transitions.jsonl, retaining the history across service restarts. Explicit bmg_show_workspace and bmg_hide_workspace calls emit command and command-result; the underlying ownership-verified window helper emits paired request / result entries for explicit calls and automatic operations. reason distinguishes explicit-show, explicit-hide, revalidate, page-foreground, page-background, page-settle, page-error, element-picker, and idle-cleanup. sequence correlates one helper request and result within a single sidecar process.

Results distinguish Win32 visible / minimized / appWindow / toolWindow from taskbarApi (AddTab or DeleteTab) and taskbarApiSucceeded. taskbarUiStatus: not_verified is intentional: Windows COM success does not independently establish that Explorer's taskbar UI removed its entry. Logs contain no URLs, page titles, OAuth secrets, or ownership marker. The trace is diagnostic, not a guarantee of taskbar UI state. The log is local and ignored by Git; inspecting it does not require showing the workspace.

The 2026-10-28 change also forwards the explicit activate value from the sidecar to the Win32 show helper. The MCP tool names and schemas are unchanged, so no ChatGPT tool refresh is required for that change.

Live desktop check (2026-10-28, Windows on 6v): two cycles of non-activating bmgctl show followed by bmgctl hide succeeded against the same owned HWND 1316646; each show returned visible=true, each hide returned visible=false, and the foreground did not transfer to BMG. Ownership marker, non-minimized state and the expected Win32 window styles remained correct. A final repeated hide also succeeded, leaving the workspace hidden. The diagnostic log recorded successful AddTab / DeleteTab COM returns. Explorer's rendered taskbar entries were not independently verified, so this test does not establish that a visual taskbar residue cannot recur.

Non-activating display uses Windows SW_SHOWNA (8) to preserve the workspace's current size, position, and maximized state without taking focus. The display helper does not resize the window or change its saved normal bounds. The minimized-window preparation inside the hide helper is separate. Validation: automated tests cover the exact Win32 flag; a real desktop maximize/show/hide cycle has not yet been retested after this change (2026-10-23).

窗口显示与前台激活

这两种操作不同,BMG 的 MCP 工具也明确区分:

  • 显示窗口:调用 bmg_show_workspace,不传参数或传 {"activate":false}。使 BMG 工作区在桌面及任务栏可见,但不主动抢占当前前台窗口或键盘焦点。适用于“显示窗口”“让我看到窗口”。

  • 弹到前台:调用 bmg_show_workspace 并传 {"activate":true}。显示 BMG 工作区并向 Windows 请求激活,让它成为当前操作窗口。适用于“弹到前台”“切换到 BMG”“聚焦工作区”。Windows 仍可能限制跨进程前台激活;结果中的 foreground 表示该请求的执行结果。

  • 真正隐藏:调用 bmg_hide_workspace,关闭 BMG 窗口的可见性及任务栏入口,不关闭浏览器工作区。

本机控制命令对应 bmgctl show(只显示)和 bmgctl show --activate(请求前台)。页面自动化默认保持后台;显式传入 background:false 的页面工具及人工选取元素操作会请求前台。以上操作只针对归属已验证的 BMG 工作区窗口。

在本机被忽略的 config/.env 中设置 BMG_WORKSPACE_MODE=1 后,页面定向 MCP 工具会固定到一个 BMG 专属 Edge 窗口/标签页。该窗口仍使用用户现有 Edge 配置,因此继续共享 Cookie、登录态和扩展。sidecar 会把 windowId、tabId、Win32 hwnd、随机 ownership marker、Edge PID/进程启动时间和显隐状态写入 .state/bmg-workspace.json。导航始终复用该工作区,公开的 chrome_close_tabs 现在会被拒绝,以免误关受管理的工作区标签页;清理网页请暂停播放或导航到其他页面。页面工具的 background 语义为后台优先:调用方未指定时按 true 处理,不激活标签页或聚焦窗口;只有显式传入 background:false 才会显示并保持该工作区可见。URL 导航在返回成功前会等待目标 URL(或实际重定向 URL)进入稳定完成状态,避免紧接着的 DOM 调用打到旧页面。

正式隐藏方式现在是 Windows ShowWindowAsync(SW_HIDE),不再把窗口移动到 (-32000,-32000)。首次认领时,BMG 会给目标 HWND 设置进程内 Win32 属性 marker;后续隐藏、显示和 sidecar 重启恢复都必须同时验证 HWND、marker 和 msedge 进程身份,已有进程指纹时还会核对 PID/启动时间。无法证明正式 workspace 归属时只退休本地记录,不关闭或修改那个可能已经属于用户的窗口。隐藏操作使用 SWP_NOMOVE|SWP_NOSIZE,并验证隐藏前后窗口几何不变;显示操作同样不强制居中或改尺寸;显式调用 bmg_show_workspace 默认只显示窗口,不主动抢占焦点;指定 activate:true,或对支持后台参数的页面工具显式传入 background:false,才会请求前台激活。工具调用如果在临时显示后因 upstream transport 错误失败,sidecar 也会 best-effort 把 ownership-verified workspace 恢复为 true-hidden,避免留下可见任务栏项;隐藏维护失败不会覆盖原始浏览器错误或成功结果。

2026-10-07 修复任务栏残留:隐藏流程先执行 SW_HIDE 并等待窗口不可见,再移除 WS_EX_APPWINDOW、设置 WS_EX_TOOLWINDOW,最后通过 ITaskbarList::DeleteTab 清理同一归属已验证 HWND 的任务栏条目;重复隐藏也会执行清理。显式显示完成后通过 AddTab 恢复任务栏入口。COM 初始化或任务栏操作失败会使 helper 报错,不能仅凭 visible=false 宣称任务栏验收通过。该流程继续保持窗口位置和尺寸,并在清理前复核 marker、PID 和进程启动时间。顺序依据:微软关于任务栏残留的说明。停止闪烁也仅作用于已确认的工作区 HWND,不再遍历同一 Edge 进程的其他窗口。

本机验证(2026-10-07):自动化检查 46/46 通过,PowerShell 语法和 C# COM 组件编译通过;正式 BMG 链路的重复隐藏、显示后再隐藏以及隐藏中读取 about:blank 内容成功。交互桌面任务栏可访问性信息显示 Edge 仅为“已固定”,没有运行窗口条目。独立控制器使用实际 100ms 空闲定时器完成清理和重新隐藏,正式空闲配置保持不变。正式 sidecar 从 PID 48624 重启为 51716 后,仍复用原窗口和标签,窗口几何、marker、Edge PID 和启动时间保持一致。Alt+Tab 测试未取得切换面板,因此此项尚未完成视觉验收;本次浏览器侧只有 BMG 一个窗口,未实测普通 Edge 窗口同时存在时的行为,也未执行 Windows 重启或重新登录。

新工作区创建不再传固定的 480×360 尺寸。scripts/patch-extension-web-content.mjs 只负责内容/交互与窗口几何补丁;导航相关修复统一由 scripts/patch-extension-navigation.mjs 维护。调用方未指定宽高时,chrome.windows.create 不再自动补入上游默认的 1280×720;导航若已给出显式 tabId,会优先直接操作该标签,不再先做全局 URL pattern 查询。URL pattern 只对 HTTP/HTTPS 生成,about: 等非 HTTP(S) scheme 不进入 match-pattern 查询;127.0.0.1、IPv6 和 localhost 也不会生成非法的 www. 变体。这样 workspace bootstrap 和空闲清理的 about:blank 都不会再因 URL pattern 校验在真正导航前失败。旧的 v1 工作区状态没有可验证 marker,会被安全退休;不会凭旧 HWND 去移动、显示或关闭旧窗口。因此其他机器若仍保留由 v1 对应的旧窗口,该窗口可能继续存在到用户关闭它或 Edge 重启,BMG 不会为了去重而猜测归属后强行清理。

登录准备时,scripts/ensure-edge.ps1 会为失败的工作区准备启动一个隐藏的 /workspace-bootstrap?nonce=... 窗口,并把 nonce 与 ownership marker 写入 .state/bmg-edge-bootstrap.json。已有无关 Edge 进程不会阻止该恢复启动;若该 bootstrap state 仍处于 30 秒 claim grace 内,后续恢复调用会直接复用 pending claim,不再启动重复窗口。超过 grace 后也不会直接覆盖 state:恢复流程必须先按 marker(初始 marker 尚未建立时按 exact nonce bootstrap 标题)定位并关闭那个 BMG-owned HWND,或确认它已经不存在,才允许创建新的 bootstrap。归属不明确或关闭失败时本次恢复直接失败,不通过不断创建窗口重试;整个过程只关闭精确 BMG HWND,不结束 msedge.exe 进程,也不修改普通 Edge 窗口。若被退休窗口恰好是最后一个 Native Messaging Edge 窗口,12306 可以短暂正常退出;同一次按需恢复随后立即启动新 bootstrap,由扩展重新建立 native bridge。helper 会立即尝试 true-hide,新窗口最终仍由 sidecar 完成 ownership claim。

普通使用请求遵循同一恢复规则:首次工作区准备失败后,sidecar 调用一次 ensure-edge.ps1,随后按 1 秒、2.5 秒、5 秒间隔重试工作区准备。仅有后台残留 Edge 进程不能再阻断恢复;新 bootstrap 窗口只用于重新建立 extension/native bridge 链路,在 ownership 校验完成前 BMG 不会把它当作正式 workspace。恢复日志只记录简洁的 reuse / retire / create / blocked / initial-hide-failed 生命周期动作及调用来源类别,不记录本机认证密钥。

BMG_WORKSPACE_IDLE_TIMEOUT_SECONDS 控制自动清理,默认 28800 秒(8 小时)。浏览器工具活动会刷新期限;到期后 BMG 重新验证 ownership,通过内部 chrome_javascript/CDP 在明确的保留 tabId 上执行 location.replace("about:blank"),然后只关闭同一受控窗口中的额外标签页,并保留一个真正隐藏的空白标签页。这里不再经过 chrome_navigate 的 URL-pattern 预检查,因此即使当前 Edge 尚未重新加载最新 extension patch,空闲清理也不受 about:///* 缺陷影响。若浏览器/12306 已被用户关闭,idle cleanup 只记录失败并按原周期重排,不会在后台主动重新启动 Edge;下一次真正需要 workspace 的请求才进入按需 recovery。设置为 0 可禁用空闲清理。读历史、书签、窗口列表等全局工具仍不是 window-scoped。

自动化验证目前覆盖:startup nonce 去重认领、stale bootstrap 保留到显式 retirement、按需恢复、v2 ownership 恢复、过期 HWND/marker 安全退休、v1 状态迁移、不改几何的 Win32 helper 静态约束、空闲清理、默认后台与显式前台语义、upstream transport 失败后的重新隐藏、截图参数绑定、显式 tab 优先导航、非 HTTP(S) URL pattern 安全处理、导航 settle,以及扩展补丁幂等;CCM trusted-node 全量测试当前为 46/46 通过。2026-10-02 已实机验证恢复链路:故障开始时 18007 正常而 12306 未监听,且仅有后台 Edge 进程;恢复流程成功创建 ownership bootstrap,触发 Edge extension/native host 重连,随后 127.0.0.1:12306 恢复监听,bmgctl workspace 返回 success=true 且工作区保持 visible=false,正式 BMG MCP 调用恢复成功。2026-09-20 已在正式 @BMG 链路上完成真实桌面验收:工作区创建成功并返回 visible=false/hidden=true,精确 HWND 检查确认窗口未最小化、几何仍为自然位置且未出现 -32000,同时普通 Edge 窗口保持正常;隐藏状态下 chrome_get_web_content 也成功读取 bootstrap 页面。2026-09-26 已在正式 BMG 工作区完成空闲清理实机验收:先以直连 upstream 在同一受控窗口创建测试标签,再把临时 runtime 的 idle timeout 缩短到 100ms;timer 自动触发后通过 chrome_javascript/CDP 将保留标签重置为 about:blank、关闭额外标签,并按周期重新排程。最终正式 sidecar 读取结果为 tabCount=1、保留 tabId=2042621324、URL 为 about:blank、workspace visible=false,2026-10-17 起,本机正式配置为 BMG_WORKSPACE_IDLE_TIMEOUT_SECONDS=28800(8 小时)。

Upstream parity validation (2026-10-01)

The mcp-chrome-bridge@1.0.31 package layout and CLI were verified against a workspace-local install, including dist/run_host.bat, the register / fix-permissions / update-port commands, and the new doctor / report commands. Its bundled chrome-mcp-shared@1.0.2 exposes 27 static public tools, and BMG's E2E catalog check now requires all 27 plus the two BMG workspace controls. The pinned extension release was rebuilt from the verified archive and all four BMG patchers were applied; the generated background.js passes node --check. CCM trusted-node tests pass 49/49.

The Controller runs the pinned mcp-chrome-bridge@1.0.31 / chrome-mcp-shared@1.0.2 upstream. The public BMG catalog has 27 upstream static tools, two BMG workspace controls, and six BMG approval tools (35 static tools plus discovered dynamic flow tools). The trusted CCM Node suite covers OAuth, workspace ownership, approval cards, phone codes, webpage one-tap unlock, the combined either mode and grant reuse. The browser approval mode defaults to timed until phone pairing, when Confirm selects either mode.

The upstream project exposes powerful browser capabilities. BMG therefore keeps the upstream listener local and requires the sidecar OAuth layer before forwarding any MCP request.

Treat the public MCP URL as a privileged automation endpoint even though OAuth is required. Keep the upstream listener bound to localhost, protect the public route with the sidecar, and avoid exposing any unprotected browser-control port. The generated OAuth state, approval secret, workspace state, logs, and config/.env are local-only and must not be committed.

真正隐藏窗口的独立验证(2026-09-19)

本机在正式接入前使用独立测试窗口验证了 Windows ShowWindowAsync(SW_HIDE);该次试验本身没有修改当时的正式工作区隐藏方式。试验通过现有上游 MCP 调用 chrome_screenshot,明确传入测试 tabId、windowId、background: true、fullPage: false,绕开当时 sidecar 的屏幕外维护步骤。当前正式实现已经改用真正隐藏,但下列结果仍作为真实窗口证据保留。

验证结果:

  • 窗口从可见变为不可见,保持非最小化状态,边界始终为 (960, 12, 1920, 732)。

  • 隐藏后仍能正常取得当前视口截图;隐藏期间导航到新页面,内容读取与截图均显示新内容。

  • Windows 窗口事件记录中,隐藏期间没有重新显示事件。

  • 隐藏状态下新页面的截图与恢复可见后的截图文件完全一致。

  • 测试窗口已恢复并关闭。

第二轮使用 --hidden-only:创建后隐藏,等待 10 秒再截图,完成后直接关闭测试标签页。两次截图前后均为不可见、坐标保持 (0, 10, 960, 730),记录中只有隐藏事件;阶段二截图和内容读取成功。用户在两轮试验中均观察到短暂闪现,因此尚不能宣称整套流程无闪现。创建测试窗口到隐藏之间存在可见阶段,需与截图阶段分别验证;未把用户观察归因于“小概率并发”。

这证明普通后台截图可与真正隐藏配合。当前代码已调整隐藏/显示脚本以识别真正不可见且 ownership-verified 的 HWND,并用自动化测试覆盖 sidecar 恢复;但整页截图、元素截图、长期真实隐藏、用户同时操作普通 Edge、普通窗口位置记忆和实际重启登录仍未完成真实桌面验收。

重复试验前需连接 BMG 扩展,并明确允许操作新建测试窗口;脚本依赖 Windows、Node.js 和 Python。执行期间会创建一个 960×720 的测试窗口、隐藏并恢复它,最后关闭其唯一测试标签页;不操作已有用户窗口。创建和关闭测试窗口仍可能更新 Edge 的普通窗口大小/位置记忆。

node scripts\test-hidden-window.mjs
# 隐藏后直接关闭,不主动恢复窗口
node scripts\test-hidden-window.mjs --hidden-only

截图及记录写入 .state/隐藏窗口试验/。若上游会话已失效,脚本会重新初始化共享 MCP 会话并保存连接信息;不重启服务,不删除共享会话。Windows 控制器 scripts/probe-hidden-window.py 每次操作前检查随机标题标记、进程 ID 和 msedge.exe 身份,使用窗口事件监听记录显示/隐藏变化。参考:Windows 窗口隐藏接口、CDP 截图接口。

Why this repository exists

mcp-chrome already provides the mature browser integration: tabs, existing login state, page extraction, interactions, screenshots, network tools, history/bookmarks, and Streamable HTTP MCP. The only local glue needed for this setup is:

  • Edge Native Messaging registration on Windows (upstream currently registers Chrome/Chromium only)

  • repeatable installation/version pinning

  • automatic Windows proxy/PAC handoff for setup

  • Tailscale Funnel configuration

That keeps this repository small and makes upstream updates replaceable instead of maintaining another browser automation implementation.

License

This repository is licensed under the MIT License. Upstream components such as hangwin/mcp-chrome retain their own copyright notices and licenses; the pinned upstream project is also MIT-licensed.

Tool Schema Changelog

Recent tool additions, removals, and schema changes observed during successful MCP inspections.

Loading tool schema changelog…

    Related MCP Connectors

    Related MCP Servers

    • A
      license
      Not graded
      quality
      B
      maintenance
      Enables AI agents to control local Chrome/Edge browser profiles through MCP, including tab groups, bounded DOM actions, observations, and optional encrypted credential use.
      MIT
    • A
      license
      Not graded
      quality
      B
      maintenance
      Enables an AI agent to read and operate the Chrome or Edge tabs you are already using — titles, URLs, text, HTML, screenshots, clicks, typing, scrolling, and tab management — via a browser extension and native messaging, without restarting the browser, switching profiles, or opening a debug port. It exposes these capabilities as 19 MCP tools, with access limited to explicitly shared tabs and hardened by per-tab, origin-bound grants plus domain allow/deny lists.
      2
      MIT
    • A
      license
      A
      quality
      C
      maintenance
      Enables MCP clients to drive a running Microsoft Edge browser over the Chrome DevTools Protocol, exposing 16 tools for navigation, clicking, typing, screenshots, page-content reading, and tab management. Runs in stdio mode for per-client processes or as a shared HTTP endpoint so multiple agents can operate the same browser session.
      16
      MIT
    • A
      license
      Not graded
      quality
      C
      maintenance
      Enables MCP clients to reuse an existing logged-in Microsoft Edge session for tab management, page reading, clicking, keyboard input, and file transfers. Also supports Jupyter experiment planning, submission, status checks, and result downloads.
      2
      MIT