edge-agent-bridge

Edge Agent Bridge

PyPI - Version Edge Add-ons GitHub Release PyPI - Python Version License: MIT GitHub Sponsors Buy Me A Coffee

Let an AI agent drive the Microsoft Edge you already have open, on the tab you are looking at.

Playwright, Puppeteer and Selenium launch a clean profile. That profile has none of your cookies, so company SSO fails, bot checks fire, and anything behind a corporate VPN is out of reach. Edge Agent Bridge attaches to your running browser instead. Your session, your extensions and your logged-in state are all still there, and input goes in as trusted Chrome DevTools Protocol events rather than synthetic clicks a page can tell apart.

Three surfaces sit on one local daemon: an MCP server for agent clients, a CLI, and a Python API.

Everything here is tested against Microsoft Edge. The extension is plain MV3 and the daemon speaks ordinary CDP, so other Chromium browsers ought to work, but none are covered by the test suite and none are claimed.


Install

pip install edge-agent-bridge

Python 3.10 or newer, with no third-party packages at install time or runtime.

Then install the companion extension directly from the Microsoft Edge Add-ons store (Edge Agent Bridge), or load it unpacked while you’re developing:

edge-bridge extension open     # opens the bundled extension directory

Turn on Developer mode at edge://extensions, click Load unpacked, and pick that directory. Start the daemon and verify Edge connected:

edge-bridge daemon start
edge-bridge daemon status
Daemon: running on 127.0.0.1:18999 (PID 24188)
Extension: 2.2.0 (connected)

CLI commands and Python’s with Edge() start the daemon on first use if it isn’t running, but daemon start brings the extension online right away.


Wire it into an agent

edge-bridge setup

That detects the MCP clients on your machine and registers the server with each one. Claude Code, Cursor, Windsurf, Gemini CLI, Codex CLI and VS Code are covered. JSON config files are backed up before they’re touched, and a file that doesn’t parse is left alone and reported.

To see the config without writing anything:

edge-bridge mcp-config --client claude
edge-bridge mcp-config --client cursor

The server speaks stdio JSON-RPC and exposes 34 tools, all prefixed edge_ so they don’t collide with Playwright MCP in a mixed setup. docs/agent-guide.md is written for the agent rather than for you: snapshot first, act by ref, re-snapshot after navigation.


Snapshot and refs

The agent reads the page as a tree of roles, names and current values, each interactive node carrying a short id:

$ edge-bridge snapshot
tab 1459 "Files - Example App" http://localhost:8080/files
page "Files - Example App" url=http://localhost:8080/files
- heading "Files" level=1
- textbox "Search" [e1] value=""
- button "Upload" [e2]
- file "Choose file" [e3] hidden
- table "Files"
  - row "12080 spec.pdf 2026-09-01" [e4]
    - button "Open" [e5]

Every action that takes a target accepts one of those refs, so click e5 hits the button that was actually named in the tree. A ref that no longer resolves returns stale_ref, and a ref from a snapshot taken before the last navigation returns stale_snapshot. Neither one silently clicks something else, which is the failure mode that makes text matching unusable on dense pages.

Values come back too, which is what makes “fill in the rest of this form” work: the agent can see which fields you already filled. Passwords are masked. Hidden input[type=file] nodes are listed anyway and tagged hidden, because upload buttons almost always hide the real input. Inside an iframe a ref carries its frame, as in f31e1, including cross-origin frames.


Python

from edge_agent_bridge import Edge

with Edge() as browser:
    snap = browser.snapshot()
    print(snap["text"])

    browser.fill("e1", "spec")
    browser.click("e5")
    browser.wait(text="Opened")
    print(browser.console())

The daemon starts on first use. Edge pins itself to the tab of its first result, so a later call can’t drift onto a different tab because you switched windows in the meantime. Methods return the raw result dict and never raise on a failed action: check success, read code and error.


Command line

