Open-source browser bridge for local AI agents. Let an agent drive your real Chromium browser — your tabs, your cookies, your logins — through a small local daemon and a Manifest V3 extension.
No cloud service, no headless browser, no telemetry. The agent talks HTTP to 127.0.0.1; the extension executes commands with chrome.debugger.
Local agent or script
│ POST http://127.0.0.1:10087/command {"action","args","session"}
▼
Kai WebBridge daemon (Node.js) ── sessions, output files, optional token
│ WebSocket ws://127.0.0.1:10087/ws hello / tool_call / tool_result
▼
Chrome extension (MV3) ── chrome.debugger → CDP → your real tab
Requirements: Node.js 20+, a Chromium-based browser 120+ (Chrome, Edge, Brave, Opera, or Vivaldi).
npx -y kai-webbridge setupOr install the CLI globally:
npm install -g kai-webbridge
kai-webbridge setupsetup downloads nothing else: the npm package already contains the extension and skill. If you use npx without installing globally, prefix later commands with npx -y kai-webbridge (for example npx -y kai-webbridge status).
git clone https://github.com/kai38/kai-webbridge.git
cd kai-webbridge
npm install
node bin/kai-webbridge.js setupRelease tarballs are also attached to every GitHub release for offline installs.
- Copies the agent skill into detected runtimes and
~/.kai-webbridge/skills. - Copies the extension to a stable path,
~/.kai-webbridge/extension, so Chrome keeps the same extension ID across upgrades. - Starts the daemon on
127.0.0.1:10087. - Prints the extension folder to load.
Flags: --no-start (configure only), --no-skill (skip agent runtimes), --port, --token, --home.
The browser only allows unpacked extensions to be loaded from its own UI:
- Open the extension manager in the browser where you want the bridge:
- Chrome:
chrome://extensions - Edge:
edge://extensions - Brave:
brave://extensions - Opera:
opera://extensions - Vivaldi:
vivaldi://extensions
- Chrome:
- Turn on Developer mode.
- Click Load unpacked and select
~/.kai-webbridge/extension(the exact path is printed bysetup).
curl -s http://127.0.0.1:10087/statusextension_connected: true means the daemon and browser are talking. extension_browser names the browser that holds the extension (for example Chrome 153.0.8010.36). If the CLI is installed globally, kai-webbridge status prints the same JSON. If the extension shows an error, open its Options page and confirm the WebSocket URL is ws://127.0.0.1:10087/ws (or the port you configured).
To update, run setup again (npx -y kai-webbridge@latest setup, or npm install -g kai-webbridge && kai-webbridge setup), then reload the extension in chrome://extensions.
curl -s -X POST http://127.0.0.1:10087/command \
-H 'Content-Type: application/json' \
-d '{"action":"navigate","args":{"url":"https://example.com","newTab":true,"group_title":"Example"},"session":"demo"}'
curl -s -X POST http://127.0.0.1:10087/command \
-H 'Content-Type: application/json' \
-d '{"action":"snapshot","args":{"format":"aria"},"session":"demo"}'The snapshot returns lines like - link "More information..." [ref=e4]. Use @e4 as the target:
curl -s -X POST http://127.0.0.1:10087/command \
-H 'Content-Type: application/json' \
-d '{"action":"click","args":{"selector":"@e4"},"session":"demo"}'
curl -s -X POST http://127.0.0.1:10087/command \
-H 'Content-Type: application/json' \
-d '{"action":"screenshot","args":{"format":"jpeg","quality":70},"session":"demo"}'
# → {"ok":true,"data":{"path":"/tmp/kai-webbridge-screenshots/...jpg", ...}}
curl -s -X POST http://127.0.0.1:10087/command \
-H 'Content-Type: application/json' \
-d '{"action":"close_session","args":{},"session":"demo"}'| Category | Tools |
|---|---|
| Tabs | navigate, find_tab, list_tabs, list_browser_tabs, attach_tab, close_tab, close_session |
| Observe | snapshot, inspect, read_page, wait |
| Act | click, mouse_click, fill, select, press_key, check, hover, batch, scroll, upload, dialog |
| Run | evaluate, cdp |
| Capture | screenshot, save_as_pdf, network |
Full request/response reference: docs/protocol.md. The agent-facing contract is skill/kai-webbridge/SKILL.md.
One task = one session, with one Chrome tab group for the tabs it opens. The session field goes at the top level of the request body:
{"action":"navigate","args":{"url":"https://example.com","group_title":"Research"},"session":"research"}The daemon remembers the session's current tab and injects internal _session, _tabId, and _ownedTabIds fields. The extension groups the tabs visibly under the group title, and close_session closes the whole group.
Sessions persist to ~/.kai-webbridge/sessions.json, so they resume after a daemon restart. After a browser restart, the extension's tab-group binding and list_tabs re-establish the session's tabs.
To work on a page the user already has open:
{"action":"find_tab","args":{"url":"https://example.com","borrow":true},"session":"research"}Or inspect all open pages with list_browser_tabs, then attach_tab a tabId. Borrowed tabs are not moved into the session group and are not closed by close_session. close_tab requires force:true to close one; navigate requires newTab:true or adopt:true to reuse one.
When a page is opened, attached, or resumed, the response includes a numbered map of interactive elements and a full-page screenshot:
{
"page": {
"counts": {"buttons": 10, "text_fields": 4, "links": 3},
"interactive": [
{"id": 1, "role": "button", "name": "Search", "target": "[data-kai-wb=\"1\"]"},
{"id": 2, "type": "text", "name": "Query", "target": "[data-kai-wb=\"2\"]"}
]
},
"screenshot_path": "/tmp/kai-webbridge-screenshots/screenshot_…png"
}Fill and click several elements in one call:
{"action":"batch","args":{"commands":"!2:t:\"Kai WebBridge\",1:c!"},"session":"research"}batch supports c/click, t/type, s/select, k/key, h/hover, u/upload,
x/check, o/uncheck, with numeric aliases 1–8. id:"text" means fill.
inspect re-scrapes without acting. IDs reset after navigation or reload.
~/.kai-webbridge/config.json (all keys optional):
{
"host": "127.0.0.1",
"port": 10087,
"token": "optional-shared-secret",
"commandTimeoutMs": 120000,
"extensionWaitMs": 29000,
"outputDir": "/tmp",
"logLevel": "info"
}CLI flags override the file: --host, --port, --token, --output-dir, --log-level, --home.
If token is set, commands must send Authorization: Bearer <token> and the extension must be configured with the same token.
The extension can read and control every tab (it needs the debugger permission). The daemon accepts only local connections, ignores any /command request that carries an Origin header (so web pages cannot drive it), and optionally requires a bearer token. It has no telemetry. Treat the agent exactly as you would treat a person at your keyboard. Details: docs/security.md · privacy policy.
npm install
npm test # unit tests: protocol, sessions, outputs, daemon HTTP/WS
npm run test:e2e # real Chrome + extension + daemon round tripThe e2e test needs Chrome for Testing because Google Chrome 137+ ignores --load-extension:
npm run fetch-chrome
npm run test:e2eThe suite runs headless and covers navigation, snapshot refs, fill, click, contenteditable, screenshots, PDF, network capture, upload, dialogs, tab grouping, and session close. See CONTRIBUTING.md before sending a pull request.
bin/ CLI entry
src/ daemon (config, sessions, hub, HTTP, outputs, persistence, CLI)
extension/ Manifest V3 extension (no build step)
skill/ agent skill installed by `setup` / `install-skill`
test/unit/ fast tests without a browser
test/e2e/ headless Chrome integration test
docs/ architecture, protocol, security
.github/ CI workflow
More: architecture · protocol · security.
- Top frame only:
snapshot/click/fill/evaluatedo not traverse cross-origin iframes (usecdpor navigate the frame URL). click/filldispatch DOM-level events, so pages that strictly requireisTrustedmay ignore them;mouse_clickandcdpuse real CDP input for those cases.- One browser per daemon. A second extension connection is refused with WebSocket close code
4409. - No multi-agent isolation: sessions are names, not security boundaries.
- Borrowed tabs are protected:
close_tabneedsforce:true, andnavigateneedsnewTab:trueoradopt:true. - HTTP/HTTPS only;
file://andchrome://navigation is refused. - Chromium browsers block extensions from scripting their Web Store domains (
chrome.google.com/webstore,chromewebstore.google.com).navigatecan open and group those tabs, but they cannot be snapshotted, clicked, or typed into. - Cross-platform code exists, but automated e2e is currently verified on Linux.
This is an independent, from-scratch implementation. It is not affiliated with, endorsed by, or sponsored by Moonshot AI, and it contains no code, skill text, icons, or other assets from the Kimi WebBridge product. The names "Kimi", "Kimi WebBridge", and "Chrome" are trademarks of their respective owners, used only to describe prior art or the runtime this project talks to.
MIT — see LICENSE. Third-party notices are in THIRD_PARTY_NOTICES.md.