nexus-browser 2.9.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,20 @@
1
+ # Python
2
+ __pycache__/
3
+ *.py[cod]
4
+ *$py.class
5
+ *.log
6
+ .venv/
7
+
8
+ # Windows / OS
9
+ Thumbs.db
10
+ ehthumbs.db
11
+ Desktop.ini
12
+ .DS_Store
13
+
14
+ # Backups
15
+ *.bak
16
+ *.tmp
17
+
18
+ # Development caches (normally stored outside OneDrive by test.ps1)
19
+ .pytest_cache/
20
+ .ruff_cache/
@@ -0,0 +1,284 @@
1
+ # Changelog
2
+
3
+ All notable changes to this project are documented in this file.
4
+ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and versions follow [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
5
+
6
+ ## [Unreleased]
7
+
8
+ ### Added
9
+ - PyPI package `nexus-browser` (the MCP server, command `nexus-browser`): the wheel mirrors `client/` and
10
+ `native-host/` unchanged under `nexus_browser/` (hatchling force-include); `install.ps1` and the three agents keep
11
+ running from the repository (`[tool.uv] package = false`). Published by Trusted Publishing
12
+ (`.github/workflows/publish.yml`), checked from the built wheel (initialize, 47 tools).
13
+ - README: animated demo recorded from the real tool; install from PyPI.
14
+
15
+ ### Changed
16
+ - Bridge not reachable and no token: the error now points to the one-time install (install.ps1).
17
+
18
+ ## [Unreleased]
19
+
20
+ ### Added
21
+
22
+ - `SECURITY.md`: private vulnerability reporting.
23
+
24
+ ### Changed
25
+
26
+ - Repository renamed to `pironjulien/NexusBrowser`; GitHub redirects the old address.
27
+
28
+ ## [2.9.0] - 2026-10-18 — Secrets masked in images and console logs
29
+
30
+ Re-run `install.ps1`, then RELOAD the extension (capabilities `capture-field-masking` and `console-masking`
31
+ advertised by ping).
32
+
33
+ ### Security
34
+ - Screenshots, PDFs and Set-of-Mark captures no longer show a password revealed by a "show" button, a card number or
35
+ a code: before `Page.captureScreenshot` / `Page.printToPDF` (also through `browser_cdp_send`), sensitive fields of
36
+ every frame (shadow DOM, same-origin and cross-origin iframes) get `-webkit-text-security: disc`, and their own
37
+ inline style is put back right after. A capture whose page cannot be masked (dialog open) is refused. Checked in
38
+ the real Chrome (`test.ps1 -Full`): two values of the same length give the same image, the same field shown as an
39
+ ordinary field gives a different one (screenshot, PDF, cross-origin iframe).
40
+ - Sensitive fields are also recognized by name or id, camelCase included: `cardNumber`, `cc_number`, `cardCvc`,
41
+ `otp`, `pin`, `securityCode`, `iban`... (`cc` alone, `spinner`, `passenger`, `option` are not secrets).
42
+ - `browser_logs` masks console messages like network bodies: secret keys, URL parameters, object previews
43
+ (`{name, value}`), tokens quoted in a text (`Bearer …`, `ghp_…`, JWT).
44
+ - Network log: card number, card code, one-time code, PIN and IBAN keys masked in bodies and URL parameters.
45
+ - `NEXUS_CHROME_EXTRA_ARGS` is now an allowlist (`--lang`, `--accept-lang`, `--variations-override-country`,
46
+ `--disable-features` with `Glic*` features only, `--force-dark-mode`): the former blocklist let through flags that
47
+ weaken Chrome (`--disable-web-security`, `--disable-features=IsolateOrigins`...).
48
+
49
+ ### Fixed
50
+ - Element screenshots (`selector`, `mark`) were blank when Chrome's page zoom is not 100% (seen at 90%): the clip is
51
+ now converted with the page zoom reported by `Page.getLayoutMetrics`.
52
+
53
+ ### Added
54
+ - Real Chrome test of masking in a cross-origin iframe (reads and capture).
55
+ - Real Chrome check that Julien's tabs are left intact now ignores tabs of other agents (they may close theirs during
56
+ the run).
57
+
58
+ - Every MCP tool has a readable `title` in its annotations, next to its four hints.
59
+ - Startup test: the MCP server answers `initialize` and `tools/list` with no Chrome, bridge or extension, on Windows
60
+ and on Linux (as MCP directories such as Glama check it); outside Windows the emergency stop keeps watching its
61
+ file and skips the global hotkey.
62
+ - GitHub Actions: `test.ps1` on Windows and the startup test on Linux, with a Tests badge; `glama.json`.
63
+ - README: Privacy section.
64
+
65
+ ### Changed
66
+ - Published as NexusBrowser in the public repository `pironjulien/chrome-computer-use` (formerly
67
+ `antigravity-chrome-sidepanel`, then `antigravity-browser-use`; GitHub redirects the old URLs). README rewritten in
68
+ English for public use.
69
+
70
+ ## [2.8.1] - 2026-10-18 — Screenshots of background tabs no longer freeze
71
+
72
+ Re-run `install.ps1`, then RELOAD the extension (capability `capture-screencast` advertised by ping).
73
+
74
+ ### Fixed
75
+ - `browser_screenshot` and `browser_vision_inspect` froze for 12 to 20 s (bridge timeout) in a background tab:
76
+ Chrome stops drawing frames for a hidden tab, so `Page.captureScreenshot` waited after any scroll or change.
77
+ Measured on the real Chrome (October 18, 20 captures alternating with scrolls): 11 to 13 freezes out of 20 for the
78
+ plain capture, `Emulation.setFocusEmulationEnabled` and `captureBeyondViewport` (still 7/12), forcing the device
79
+ metrics (7/12); `fromSurface: false` is refused by Chrome ("Only screenshots from surface are allowed"). A tiny
80
+ `Page.startScreencast` (16×16, never read) held only for the duration of the capture keeps the renderer drawing:
81
+ 0 freezes out of 40, about 0.1 s per capture, correct image of the scrolled view; the tab stays in the background.
82
+ If Chrome refuses the screencast, the capture runs as before; it is always stopped, even when the capture fails.
83
+
84
+ ### Changed
85
+ - Chrome relaunch (`launch_chrome`): only `--profile-directory=Default` by default. Extra flags are opt-in
86
+ through the `NEXUS_CHROME_EXTRA_ARGS` environment variable (`--name` or `--name=value` separated by spaces);
87
+ anything else, and flags that load or run code (`--load-extension`, `--remote-debugging-*`, command prefixes,
88
+ `--user-data-dir`, `--no-sandbox`...), are ignored.
89
+ - Agent guide and docstrings: repository-relative paths instead of the author's folders.
90
+
91
+ ### Removed
92
+ - Live validation: the 3 retries of `browser_vision_inspect` (`gels_de_capture`) are gone, a single capture is
93
+ expected to succeed.
94
+
95
+ ## [2.8.0] - 2026-10-18 — October 18 audit: masked passwords, corrected cookie promise, MCP SDK 2
96
+
97
+ Re-run `install.ps1` (with Antigravity closed), then RELOAD the extension in `chrome://extensions` (capabilities
98
+ `sensitive-field-masking`, `cdp-reload-no-script` and `ax-value-masking` advertised by ping), then open a new
99
+ conversation in each agent.
100
+
101
+ ### Security
102
+ - Plaintext passwords (severe): `browser_content` (default interactive mode), `browser_vision_inspect`,
103
+ `browser_find` and the read-back of `browser_fill` returned field values, passwords included; `browser_axtree`
104
+ returned card numbers and codes (Chrome only masks passwords there, observed in a real run). The value is never
105
+ returned for a `type=password` field or one whose `autocomplete` is current-password, new-password,
106
+ one-time-code, cc-number, cc-csc or cc-exp (including a "show password" field switched to `type=text`):
107
+ `[valeur masquée]` is returned instead. `browser_content(mode="html")`: the `value` attribute of these fields is
108
+ masked in a copy of the DOM (React copies it from the input), the page itself is untouched. `browser_fill` still
109
+ reads the real value back from the page (`verified` stays accurate); only the output is masked, including the
110
+ expected value and the JavaScript fallback. A single definition of sensitive fields, in the localization kit,
111
+ injected into every page script.
112
+ - Antigravity review (October 18): the rendered text of a sensitive field (textarea, contenteditable) was not
113
+ masked; a "show password" field without a recognized autocomplete is now identified by its name or id (password,
114
+ pwd, mot de passe, mdp, cvc...), never a container; non-input components carrying a sensitive autocomplete are
115
+ masked in the accessibility tree and the HTML.
116
+ - `browser_cdp_send`: `Page.reload` accepted `scriptToEvaluateOnLoad` passed through unchanged (a script injected
117
+ into the page world on load, like `Page.addScriptToEvaluateOnNewDocument`, which is refused). Now refused before
118
+ reaching Chrome, with a clear message; a plain reload is still allowed.
119
+
120
+ ### Fixed
121
+ - Incorrect promise: "cookies sealed outside localhost". `browser_eval` and `Runtime.evaluate` (both worlds share the
122
+ document) can read `document.cookie` (non-HttpOnly cookies), localStorage, sessionStorage, IndexedDB and the result
123
+ of a `fetch` sent with the user's session, without masking. Kept as is (maximum autonomy; pattern-based masking
124
+ would be bypassed by the slightest `btoa`) and stated clearly in the tool, the server instructions, the skill and
125
+ the README: masking applies to the dedicated tools, not to a script nor to raw `browser_cdp_send` responses
126
+ (`DOM.getOuterHTML`, `Accessibility.getFullAXTree`).
127
+
128
+ ### Changed
129
+ - MCP SDK 2.3.0 (instead of 1.30.0; the 1.x branch only receives security fixes): `MCPServer` (formerly
130
+ `FastMCP`), advertised version read from `VERSION`.
131
+ - MCP annotations on all 47 tools (`readOnlyHint`, `destructiveHint`, `idempotentHint`, `openWorldHint`): page
132
+ reads, local reads, tab management, navigation, and page actions marked destructive (in the user's real sessions, a
133
+ click, an Enter key press, a checked box or a script can send, delete or confirm). A tool marked "read-only" only
134
+ sends the bridge commands from its read list (tested), with one deliberate exception: `browser_get_cookies`, which
135
+ modifies nothing but is still blocked by the emergency stop.
136
+ - Tools are typed exactly (the decorator no longer turned them into `Any`): mistyped test calls fixed.
137
+
138
+ ### Tests
139
+ - Node: content, visual map, search, HTML and accessibility tree contain no sensitive value; typing into a password
140
+ field, into a card field rewritten by the page, and through the JavaScript fallback returns no value;
141
+ `Page.reload` with a script refused before reaching Chrome. Mutation checked: without masking, the three masking
142
+ tests fail.
143
+ - Python: SDK 2, complete annotations consistent with the bridge read list, accurate wording about `browser_eval`
144
+ (tool, instructions, both copies of the skill, README).
145
+ - Real-world validation (`--full-live`): password and card never read (content, HTML, visual map, search, Chrome
146
+ accessibility tree), password entry verified and masked, `Page.reload` with a script refused.
147
+
148
+ ### Known issues
149
+ - `Page.captureScreenshot` sometimes hangs for 12 to 20 s in a background tab (both `browser_screenshot` and
150
+ `browser_vision_inspect`, measured on October 18, unrelated to this version): the real-world validation retries the
151
+ visual map up to 3 times and counts the hangs. Root cause to be addressed separately.
152
+
153
+ ## [Unpublished] - 2026-10-17 — Antigravity review: regressions fixed
154
+
155
+ Version 2.7.1. Re-run `install.ps1` (with Antigravity closed), then RELOAD the extension in `chrome://extensions`
156
+ (capabilities `orphan-tabs`, `cdp-page-world`, `local-cookie-values`, `body-masking-allowlist` advertised by ping),
157
+ then open a new conversation in each agent.
158
+
159
+ ### Fixed
160
+ - Orphaned tabs (severe): after an MCP server restart, its tabs stayed attached to the old session and could not be
161
+ driven, closed, released or adopted. The bridge and the extension now track live sessions: every command and an MCP
162
+ server heartbeat (every 60 / 1.618 s) record the last sign of life; each MCP server announces its process (header
163
+ `X-Nexus-Browser-Pid`), which the bridge checks. A session silent for 1.618 x 60 s, or whose process is gone, is
164
+ dead: its tabs become orphaned. The same agent takes them back (`browser_adopt_tab` without `julien_asked`, and
165
+ automatically on the first heartbeat if it has no tab), and can close or release them. Never another agent; a live
166
+ session of the same agent stays protected (ownership is still held per session). `browser_list_tabs`: `state`
167
+ (`mine`, `orphan`, `other`, `julien`); control panel: "orphelin" badge.
168
+ - `browser_cdp_send`: `Runtime.evaluate` forced into the isolated world prevented reading an application's state.
169
+ Isolated world by default, page world on request (`world="page"`); the context is always enforced, without the
170
+ console API; a dialog opened by the evaluation is reported instead of blocking.
171
+ - Cookies: `include_values=True` is back, ONLY for a local development origin (localhost, 127.0.0.1, [::1],
172
+ *.localhost, *.test) and for cookies of those domains; clear refusal, before any call to Chrome, for any other
173
+ origin. Never a value by default.
174
+ - Body masking: `token` without an anchor masked `nextPageToken`, `pageToken`, `total_tokens`, `prompt_tokens`,
175
+ `max_tokens`, `tokenizer`... Allowlist added (pagination, `token_type`, `expires_*`, `tokenizer`), token counters
176
+ left alone when the value is a number (OpenAI, Anthropic, Gemini), ambiguous keys masked only when the value looks
177
+ like a secret (known formats: `ya29.`, `ghp_`, `sk-`, JWT... or a long opaque string). Unambiguous secret keys
178
+ (`access_token`, `refresh_token`, `id_token`, `token`, `password`, `client_secret`...) always masked.
179
+
180
+ ## [Unpublished] - 2026-10-17 — Default CLI identity
181
+
182
+ ### Fixed
183
+ - Default CLI identifier `cli` (3 characters) was refused by the bridge (4 minimum): now `cli-local`. Test added.
184
+
185
+ ## [Unpublished] - 2026-10-17 — Claude review: isolation and secrets in the extension
186
+
187
+ Version 2.7.0. Re-run `install.ps1` (with Antigravity closed), then RELOAD the extension in `chrome://extensions`
188
+ (the real-world validation and `verify.ps1 -Browser` refuse an outdated extension: version and features advertised by
189
+ ping), then open a new conversation in each agent. Tools that check the installation must read the fingerprint
190
+ through `scripts\empreinte.ps1`.
191
+
192
+ ### Security
193
+ - `browser_cdp_send`: closed list of ALLOWED commands (DOM reads, `Accessibility.getFullAXTree`,
194
+ `Runtime.evaluate` forced into the isolated world, screenshots, `Input.dispatch*`, display `Emulation.*`,
195
+ `Network.enable/disable`, Log/Console); everything else refused before reaching Chrome
196
+ (`Storage.clearDataForOrigin`, `Network.getRequestPostData`, `Network.loadNetworkResource`, `IO.read`,
197
+ `Fetch.enable`, `Page.addScriptToEvaluateOnNewDocument`, `Page.navigate`...).
198
+ - Tab ownership enforced by the extension: each bridge client identifies itself (agent + session, headers
199
+ `X-Nexus-Browser-Agent` / `X-Nexus-Browser-Client`, set by the bridge in the signed channel); the tab opened by
200
+ `browser_new_tab` belongs to that session (`chrome.storage.session`, survives service worker restarts); any page
201
+ command on another tab (the user's, another agent's, another session's) is refused. Adopting one of the user's tabs
202
+ only through `browser_adopt_tab(tab_id, julien_asked=True)`, visible in the control panel.
203
+ - `tab_id` required in the schema of every page tool (no more "last tab" on the MCP side).
204
+ - Response bodies: secret fields masked at any depth (JSON, anti-XSSI prefix, forms, truncated JSON, patterns inside
205
+ a script); no cookie value leaves the bridge anymore (`include_values` removed).
206
+ - Uploads also checked by the bridge (any HTTP client); folders added (`~\.gemini`, `~\.android`,
207
+ `~\.git-credentials`, `~\.config\gcloud`, `%APPDATA%\gcloud`, Chrome profile, Credentials/Protect...); paths
208
+ normalized (`\\?\`, `\\.\`, `\\localhost\C$\`, `\\127.0.0.1\C$\`, machine name, junctions and links, trailing
209
+ dots, case).
210
+ - Isolated world for all extension scripts (main frame included, `chrome.scripting` fallback in ISOLATED): markers,
211
+ click probes and AX registry invisible to the page and not replaceable by it. Only `browser_eval` and the YouTube
212
+ transcript (the page's player) run in the page world.
213
+ - Navigation and new tab: http, https, data:, about:blank only (never file://, chrome://, javascript:).
214
+ - Badge and click dot: extension messages only (a page can no longer display fake ones).
215
+
216
+ ### Added
217
+ - Control panel (side panel, neutral style): tabs driven per agent, debugger state, STOP (shared stop file
218
+ `~\.nexus\stop` set by the bridge, debuggers detached), "Rendre l'onglet" (release tab), bridge state.
219
+ - Tools `browser_adopt_tab`, `browser_release_tab`, `browser_network_record` (debugger kept attached, 600 s at most),
220
+ `browser_network_stop`.
221
+ - Emergency stop: the bridge cancels in-flight commands (423 response) and notifies the extension, which interrupts
222
+ key-by-key typing, dragging (button released) and waits, detaches all debuggers and refuses any action.
223
+ - `VERSION`; `scripts\empreinte.ps1` (installed.stamp includes `installer-hote-natif.ps1` and `runtime.ps1`);
224
+ `scripts\doublon-antigravity.ps1`, `scripts\ancienne-extension.ps1`.
225
+
226
+ ### Changed
227
+ - Debugger: per-tab command counter (replaces the global counter that prevented any detach).
228
+ - Element capture in page coordinates (scrolling added, beyond the viewport); debugging session resumed after a
229
+ service worker restart (iframe tracking restarted).
230
+ - `windows.create` fallback: id of the created tab, never that of the window.
231
+ - Panel shortcut: Alt+Shift+N (Ctrl+Shift+A is Chrome's "Search tabs").
232
+ - Antigravity: nexus-browser declared by its plugin only (the `mcp_config.json` entry is removed, after a backup);
233
+ `verify.ps1` reports a duplicate.
234
+ - Skill also copied for Claude Code (`~\.claude\skills`) and Codex (`~\.codex\skills`).
235
+ - `browser_cli.py`: version read from the code, French help, `--tab-id` required, new commands.
236
+
237
+ ### Removed
238
+ - Panel chat (text box, bridge queue, `browser_sidepanel_requests` / `browser_sidepanel_reply`) and its four
239
+ buttons; dead `popup.html/js`; SoM display from the content script that was never triggered.
240
+
241
+ ### Migration
242
+ - `install.ps1` removes (log `~\.nexus\nexus-browser\install.log`) the old token `~\.nexusagy\bridge-token` and the
243
+ files of the old native host; `verify.ps1` checks they are gone and reports the old "Antigravity Browser Bridge"
244
+ extension if a Chrome profile loads it (preferences read, never modified).
245
+ - No caches in the source tree: ruff `cache-dir` and pytest `-p no:cacheprovider` in `pyproject.toml`,
246
+ `sys.dont_write_bytecode` in the client and bridge modules; `.ruff_cache`, `client\__pycache__` and
247
+ `native-host\__pycache__` deleted.
248
+
249
+ ## [2.6.0] - 2026-10-16 — Neutral repository, shared by the three agents
250
+
251
+ Reload the extension (new folder, new identifier) and re-run `install.ps1` on each machine.
252
+
253
+ ### Added
254
+ - Standalone repository, extracted from the former Antigravity package: extension, local bridge, native host,
255
+ `nexus-browser` MCP server, CLI, Antigravity skill and plugin, tests (Python, Node, real-world validation).
256
+ - `install.ps1`: environment outside the source tree (`~\.nexus\nexus-browser`), native host, "Nexus Browser Bridge"
257
+ task with no trigger, registration of `nexus-browser` with Claude Code, Codex and Antigravity (plugin and skill
258
+ included), installation fingerprint.
259
+ - Agent name per registration (`NEXUS_BROWSER_AGENT`: Antigravity, Claude, Codex): default tab group named after it,
260
+ to see at a glance which agent opened what.
261
+ - Fixed extension identifier (`blcgddmjdkobmgakjffjpgiicjlflgjf`), derived from the public `key` in the manifest: the
262
+ same on every machine and whatever folder is loaded; the only origin accepted by the bridge and the native host (six
263
+ identifiers tied to the old folders previously).
264
+ - `verify.ps1`: task, native host, absence of the old task; `-Browser`: bridge reachable and extension loaded and up
265
+ to date.
266
+
267
+ ### Changed
268
+ - Neutral names: "Nexus Browser Bridge" extension, native host `com.nexus.browser_auth`, protocol `nexus-browser/1`,
269
+ header `X-Nexus-Browser-Token`, token `~\.nexus\nexus-browser\bridge-token`, exports in
270
+ `Téléchargements\nexus-browser-exports`, default tab group "Nexus" when no registration is set.
271
+ - MCP server reduced to the browser (Computer Use stays in the former Antigravity package); screenshot cleanup with
272
+ no dependency on the desktop client.
273
+ - Uploads also refused under `~\.nexus` (bridge token, emergency stop).
274
+
275
+ ### Verified
276
+ - Real-world validation in the user's Chrome: `--mouse` 11/11, `--full-live` 29/29 (`test.ps1 -Full` green four
277
+ times in a row). First three runs right after loading the extension: 27/29, empty network log for the `/api/data`
278
+ request (cause not found, not reproduced since); the failure now prints the log and the click response.
279
+
280
+ ### Migration
281
+ - `install.ps1` stops and removes the "NexusAGY Browser Bridge" task (same ports 9224/9225) and the native host
282
+ `com.nexusagy.bridge_auth`. The old "Antigravity Browser Bridge" extension must be removed from Chrome.
283
+
284
+ Versions up to 2.4.0 were published as "Antigravity Browser Bridge" in the same public repository; see its git history.
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Julien Piron (https://julienpiron.fr)
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,211 @@
1
+ Metadata-Version: 2.5
2
+ Name: nexus-browser
3
+ Version: 2.9.0
4
+ Summary: Your real Chrome, driven by Claude, Codex and Antigravity in background tabs: the nexus-browser MCP server (needs the NexusBrowser bridge and extension)
5
+ Project-URL: Homepage, https://julienpiron.fr/nexusbrowser/
6
+ Project-URL: Repository, https://github.com/pironjulien/NexusBrowser
7
+ Project-URL: Changelog, https://github.com/pironjulien/NexusBrowser/blob/main/CHANGELOG.md
8
+ Project-URL: Issues, https://github.com/pironjulien/NexusBrowser/issues
9
+ Author: Julien Piron
10
+ License-Expression: MIT
11
+ License-File: LICENSE
12
+ Keywords: ai-agents,antigravity,browser-use,cdp,chrome,claude,codex,computer-use,mcp,mcp-server
13
+ Classifier: Development Status :: 4 - Beta
14
+ Classifier: License :: OSI Approved :: MIT License
15
+ Classifier: Operating System :: Microsoft :: Windows
16
+ Classifier: Programming Language :: Python :: 3
17
+ Classifier: Topic :: Internet :: WWW/HTTP :: Browsers
18
+ Requires-Python: >=3.10
19
+ Requires-Dist: mcp<3,>=2.3.0
20
+ Requires-Dist: websockets<17,>=13.0
21
+ Description-Content-Type: text/markdown
22
+
23
+ # NexusBrowser
24
+
25
+ <!-- mcp-name: io.github.pironjulien/nexus-browser -->
26
+
27
+ [![Release](https://img.shields.io/github/v/release/pironjulien/NexusBrowser)](https://github.com/pironjulien/NexusBrowser/releases)
28
+ [![PyPI](https://img.shields.io/pypi/v/nexus-browser)](https://pypi.org/project/nexus-browser/)
29
+ [![Tests](https://github.com/pironjulien/NexusBrowser/actions/workflows/tests.yml/badge.svg)](https://github.com/pironjulien/NexusBrowser/actions/workflows/tests.yml)
30
+ [![License: MIT](https://img.shields.io/badge/license-MIT-green)](LICENSE)
31
+ [![Chrome MV3](https://img.shields.io/badge/Chrome-Manifest%20V3-4285F4?logo=googlechrome&logoColor=white)](extension/manifest.json)
32
+ [![Python 3.14](https://img.shields.io/badge/python-3.14-blue?logo=python&logoColor=white)](pyproject.toml)
33
+ [![MCP](https://img.shields.io/badge/MCP-server-7ee2a8)](https://modelcontextprotocol.io)
34
+
35
+ [![NexusBrowser: your real Chrome, driven by an AI agent in a background tab.](docs/assets/banner.webp)](https://julienpiron.fr/nexusbrowser/)
36
+
37
+ ![NexusBrowser: an AI agent types into a form, picks an option and clicks in a background Chrome tab while the user keeps browsing](docs/assets/demo.webp)
38
+
39
+ **Your real Chrome, driven by AI agents in background tabs.** NexusBrowser lets Antigravity, Claude and Codex use
40
+ the user's own Chrome (their sessions, their extensions) through one shared local bridge and one MCP server,
41
+ `nexus-browser`. Each agent works in its own background tabs, grouped under its name, with real (trusted) mouse
42
+ and keyboard events sent through the Chrome DevTools Protocol, while the user keeps working in their own tabs.
43
+
44
+ Version 2.9.0 · Windows 10/11 · Chrome 116+ · Python 3.14 · MIT licence
45
+
46
+ Part of the Nexus family: [NexusWindows](https://julienpiron.fr/nexuswindows/)
47
+ ([Claude](https://github.com/pironjulien/NexusWindows-Claude) ·
48
+ [Codex](https://github.com/pironjulien/NexusWindows-Codex) ·
49
+ [Antigravity](https://github.com/pironjulien/NexusWindows-Antigravity)),
50
+ [NexusBrowser](https://julienpiron.fr/nexusbrowser/) ([repo](https://github.com/pironjulien/NexusBrowser)),
51
+ [NexusAndroid](https://julienpiron.fr/nexusandroid/) ([repo](https://github.com/pironjulien/NexusAndroid)).
52
+ Project page: [julienpiron.fr/nexusbrowser](https://julienpiron.fr/nexusbrowser/).
53
+
54
+ ## Why
55
+
56
+ Browser automation usually means a separate headless browser (no sessions, no extensions) or screen-level computer
57
+ use that moves the user's cursor and brings the window to the front. NexusBrowser works inside the browser the user
58
+ already uses, without activating the agent's tab or window, and keeps the guard rails in the extension and the
59
+ bridge rather than in the agent: tab ownership, an allowlist of DevTools commands, masked secrets, an emergency stop.
60
+
61
+ - **Antigravity**: its own browser agent runs in a separate Chrome profile, without the cookies or sign-ins of the
62
+ user's browsing ([Antigravity docs](https://antigravity.google/docs/ide/separate-chrome-profile/)). NexusBrowser
63
+ gives it the user's real Chrome.
64
+ - **Claude and Codex** already have their own Chrome extension (Claude in Chrome, the Codex Chrome extension).
65
+ NexusBrowser complements it where a bridge with the DevTools protocol helps: JavaScript dialogs
66
+ (`browser_handle_dialog`), detailed network capture with response bodies, elements inside cross-origin iframes,
67
+ content and Google Docs/Sheets/Slides exports, and downloads.
68
+
69
+ Until 2.4.0 it was published here as "Antigravity Browser Bridge", for Antigravity only. Since 2.6.0 it is a neutral
70
+ bridge shared by several agents.
71
+
72
+ ## Pieces
73
+
74
+ | Folder | Role |
75
+ | --- | --- |
76
+ | `extension/` | Chrome MV3 extension "Nexus Browser Bridge": CDP actions, locators, network, dialogs, cross-origin iframes, exports, tab ownership, control panel |
77
+ | `native-host/` | Local bridge (`bridge_server.py`: WebSocket 9224 for the extension, HTTP 9225 for the agents) and native host `com.nexus.browser_auth` |
78
+ | `client/` | MCP server `nexus-browser` (`mcp_server.py`), authenticated transport (`bridge_client.py`), CLI (`browser_cli.py`), shared emergency stop |
79
+ | `skills/`, `plugins/` | Skill (copied for Antigravity, Claude Code and Codex) and Antigravity plugin, by `install.ps1` |
80
+
81
+ ## Tools
82
+
83
+ 47 MCP tools. Tool descriptions and messages are in French; tool and parameter names are in English.
84
+
85
+ | Area | Tools |
86
+ | --- | --- |
87
+ | Tabs | `browser_list_tabs`, `browser_active_tab` (read only), `browser_new_tab`, `browser_select_tab`, `browser_adopt_tab`, `browser_release_tab`, `browser_group_tabs`, `browser_close_tab`, `browser_reload`, `browser_navigate`, `browser_wait_load` |
88
+ | Input (trusted CDP events) | `browser_click`, `browser_semantic_click`, `browser_hover`, `browser_fill`, `browser_semantic_fill`, `browser_select_option`, `browser_check`, `browser_press_key`, `browser_type_keys`, `browser_drag_and_drop`, `browser_scroll`, `browser_upload`, `browser_handle_dialog` |
89
+ | Finding elements | `browser_find` (Playwright-style locators: role, label, text, placeholder, test id, in every frame), `browser_interactive_map`, `browser_vision_inspect` and `browser_click_mark` (Set-of-Mark badges), `browser_axtree`, `browser_content`, `browser_check_handoff` (2FA, CAPTCHA) |
90
+ | Network and downloads | `browser_network_record`, `browser_network_stop`, `browser_network_requests`, `browser_network_response`, `browser_wait_network_idle`, `browser_download_status`, `browser_wait_download`, `browser_get_cookies` |
91
+ | Output | `browser_screenshot`, `browser_pdf`, `browser_export` (main content as Markdown, text or HTML), `browser_export_gsuite` (Google Docs, Sheets, Slides through Google's export URLs), `browser_export_youtube_transcript`, `browser_logs` |
92
+ | Page access | `browser_eval`, `browser_cdp_send` (allowlisted commands) |
93
+
94
+ ## Install
95
+
96
+ Requirements: Windows 10 or 11, Chrome, [uv](https://docs.astral.sh/uv/), Python 3.14.
97
+
98
+ ```powershell
99
+ git clone https://github.com/pironjulien/NexusBrowser.git
100
+ cd NexusBrowser
101
+ pwsh -NoProfile -File install.ps1
102
+ ```
103
+
104
+ - Python environment, token, logs, screenshots and exports live in `~\.nexus\nexus-browser` (outside the clone).
105
+ - Scheduled task `Nexus Browser Bridge` with **no trigger**: the bridge starts on demand (first agent call,
106
+ `schtasks /Run`).
107
+ - Native host `com.nexus.browser_auth` declared to Chrome (HKCU): it computes the extension's proofs, the token never
108
+ reaches Chrome.
109
+ - MCP server `nexus-browser` registered for Claude Code, Codex and Antigravity when they are installed, each with
110
+ `NEXUS_BROWSER_AGENT` set to its name (default tab group).
111
+ - When the bridge has to relaunch Chrome, it uses the `Default` profile; extra Chrome flags (language...)
112
+ can be added with the `NEXUS_CHROME_EXTRA_ARGS` environment variable (allowlist: `--lang`, `--accept-lang`,
113
+ `--variations-override-country`, `--disable-features=Glic…`, `--force-dark-mode`; anything else is ignored).
114
+ - Migration from the former "Antigravity Browser Bridge": its task (same ports) and native host
115
+ `com.nexusagy.bridge_auth` are removed.
116
+
117
+ Then load `extension\` once in `chrome://extensions` (developer mode, "Load unpacked") and remove the old
118
+ "Antigravity Browser Bridge" if present. The extension ID is fixed by the public `key` in `manifest.json`
119
+ (`blcgddmjdkobmgakjffjpgiicjlflgjf`), the same on every PC and whatever the folder; it is the only origin the bridge
120
+ and the native host accept.
121
+
122
+ ### The MCP server from PyPI
123
+
124
+ Once the bridge and the extension are installed (above), any MCP client can also start the server straight from
125
+ [PyPI](https://pypi.org/project/nexus-browser/), without the clone:
126
+
127
+ ```bash
128
+ claude mcp add nexus-browser --scope user -e NEXUS_BROWSER_AGENT=Claude -- uvx nexus-browser
129
+ ```
130
+
131
+ Without the bridge, the server still starts and lists its 47 tools, and every call answers with a clear error that
132
+ points to the one-time install.
133
+
134
+ ## Security (held by the extension and the bridge, not by the agent)
135
+
136
+ - **Mutually authenticated bridge** (`nexus-browser/1`): HMAC proofs over both nonces, then every message signed
137
+ with a per-connection key and a strict sequence number. HTTP requires `X-Nexus-Browser-Token` and refuses any
138
+ browser origin.
139
+ - **Tab ownership**: every bridge client identifies itself (agent + session headers, forwarded by the bridge in the
140
+ signed channel). A tab opened by `browser_new_tab` belongs to that session (`chrome.storage.session`); any page
141
+ command on another tab (the user's, another agent's) is refused. A tab of the user only through
142
+ `browser_adopt_tab(tab_id, julien_asked=True)` (the parameter keeps its historical name). `tab_id` is mandatory on
143
+ every page tool. A session whose MCP process is gone (or silent for 1.618 x 60 s; servers beat every 37 s) leaves
144
+ **orphan** tabs: the same agent can reclaim (`browser_adopt_tab`, also automatically at startup), close or release
145
+ them; never another agent.
146
+ - **CDP allowlist** for `browser_cdp_send`; every extension script runs in an **isolated world** (only
147
+ `browser_eval`, the YouTube transcript and `browser_cdp_send(..., world="page")` touch the page's world).
148
+ - **Secrets**: secret headers, URL parameters and JSON/form body fields masked (pagination tokens and LLM token
149
+ counters kept); cookie values only for local development origins (`include_values=True`); uploads refused by the
150
+ bridge itself from secret folders (normalized paths). The value of a password, one-time code or payment card field
151
+ (`type=password`, `autocomplete` current-password, new-password, one-time-code, cc-number, cc-csc, cc-exp) never
152
+ leaves the page: `browser_content` (html mode included), `browser_vision_inspect`, `browser_find`,
153
+ `browser_axtree` and the value read back by `browser_fill` return `[valeur masquée]` instead.
154
+ - **`browser_eval` is full access to the page, nothing masked**: like `browser_cdp_send(Runtime.evaluate)` (both
155
+ worlds share the document), it reads what the page reads (non-HttpOnly cookies through `document.cookie`,
156
+ localStorage, sessionStorage, IndexedDB, form fields) and its `fetch` carries the user's session. The masking
157
+ above covers the dedicated tools, not scripts nor the raw Chrome replies of `browser_cdp_send`
158
+ (`DOM.getOuterHTML`, `Accessibility.getFullAXTree`...): kept on purpose, said plainly in the tool, the skill and
159
+ the server instructions. `Page.reload` with `scriptToEvaluateOnLoad` is refused (script injected at load).
160
+ - **Emergency stop** (Ctrl+Alt+Shift+Esc, STOP button of the control panel, `~\.nexus\stop`): commands already
161
+ running are cancelled (typing, drag, waits), debuggers detached, every action refused for 120 s. The same file
162
+ protocol is shared with the [NexusWindows](https://julienpiron.fr/nexuswindows/) agents.
163
+ - **Control panel** (side panel, Alt+Shift+N): tabs driven by each agent, debugger state, STOP, give a tab back.
164
+ - Whoever holds the bridge token controls the browser session: keep `~\.nexus\nexus-browser\bridge-token` private.
165
+
166
+ ## Checks
167
+
168
+ ```powershell
169
+ pwsh -NoProfile -File test.ps1 # offline: ruff, Pyright, pytest (real bridge on ephemeral ports), Node tests
170
+ pwsh -NoProfile -File test.ps1 -Full # plus real validation in the user's Chrome: --mouse then --full-live
171
+ pwsh -NoProfile -File verify.ps1 -Browser # task, native host, bridge reached, loaded extension up to date
172
+ ```
173
+
174
+ `test.ps1` uses the environment created by `install.ps1`.
175
+
176
+ ## Privacy
177
+
178
+ NexusBrowser runs entirely on your PC: a Chrome extension, a local bridge bound to `127.0.0.1` (authenticated by a
179
+ local token) and an MCP server. It has no account, no telemetry, no analytics and no remote server; nothing is sent
180
+ to the author or to any third party.
181
+
182
+ - **What it reads**: in the tabs the agent opened (or a tab of yours you explicitly let it adopt): page text and
183
+ accessibility tree, screenshots, console logs, network requests (headers and secret fields masked), cookies when
184
+ `browser_get_cookies` is called. Password, code and card values are masked in reads and drawn as dots in
185
+ screenshots and PDFs (every frame, cross-origin ones included); console logs are masked like network bodies;
186
+ `browser_eval` and `Runtime.evaluate` through `browser_cdp_send` do have full access to the page.
187
+ - **Network**: pages are loaded by your own Chrome with your own sessions, as if you browsed them. Exports (Google
188
+ Docs, Sheets, Slides, YouTube transcripts) are fetched from those sites in your session.
189
+ - **Where it goes**: tool results are returned to the AI client you connected (Antigravity, Claude, Codex…), which
190
+ may send them to its model provider under its own privacy policy.
191
+ - **What it stores**, under `~/.nexus/nexus-browser/`: the Python environment, the bridge token, `bridge.log`
192
+ (connection and emergency-stop events, rotated at 1 MB, three backups), `install.log`, screenshots (at most 50,
193
+ each kept under 24 hours) and the exports you ask for. The extension keeps tab ownership in Chrome's session
194
+ storage, cleared when Chrome closes.
195
+ - **Removing it**: remove the extension from Chrome, delete the scheduled task `Nexus Browser Bridge` and the
196
+ registry key `HKCU\Software\Google\Chrome\NativeMessagingHosts\com.nexus.browser_auth`, then delete
197
+ `~/.nexus/nexus-browser/`.
198
+
199
+ Questions or concerns: open an issue on this repository.
200
+
201
+ ## Limits
202
+
203
+ - Windows and Chrome only (Manifest V3, `chrome.debugger`). While the debugger is attached, Chrome shows its "is
204
+ being debugged" banner on the agent tab. Protected pages (`chrome://`, the Web Store) cannot be scripted.
205
+ - Tab ownership is per MCP process: sub-agents of one conversation share it.
206
+ - The YouTube transcript reads the page's own player (page world).
207
+ - Tool descriptions, messages and code comments are in French.
208
+
209
+ ## Licence
210
+
211
+ MIT, see [LICENSE](LICENSE). Copyright (c) 2026 Julien Piron.