edge-bridge snapshot                      # tree with refs; --full adds static text
edge-bridge elements                      # interactive nodes with coordinates
edge-bridge click e5                      # ref, CSS selector or visible text
edge-bridge fill e1 "AI agents"
edge-bridge type "slow typing" --delay 30 # per-key events, for autocomplete widgets
edge-bridge key Enter
edge-bridge select e6 --label 日本語
edge-bridge upload e3 C:/tmp/spec.pdf
edge-bridge wait --text Opened
edge-bridge wait --idle                   # no in-flight requests; ignores WebSockets
edge-bridge screenshot out.jpg --of e4
edge-bridge console                       # console output, exceptions, dialogs
edge-bridge nav https://example.com
edge-bridge new https://example.com --group Agent
edge-bridge close --tab 1459
edge-bridge history search "quarterly report"  # search titles/URLs; --max-results, --start-time/--end-time
edge-bridge history delete https://example.com/page
edge-bridge group list                     # tab groups; --window-id filters
edge-bridge group move 1459 1460 --title Agent --color blue   # omit --group-id for a new group
edge-bridge group ungroup 1459
edge-bridge batch '[{"action":"click","target":"e5"},{"action":"sleep","ms":50},{"action":"fill","target":"e1","text":"spec"}]'
edge-bridge session start               # prints sessionToken=<uuid>; --new always mints
edge-bridge session status --session <uuid>
edge-bridge session stop --session <uuid>
edge-bridge click e5 --session <uuid>   # session pin; --tab is a one-shot override
edge-bridge click e5 --no-fallback      # disable the ref→text→scan→coords ladder

--json prints the raw result as a single line and sets the exit code: 0 on success, 1 when the action failed, 2 when the daemon is unreachable, 3 on a usage error. --no-highlight turns off the ring drawn around the element about to be acted on.

Each CLI call pays roughly 600 ms of Python startup. For anything repetitive use MCP, the Python API, or the REPL, which reuses one connection:

edge-bridge repl
Edge Agent Bridge REPL (port 18999). Type 'help' or action commands, 'exit' to quit.
edge> tab
[11.8ms] {"success": true, "tab": {"id": 1459, "title": "GitHub"}}
edge> click "Pull requests"
[14.2ms] {"success": true, "x": 380, "y": 96, "native": true}

Security model

The trust boundary is your OS user account. Anything running as you can already read your files and your browser profile, so the daemon doesn’t try to defend against it.

eval runs arbitrary JavaScript in the page with your session. It’s there on purpose, and it’s the reason to think about which agent you hand this to. Actions that destroy state need an explicit tab id: tab_close without one is an error rather than a guess.

The extension asks for debugger, which is what makes input trusted and screenshots possible. While it is attached, Edge shows its “is debugging this browser” bar under a Chromium policy no extension can dismiss.


Measured benchmarks

Against live Edge tabs, from tests/e2e/test_benchmark.py. These are one machine’s numbers; the timings move with the host, so the suite treats them as advisory and gates on the correctness assertions instead. EDGE_BRIDGE_BENCH_STRICT=1 makes the timings gate too.

Gate What Conditions Result
BM-01 WebSocket latency 200 sequential calls P50 9.09 ms, P95 14.35 ms
BM-02 Action batching 50 ops batched against sequential 7.9x, 140 ms against 1103 ms
BM-03 Native click burst 100 rapid CDP clicks 100/100 with isTrusted: true
BM-04 Typing integrity 55 chars of Unicode, symbols and Japanese byte-exact in 216 ms
BM-05 CSS :hover cascade multi-tier pure CSS dropdown sub1 then sub2 opened, target clicked
BM-06 CDP mouse drag 300 px over 15 steps dropped in 612 ms
BM-07 DOM scanner 3,000 synthetic elements 2,253 interactive found in 134.8 ms
BM-08 Screenshot viewport capture and base64 decode 111.2 ms average, 99.8 KB
BM-09 Tab query open tabs and active tab id 12 tabs in 11.9 ms
python -m pytest tests/e2e/test_benchmark.py -q -s

Development

python -m pytest -q -m "not e2e"   # hermetic, no browser needed
python -m pytest -q -m e2e         # drives a real Edge
python scripts/build_extension.py  # syncs the manifest version, zips to dist/

The hermetic suite runs a real daemon against a fake extension over a real WebSocket, so it covers the wire protocol without Edge installed, while CI runs both suites on Windows, Linux and macOS; releases are automated, see docs/release.md.


Support

Built by Shanewas Ahmed. If it saves you time, sponsorship funds keeping the CDP handlers current with Chromium releases:


License

MIT. See LICENSE.