Tabduct
Allows controlling Brave browser tabs via MCP, including listing, reading content, clicking, typing, and managing tabs in the user's existing session.
Click on "Deploy Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@Tabductread the content of the active tab"
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
Tabduct
Give your CLI coding agent a handle on the real browser you're already using — the tabs you're logged into, not a throwaway sandbox.
Tabduct is a tiny, local, agent-agnostic bridge. It exposes your already-open, already-logged-in browser tabs to any agent that speaks the Model Context Protocol (MCP) — Claude Code today; Kilo, OpenCode, Cursor, and anything MCP-capable tomorrow.
No built-in chat. No embedded LLM. No vector DB. No telemetry. No native modules. It does exactly one thing: hand your agent the tab you point it at — under your consent, on your machine only.
CLI agent (Claude Code / Kilo / OpenCode / …)
│ MCP (streamable HTTP, 127.0.0.1) ← standard, language-neutral
▼
Tabduct host (Node — the reference host) ← implements /protocol
│ Chrome Native Messaging (stdio) ← Tabduct wire protocol
▼
Tabduct extension (MV3 background service worker) ← the one shared impl
│ chrome.tabs / chrome.scripting
▼
Your live browser tab (cookies, sessions, DOM)Why Tabduct
Your real session. The agent works with your logged-in tabs — no re-login, no captchas, no throwaway profile.
Local-only & private. Binds
127.0.0.1, guarded by a per-session bearer token. Nothing ever leaves your machine — no server, no telemetry, no external calls.You're always in control. Default-deny consent: share one tab or everything, block- or allow-list origins, read-only mode, auto-expiry, and a visible "⚡" group of shared tabs (drag a tab in to share it).
Agent- and language-agnostic. MCP to the north, a tiny documented wire protocol to the south. One extension is the fixed point; every host is a thin adapter.
Minimal & auditable. Reference host ~1–1.5k lines, zero native dependencies.
Related MCP server: Chromanche
Quickstart
Runs on macOS, Linux, and Windows, with Chrome, Chromium, Edge, or Brave. Requires Node ≥ 18.
git clone https://github.com/ultrathinker/tabduct.git && cd tabduct
npm install
npm run register # installs the native-messaging manifest for your OS + browser
# other browsers: node hosts/node/bin/tabduct.js register --browser edge|brave|chromiumregister writes the manifest to the right place automatically — ~/Library/Application Support/…/NativeMessagingHosts on macOS, ~/.config/…/NativeMessagingHosts on Linux, or an HKCU registry key on Windows (and makes the launcher executable on POSIX). Then:
Open
chrome://extensions→ enable Developer mode → Load unpacked → select theextension/folder.Click the Tabduct toolbar icon → Start (launches the local server; the header dot turns green). Click the dot again to Stop: a dialog asks what you mean, because the hub is shared by every connected browser: restart the hub for all browsers (shared tabs stay shared; the way to pick up new code), stop in this browser only, or stop for all browsers (every browser disconnects and stays stopped until you press Start in it).
Open Settings (⚙) → copy the MCP endpoint and Authorization token.
Paste them into your agent's MCP config (below) and reload the agent.
Share what the agent may touch: Share Current Tab, or Share Everything. That's it.
Diagnose the host anytime with
npm run doctor. In-app help lives under Settings → How it works.
Point your agent at it (MCP)
Use the Node host. The "shared hub" below is implemented only in
hosts/node/src/hub.js, and the current extension requires it. The Python and .NET hosts are direct-mode ports that predate the hub (see the host table below), so they cannot be used with this extension yet.
With the shared hub (on by default when using the Node host), every browser you connect appears behind one stable endpoint with a token that never changes:
{
"mcpServers": {
"tabduct": {
"type": "http",
"url": "http://127.0.0.1:12311/mcp",
"headers": { "Authorization": "Bearer PASTE_TOKEN_FROM_SETTINGS" }
}
}
}Reload your agent and it discovers the Tabduct tools below. Start Tabduct in one
browser and any other browser you open joins the same hub automatically — you always
point your agent at 127.0.0.1:12311, never at a per-browser port.
Tip — add it at the global / user scope. Some agents (Claude Code included) scope MCP servers to the directory you configured them in, so a session launched from another folder won't see Tabduct. Register it globally instead — e.g. in Claude Code:
claude mcp add --scope user tabduct http://127.0.0.1:12311/mcp --header "Authorization: Bearer <token>". Cursor and most GUI clients already store MCP servers globally.
Tools
Tool | What it does |
| Enumerate / get the focused tab — filtered to shared tabs only |
| Read a shared tab's text/HTML, or a compact outline of its interactive elements |
| Capture the visible tab (returned as an MCP image) |
| Click an element / type into a field (or pick a |
| Press a key (Enter, Tab, Ctrl+C…) as real keyboard input — terminals, custom widgets |
| Wait for a selector, a piece of visible text, a URL fragment, or a load state (bounded; any one of them ends the wait) |
| Point a shared tab at a URL |
| Tab management |
| Read the tab's console output (plus uncaught errors, in CDP mode) |
| Inspect captured network traffic (CDP developer mode) |
| Run arbitrary JS in a shared tab — read and modify the page |
| List the iframes inside a shared tab — target one with |
Frames. Embedded forms and widgets often live in an iframe from another domain
(HubSpot, Typeform, payment and sign-up forms), where page scripts can't reach.
get_dom_snapshot outlines each visible frame under its own --- frame N ---
header, and the page tools (get_page_content, get_dom_snapshot, click,
type, wait_for, execute_script, get_console_logs) take a frameId to work
inside it. The origin filter applies to each frame's own site.
Trusted input. Terminals (xterm.js — AWS CloudShell), Cloudscape/Material dropdowns and
canvas editors only react to events the browser itself generated. type / click with
trusted: true, and press_key, send exactly that through the DevTools Protocol. It needs the
same opt-in as CDP eval (Allow CDP eval, read-only off) and works inside cross-origin
frames for typing and keys. A terminal: click it (or type into its helper textarea,
.xterm-helper-textarea) with trusted: true, then press_key Enter. Paste shortcuts are refused
(they would read your clipboard).
Most tools — including click / type / wait_for / get_dom_snapshot — run as
injected functions, so they work even on strict-CSP sites (GitHub, banks, SaaS).
Only arbitrary-string execute_script is blocked by a page's CSP; for that, opt into
CDP mode (see below). Unshared tabs are completely invisible — the agent
can't even read their title.
Using Tabduct without disturbing the user (rules for agents)
Tabduct works with the user's real browser, so an agent that raises the window interrupts a game, a call or a presentation. The rules, also written into the tool descriptions:
Read in the background.
get_page_content,execute_script,get_dom_snapshot,list_frames, console and network reads work on a minimized window or a background tab. Prefer them. A screenshot needs a drawn page: use it only when text will not do.For polling and scheduled checks pass
quiet: true. The call never brings the window forward, never activates a tab; when the page cannot answer without that you get an error that says so, and you can report it instead of interrupting the user.Without
quiet, where the user allows waking the tab,screenshot,type,click,press_key,get_page_contentandget_dom_snapshotmay bring a minimized or covered window forward and put it back afterwards.activate_tab,screenshotwithactivate:trueandopen_tabwithactive:truealways take the user's focus: last resort only.The user decides, per tab, whether a tab may be woken (the 🔔 / 🔕 next to the tab's ✕ in the Tabduct popup; new shares take the default from the settings, on) and can switch waking off for every tab at once with Silent mode.
list_tabsshowswakeAllowed:falsefor a tab you may not bring forward:activate_tabandscreenshotwithactivate:trueare then refused withWAKE_NOT_ALLOWED, and in Silent modeopen_tabopens the tab in the background. Respect it: do not retry, work with the tab where it is, or tell the user.A page Chrome has frozen is thawed silently (see below) before the call. If that fails and the call is
quiet:true(or waking is off for the tab) you getTAB_FROZENat once, with the reason; repeat the call withoutquietto wake it (the window is brought forward, which the user allowed by default), or leave it. A call the page does not answer ends withTIMEOUTafter 18 s: look for a dialog ("Leave site?") instead of retrying blindly, and do not calllocation.reload()fromexecute_scripton a tab the user has been using.
Why a tab stops answering. Chrome slows timers in a hidden tab (once a second, once a minute after 5 minutes), can unload it from memory
(Tabduct marks shared tabs non-discardable) and, with Energy Saver on, freezes one that has been hidden and silent for more than five minutes: a
frozen page runs nothing, so every call to it hangs. Tabduct sees tab.frozen and thaws the page through the debugger (Page.setWebLifecycleState),
which needs Allow CDP eval on, and keeps the debugger attached to that tab for the call and 20 s after it (Chrome does not freeze a page that is being
inspected) and tries up to three ways of thawing in turn. Chrome 154 does not let the debugger thaw every kind of freeze (reproduced on a tab Chrome froze
by itself; one frozen from chrome://discards thaws), so the dependable way is the window, as when you click the tab yourself: a call without quiet brings the window
forward where waking is allowed, and the call goes on. A quiet call, or one on a tab where waking is off, gets
TAB_FROZEN with what each way of thawing did. list_tabs shows frozen: true. Chrome has no extension
API for its "Always keep these sites active" list, so Tabduct does not touch it.
Security & consent
The endpoint is token-authenticated — not merely bound to localhost (which
every local process shares). On Start the extension mints a bearer token; the
host requires Authorization: Bearer <token> on every request, rejects
Origin-bearing requests, and pins the Host header (DNS-rebinding defense).
Consent is default-deny and enforced inside the extension (the sole path to the browser). All of these are in the popup:
Origin filter — Block mode (listed sites are never shared) or Allow mode (only listed sites can ever be shared). Overrides every sharing mode.
Lock shared tabs to their domain (default on) — a shared tab that navigates to another site is paused: the agent is refused and the tab disappears from its list, so a shared shopping tab can't follow you into your bank; the share is kept and resumes when the tab is back. It is a live setting — switching it off frees tabs you already shared.
Read-only — the agent may look but never click, type, navigate, run scripts, or open/close tabs.
Auto-expire — un-shares tabs after a chosen time (5 min … 10 h), counted from when each was shared or from when you changed the setting.
Let the agent wake a tab by bringing the browser forward (default on; the starting value for each tab you share) — a minimized window, a window covered by others, a background tab or a tab Chrome has frozen does not draw or answer, so
screenshot,type,click,press_keyand page reads come back stale, ignored or never come back. Where waking is allowed the extension brings that window forward for the call and puts it back afterwards (a minimized one is minimized again a few seconds after the last call; Windows gives the focus back to the app you were in). A window you are working in is never touched. Each shared tab has its own 🔔 / 🔕 switch next to its ✕ in the popup (it works for the tabs of the other browsers listed there too), and the Silent button in the popup header switches waking off for every tab in every browser at once (a mute mark shows on the toolbar icon); your per-tab choices are kept and come back when you turn Silent mode off. In Silent mode an agent also cannot take the focus withactivate_tab, andopen_tabopens tabs in the background. A tab Chrome unloaded to save memory is never woken, since that would reload the page; shared tabs are marked as not discardable while shared.Full access / Safe defaults — one click over the flags above for "let the agent work freely on what I shared" (lock off, read-only off, no expiry, CDP eval on), or back to the safe side (lock on, CDP options off; read-only and expiry stay as you set them). The origin list is never touched.
Don't auto-share tabs the agent opens (default on).
CDP mode (Advanced, opt-in, default off) — lets
execute_scriptbypass a page's CSP via the DevTools Protocol, with an optional "developer mode" that routes all eval through it and full console/error capture. Chrome forbids requestingdebuggerat runtime, so it's a required permission granted at install — but nothing attaches until you flip this toggle on, and use is still gated by consent (never in read-only). Chrome shows a "being debugged" banner whenever it's actually in use.Sharing resets when the browser restarts (so nothing stays shared by accident), but survives reloading the extension — handy after a
git pull.
The full trust model and honest limitations are in SECURITY.md —
which is also where to report a vulnerability (please don't open a public issue).
Multiple browsers & profiles
This section describes the Node-host hub. The Python and .NET hosts expose one per-instance endpoint each, do not aggregate behind a shared endpoint and do not work with the current extension.
Install Tabduct in each Chrome profile you use (each Google account / profile is
separate). Start it in one browser — any other profile you open joins the same
hub automatically (no need to Start each). They all sit behind the one endpoint
(127.0.0.1:12311), and the agent tells them apart by their Label (auto-named
like Chrome-abcd — rename to Work / Personal in Settings).
The popup gives you one place to manage all of them: the shared-tab list is grouped
by browser (Current first, then each other browser that's sharing something), so you
can see everything that's exposed across every profile at a glance. The ✕ next to any tab
unshares it even on another browser; a browser in Share-Everything mode shows a single
⚡ Sharing all tabs row you can switch off the same way; and a Revoke all sharing
link clears every browser at once. (Cross-browser management flows through the hub over a
separate, token-authed control channel that the agent never sees — it can only ever
reduce sharing, never grant it; see protocol/PROTOCOL.md §11a.)
Two protocols, one extension, many hosts
Tabduct is defined by contracts, not implementations:
North (agent ↔ host): MCP. Already standardized; SDKs for Node, Python, .NET. Nothing to invent.
South (host ↔ extension): the Tabduct wire protocol. Chrome Native Messaging framing + message schema + tool catalog. Specified once in
protocol/— the single source of truth.The extension is the fixed point (it must be JS): it defines what the browser can do; every host is a thin relay of MCP calls to it (~1k lines in any language — see the per-host counts in
docs/ARCHITECTURE.md).
Host | Status | Hub | Notes |
✅ reference impl | ✅ yes | zero native deps, Node ≥ 18, MCP SDK wired, conformance-passing; ships the shared hub facade ( | |
⚠ direct mode only | ❌ no | official | |
⚠ direct mode only | ❌ no |
|
Hub is currently a Node-host feature. The shared hub (one stable endpoint behind which every connected browser appears) is implemented only in
hosts/node/src/hub.js. The Python and .NET hosts do not readpayload.huband expose only their own per-instance MCP endpoint. Seedocs/ROADMAP.mdfor plans to bring the hub to the other hosts.
New languages need no permission — implement protocol/PROTOCOL.md and pass protocol/conformance/.
Project layout
extension/ MV3 extension (the fixed point): consent, sharing, popup, icons
hosts/node/ reference host — CLI (register/doctor/run/instances/hub) + src/
protocol/ PROTOCOL.md + JSON schemas + conformance runners
docs/ ARCHITECTURE, DESIGN-consent-and-multibrowser, ROADMAP
scripts/ tests (consent, store, handlers, gate, wake, host; opt-in real-Chrome e2e), icon/key generatorsRun the full test suite (pure JS, no browser needed): npm test — the consent, store,
handler, wake, gate and host tests (mock chrome / fake host) + host conformance + hub
conformance. Two opt-in runs use a real, throwaway Chrome: npm run test:e2e (headless)
and npm run test:e2e-wake (opens a window for a few seconds).
Status
Working reference implementation, pre-1.0. Developed and exercised on Windows;
the macOS/Linux code paths are implemented (per-OS manifest install, POSIX file
modes, launcher chmod) but deserve a smoke test on each before you lean on them.
See docs/ROADMAP.md.
Originality
Tabduct is written from scratch. It reuses no third-party source code — only standard, public interfaces: Chrome's Native Messaging framing (a documented OS transport) and the Model Context Protocol. Nothing here carries a third-party attribution obligation.
License
MIT — see LICENSE.
This server cannot be deployed
Maintenance
Related MCP Connectors
Browser MCP for logged-in tasks. Uses your Chrome — credentials stay local. Zero-token replay.
Live browser debugging for AI assistants — DOM, console, network via MCP.
Stealth web browser for agents: search, fetch, click, download and type in persistent MCP sessions.
Hyperbrowser MCP — wraps the Hyperbrowser AI-agent browsing API
Related MCP Servers
- FlicenseNot gradedqualityDmaintenanceReal browser tabs for AI agents through a safe localhost MCP bridge, enabling control of Chrome tabs via MCP-compatible AI CLIs.2-
- FlicenseNot gradedqualityBmaintenanceLets any MCP-capable coding agent drive your real, logged-in Chrome through a local MCP server and MV3 extension.5-
- AlicenseNot gradedqualityBmaintenanceGives MCP-compatible AI agents direct control of your real browser with existing sessions, logins, and cookies. Supports multiple agents concurrently with tab targeting.7 npm8MIT
- AlicenseNot gradedqualityAmaintenanceConnects AI agents to your Chrome browser via MCP, enabling real-time control of existing tabs, sessions, and application state for development workflows.MIT