Give AI-agent sessions brokered, profile-aware access to multiple local Chrome or AdsPower browsers through MCP.
Tabro was previously named Octopus Browser Relay. New MCP registrations use tabro; existing profile data, extension identity, Native Messaging registration, and legacy environment variables remain compatible. The repository is now ohmyskyhigh/tabro; the previous repository address redirects to it, and historical release asset filenames remain valid.
Tabro connects a local MCP gateway to one extension instance in each browser profile. An agent asks for browser capacity, receives broker-issued workspace and tab references, submits extension-supported Chrome DevTools Protocol (CDP) commands, and polls durable request tickets. The extension relays those commands through chrome.debugger; Chrome does not need a public remote-debugging port.
Important
The 0.3.1 development tree implements the canonical eighteen-tool runtime, relay-v2 protocol, Native Messaging path, and extension-backed CDP adapter. A packaged 0.3.1 release has not been published. The existing v0.3.0 release uses the previous Octopus name and fourteen-tool contract; use the source installer for current Tabro features and automatic managed Profile lifecycle. Automated verification and physical Chrome, AdsPower, Codex, and Hermes qualification are separate gates; see Current limits and the real-world runbook.
The current source updater requires a release declaring shared runtime discovery support; it rejects older packages before stopping an existing installation. The published v0.3.0 installer retains its older contract. Use the source installer below for the shared dynamic runtime until a qualified package is published.
- Prepare the Windows source-build prerequisites under Requirements, then clone this repository and open its root directory.
- Run
pwsh -NoProfile -File .\tools\install-local.ps1 -Install -StartBroker. Add-EnableManagedProfileswhen using the verified managed Chrome lifecycle. - For external profiles, load the reported extension directory through
chrome://extensionsand wait for Native companion connection. Managed Profiles load their extension during create/open. - Apply
.relay-data/bootstrap/codex-mcp.tomlor the generatedhermes-mcp.txtcommand, then start a new agent session and verify eighteen tools. - Read actual MCP and relay addresses from
.relay-data/runtime.json. Broker health and a real managed-tab operation establish different parts of readiness.
The Release installation section retains the published-package procedure; its historical package contract differs from this source baseline.
- discover connected browser-profile endpoints and broker-issued window choices;
- list owned persistent Profiles, create new ones, and reopen or stop existing ones;
- request one or more workspaces on distinct profiles;
- receive an initial managed tab and CDP event cursor for each workspace;
- create additional managed tabs;
- send raw CDP commands from the extension-supported capability manifest;
- poll asynchronous request tickets and read retained CDP events;
- transfer, pause, resume, terminate, or recover workspace control; and
- pause or resume every owned workspace on one endpoint.
The broker keeps the relationship among agent sessions, endpoints, windows, workspaces, tabs, tickets, and live extension connections. Agent-facing calls use broker-issued references and endpoint nicknames instead of Chrome profile IDs, extension IDs, window IDs, tab IDs, socket IDs, or debug ports.
flowchart LR
C["Codex session"] --> A1["Session-owned stdio adapter"]
H["Hermes session"] --> A2["Session-owned stdio adapter"]
A1 --> M["HTTP MCP gateway\n18 tools"]
A2 --> M
M --> B["Local broker\nrouting, status, tickets, controls, logs"]
B <--> D["SQLite durable state"]
B <--> G["Extension gateway\nrelay protocol v2"]
G <--> N["Windows Native Messaging companion"]
N <--> E1["Profile A extension"]
N <--> E2["Profile B extension"]
E1 <--> P1["Chrome or AdsPower profile A\nchrome.debugger"]
E2 <--> P2["Chrome or AdsPower profile B\nchrome.debugger"]
Normal installed profiles use the Native Messaging companion. The companion forwards extension messages to the broker's loopback relay. Direct extension-to-WebSocket transport remains available only for diagnostics.
Read the canonical Product definition, MCP contract, System architecture, and Component architecture for the complete design.
- Windows with PowerShell, current-user Native Messaging registry access, and WinHTTP WebSocket support for the checked-in native host and installer;
- Node.js
22.12.0or newer; - pnpm
11.19.0or another compatible pnpm 11 release; - Chrome, Chromium, or an AdsPower browser kernel compatible with Manifest V3 and Chrome
116or newer; and - Visual Studio C++ Build Tools with an x64 compiler and Windows SDK when rebuilding the native companion.
The TypeScript broker is not intrinsically tied to Windows, but the current native companion uses WinHTTP and the current registration script writes Windows registry keys.
From the repository root, run:
corepack enable
pwsh -NoProfile -File .\tools\install-local.ps1 -Install -StartBrokerThe installer runs the frozen pnpm install and build unless skip switches are supplied, verifies the compiled stdio MCP adapter, registers io.github.ohmyskyhigh.octopus_browser_relay for the current Windows user under Google Chrome, Chromium, and the installed AdsPower/SunBrowser registry roots, migrates an attributable prototype registration, optionally starts the compiled broker, and creates these local handoff files:
.relay-data/bootstrap/PAIRING.md
.relay-data/bootstrap/MCP-REGISTRATION.md
.relay-data/bootstrap/codex-mcp.toml
.relay-data/bootstrap/hermes-mcp.txt
It does not overwrite Codex configuration. The generated Hermes registration command updates every Hermes profile installed when it runs. The installer also leaves browser-profile data and existing broker state in place.
With the Broker running, use the installer’s read-only readiness entry point; it derives health addresses from the data-directory runtime record:
pwsh -NoProfile -File .\tools\install-local.ps1The standalone preflight retains fixed-port defaults. Supply discovered URLs when using it directly:
$runtime = Get-Content -Raw .relay-data/runtime.json | ConvertFrom-Json
$mcpHealth = $runtime.mcpUrl -replace '/mcp$', '/health'
$relayHealth = ($runtime.relayUrl -replace '^ws:', 'http:') -replace '/relay$', '/health'
pwsh -NoProfile -File .\tools\real-world-preflight.ps1 -McpUrl $runtime.mcpUrl -McpHealthUrl $mcpHealth -RelayHealthUrl $relayHealthThe check verifies the workspace, built extension files, required manifest declarations, native executable, compiled stdio adapter, Native Messaging manifest, every configured Native Messaging registry value, generated pairing and MCP handoff files, and both health endpoints. It exits with code 10 when operator action is still required. Pass the same -NativeRegistryRoots values to installation and preflight when a browser build uses different roots.
The handoff validator still expects a literal broker URL in generated registration files and can report mcp_registration_handoffs: ACTION_REQUIRED for valid runtime-file registrations. Inspect the generated discovery/token paths and verify actual tool discovery; do not treat that warning as a passed full preflight or repeatedly reinstall. The runbook records this implementation gap.
The release workflow produces a portable Windows ZIP, checksum and standalone updater. Repository publication records describe the older v0.3.0 package with fourteen tools; this download procedure does not provide the current source features. The current updater rejects packages lacking runtimeDiscoveryVersion: 1 before shutdown. For the historical published-package path, download and run its updater:
Invoke-WebRequest `
https://github.com/ohmyskyhigh/tabro/releases/latest/download/octopus-browser-relay-update.ps1 `
-OutFile .\octopus-browser-relay-update.ps1
pwsh -NoProfile -File .\octopus-browser-relay-update.ps1The updater installs under %LOCALAPPDATA%\Octopus Browser Relay, preserves durable data under its data directory, registers the versioned native host, starts the broker, and prints the stable extension directory. It also writes these handoff files under %LOCALAPPDATA%\Octopus Browser Relay\bootstrap:
INSTALLATION.md
codex-mcp.toml
hermes-mcp.txt
current-release.json
Open chrome://extensions, enable developer mode, choose Load unpacked, and select the returned extension directory once. Do not select the downloaded ZIP itself; the updater extracts and maintains the stable extension directory.
For later updates, run the installed updater:
pwsh -NoProfile -File "$env:LOCALAPPDATA\Octopus Browser Relay\update-local.ps1"Later updates keep the unpacked-extension path unchanged. Reload the extension after updating its files to activate the new code; its pairing identity is preserved. The broker accepts different extension package versions when relay protocol negotiation succeeds and does not request a reload based on version differences.
Maintainers build the assets with:
pnpm package:releaseAn installer-started broker records its process ID in .relay-data/broker.pid. Stop it with:
pwsh -NoProfile -File .\tools\stop-local-broker.ps1The command inspects the recorded Windows process and stops it only when it is node.exe running this workspace's absolute compiled broker entry point. A missing PID file is a successful no-op; a stale PID is retained for inspection; a process mismatch is rejected without stopping anything or deleting the PID file. Use Ctrl+C for a foreground pnpm dev broker.
After the one-time Native Messaging registration, use this shorter development loop:
corepack enable
pnpm install --frozen-lockfile
pnpm build:extension
pnpm devpnpm dev runs apps/broker/src/runtime/main.ts through tsx. Rebuild and reload dist/browser-extension after extension source changes.
Repeat these steps inside each Chrome or AdsPower profile that the broker should control:
- Open
chrome://extensions. - Enable Developer mode.
- Choose Load unpacked and select
dist/browser-extension. - Confirm the extension ID is
caekiojlchhifdomfghejkbfpmaklafe. - Open Tabro Settings. The extension displays an editable two-word profile-local pairing code and its compact combined nickname, such as
MINT-WAVEandmintwave. - Keep the generated code or enter two three-to-eight-letter words, keep Native companion selected, and choose Save settings and reconnect after any change.
- Wait until the extension reports
Status: connectedwith the final endpoint nickname.
The extension generates the default readable code and registers itself with the running loopback broker; there is no broker-issued code command or copy step. The saved generated or customized code remains authoritative across browser windows and restarts. An existing endpoint is renamed only after reconnect authentication proves the profile's persisted cryptographic identity. A nickname collision preserves the requested code and shows an error until you choose another. The readable code helps correlate this profile with its endpoint and is not an authorization secret. Repeat the load-and-connect steps in every participating profile.
The extension options page can connect directly to the relayUrl published in the runtime record, but that mode is not the normal Chrome or AdsPower setup. Browser kernels can block extension-initiated loopback WebSockets even when ordinary page requests to 127.0.0.1 work. Use Native companion for installed profiles and switch to direct WebSocket only while diagnosing transport behavior.
Merge .relay-data/bootstrap/codex-mcp.toml into the active Codex config.toml, then start a new Codex session. The generated fragment launches Node with dist/mcp-stdio-adapter/src/main.js and passes the broker URL, token-file path, and codex runtime label as process environment.
The token remains in .relay-data/admin-token.txt; the generated fragment points to that file instead of embedding the token. The repository does not choose or modify the active Codex configuration file.
Open .relay-data/bootstrap/MCP-REGISTRATION.md and run the exact PowerShell command stored in .relay-data/bootstrap/hermes-mcp.txt. It discovers the default and every installed named Hermes profile, then registers the same compiled adapter with TABRO_RUNTIME=hermes, the broker URL, and the local token-file path in each isolated profile. Start a new session in each profile, then run:
hermes -p <profile> mcp test tabroRun the generated command again after creating another Hermes profile. Hermes CLI releases can change their configuration syntax. The current repository generates the command and readiness handoff but does not install Hermes or prove a particular external Hermes release.
The adapter prefers runtime-owned environment values in this order:
CODEX_THREAD_ID;CODEX_SESSION_ID;HERMES_SESSION_ID; andHERMES_AGENT_SESSION_ID.
TABRO_RUNTIME_SESSION can supply an explicit fallback. When none exists, the adapter generates one random session key at startup and retains it for that process. Parent-session variants are forwarded for related subagents.
The adapter forwards these facts to the HTTP broker as x-octopus-runtime, x-octopus-runtime-session, and optional x-octopus-parent-runtime-session headers. They are adapter-supplied evidence, not values for the model to invent or include in tool bodies.
| Execution | Tool | Purpose |
|---|---|---|
| Read | list_browser_profiles |
List owned persistent Profiles, including stopped browsers and their observed readiness. |
| Async | create_browser_profile |
Create an owned persistent Profile, launch Chrome, and connect its extension. |
| Async | open_browser_profile |
Open an existing Profile or reuse its running instance. |
| Async | stop_browser_profile |
Normally close an owned Profile after its active work ends. |
| Read | get_browser_context |
Read one narrow, paginated broker, endpoint, window, capability, workspace, tab, or request-summary view. |
| Async | request_browser_workspace |
Request an exact number of workspaces on distinct eligible profile endpoints. |
| Async | create_browser_tab |
Create one managed tab in an owned workspace. |
| Async | send_cdp_command |
Submit one supported raw CDP command to one managed tab. |
| Read | read_cdp_events |
Read retained CDP events from a broker-issued tab cursor. |
| Read | get_browser_request |
Read one visible request ticket. |
| Async | take_over_workspace |
Transfer one exactly identified workspace. |
| Async | terminate_workspace |
Reconcile running work, archive the tab group, and end the workspace. |
| Async | resolve_browser_request |
Resolve one owner-visible request paused for confirmation. |
| Immediate | close_browser_request |
Remove one terminal ticket from public discovery while retaining audit history. |
| Async | stop_workspace_automation |
Pause one workspace manually. |
| Async | resume_workspace_automation |
Reconcile and clear the workspace's manual-stop cause. |
| Async | kill_browser_endpoint |
Pause every active workspace on one entirely owned endpoint. |
| Async | resume_browser_endpoint |
Reconcile the endpoint and clear its endpoint-kill cause. |
Every asynchronous tool returns an accepted request_ref before its browser effect becomes eligible for dispatch. The agent polls that reference with get_browser_request. The exact request and result schemas live in MCP-Contract.schema.json.
The current extension capability baseline is extension-baseline.json. It includes selected Accessibility, DOM, Emulation, Input, Network, Page, and Runtime methods, all confined to a managed tab.
Use DOM.getDocument and DOM.querySelector to locate an input[type="file"] in the managed tab, then call send_cdp_command with its node ID:
{
"workspace_ref": "<workspace_ref>",
"target": { "kind": "tab", "tab_ref": "<tab_ref>" },
"method": "DOM.setFileInputFiles",
"params": { "nodeId": 42, "files": ["C:\\uploads\\image.png"] }
}Poll the returned request_ref with get_browser_request until completion. Supply absolute paths accessible on the browser's machine; Tabro forwards paths and does not transfer file bytes from a remote agent. The command also accepts backendNodeId or objectId instead of nodeId, multiple paths for a multiple-file input, and files: [] to clear the selection. Setting the input selects files; complete any page-specific upload or submit step afterward. See the CDP method definition.
| Purpose | Default |
|---|---|
| MCP and MCP health | Dynamic loopback HTTP port; read mcpUrl in .relay-data/runtime.json and replace /mcp with /health |
| Extension relay and relay health | Dynamic loopback WebSocket port; read relayUrl in the same runtime record |
| Shared discovery | .relay-data/runtime.json; Native Host reads relay-runtime.json beside its executable |
| SQLite state | .relay-data/relay.sqlite |
| Generated bearer token | .relay-data/admin-token.txt |
| Installer-managed broker PID | .relay-data/broker.pid |
| Generated setup handoff | .relay-data/bootstrap/ |
Demo and ordinary MCP sessions share one Broker. Both listeners default to port 0, so the operating system chooses available ports. Registrations use TABRO_RUNTIME_FILE and a token file rather than fixed port numbers. The adapter checks the instance identity and reconnects before a new tool call after a Broker restart; it never retries a dispatched browser mutation. Native Host reads the current relay record on each connection.
The broker creates the token on first start when RELAY_ADMIN_TOKEN is unset. .relay-data/ is ignored by Git.
Supported environment variables are RELAY_HOST, RELAY_MCP_PORT, RELAY_WS_PORT, RELAY_DB_PATH, RELAY_LOG_LEVEL, RELAY_HEARTBEAT_TIMEOUT_MS, RELAY_ERROR_THRESHOLD, RELAY_LEASE_TTL_MS, RELAY_ADMIN_TOKEN, and RELAY_PROFILES_CONFIG. Configuration validation lives in apps/broker/src/runtime/config.ts. TABRO_RUNTIME_FILE and TABRO_NATIVE_RUNTIME_FILE select the discovery outputs in apps/broker/src/runtime/main.ts.
pnpm lint
pnpm typecheck
pnpm test
pnpm test:e2e
pnpm buildpnpm verify runs those checks as one gate. pnpm build also compiles the Windows native companion and therefore needs the C++ toolchain. Use the real-world runbook for physical profiles and agent runtimes.
- Read
relayUrlfrom the runtime record, changews:tohttp:and/relayto/health, and confirm it responds. - Confirm the extension ID is
caekiojlchhifdomfghejkbfpmaklafeand Native companion is selected. - Confirm the extension directory still exists at the path reported by the updater.
- Open
%LOCALAPPDATA%\Octopus Browser Relay\bootstrap\INSTALLATION.mdand verify the installed paths. - Reload the unpacked extension after updating its files. If it reports a required-version mismatch, update and restart the broker to remove the old version gate, then reconnect the extension.
For a source checkout, run pwsh -NoProfile -File .\tools\real-world-preflight.ps1. For a GitHub Release install, check both health endpoints and the generated handoff files directly.
Confirm the broker health first. Then verify that the Codex or Hermes registration points to the generated stable adapter launcher and token-file path. Rerun hermes-mcp.txt if the affected Hermes profile is missing its entry, and restart the agent session after changing its MCP registration. Hermes can check that profile with:
hermes -p <profile> mcp test tabroExactly eighteen tools must be discovered. Do not point an agent at the relay WebSocket port; agents use the stdio adapter and HTTP MCP gateway.
Both ports default to 0; clients use the actual addresses in the discovery record. tools/start-local-broker.ps1 serializes launches and reuses a healthy instance; the Broker also locks its data directory against duplicate startup. Use the installed stop-installed-broker.ps1 or source checkout's tools/stop-local-broker.ps1 for verified shutdown. Explicit nonzero port overrides remain available for compatibility.
- The Native Messaging host and registration installer are Windows-specific.
- The installer registers current-user host manifests for Google Chrome, Chromium, and the installed AdsPower/SunBrowser root. Another browser build or AdsPower variant that reads a different registry location needs its actual root passed through
-NativeRegistryRoots. - The installer generates a Codex stdio handoff without modifying Codex configuration and a Hermes command that registers every profile installed when it runs. It does not install either runtime, and a later Hermes profile requires rerunning that command.
- Independent sessions remain distinct when the host launches one adapter process per session or supplies a supported runtime session environment value. A host that deliberately reuses one adapter process across unidentified sessions also reuses that adapter identity.
- The HTTP broker confirms an accepted ticket after handing it to the stdio adapter. MCP has no transaction spanning that HTTP handoff and the adapter's later stdout write, so an adapter crash in that narrow interval can dispatch work whose ticket the agent runtime did not receive.
- The extension executes only methods published by
octopus-extension-baseline-v1; flattened child CDP sessions are disabled. - Relay-v2 extension envelopes are limited to 1 MiB. Capability and inventory limits are published in the same manifest.
- The relay-v1 compatibility bridge remains enabled for migration, while the public MCP gateway exposes only the canonical eighteen tools.
- The repository does not install Codex, Hermes, Chrome, or AdsPower. The current release has recorded a three-profile, independent-Codex-session, and Hermes physical qualification; another machine or browser build still requires its own preflight and physical evidence.
- There is a PID-verified broker stop command but no full uninstall command yet.
doc/ Top-down knowledge vault and change governance
apps/broker/ Broker runtime, core, MCP, relay, and storage source
apps/browser-extension/ Manifest V3 extension source
apps/mcp-stdio-adapter/ Session-owned stdio bridge for Codex and Hermes
apps/native-host/ Windows Native Messaging companion source
apps/shared/protocol/ MCP schemas, relay schemas, domain facts, and capabilities
tools/ Build, install, pairing, preflight, and test automation
tests/ Contract, unit, integration, fault, E2E, and physical tests
dist/ Generated broker, adapter, extension, and native artifacts
Start with the top-down vault map for project knowledge and the repository map for exact implementation paths.
Read CONTRIBUTING.md, the vault editing rules, and SECURITY.md before changing contracts, routing, transport, listener scope, or identity behavior. Do not include bearer tokens, pairing codes, private browser identifiers, profile paths, or SQLite files in public reports.
Tabro is available under the MIT License.
The development implementation adds list_browser_profiles, create_browser_profile, open_browser_profile and stop_browser_profile under MCP contract v2. It is verified on Windows with Chrome 153.0.8010.53 and a one-time Native Messaging installation. New Profiles automatically load the extension; normal stop retains cookies, website storage and identity. This is a local development change, not a newly published release.
Use tools/install-local.ps1 -Install -EnableManagedProfiles for local setup, or -EnableManagedProfiles with a package built from this source. Broker and adapter must upgrade together. Read the single-Agent Demo runbook for the two verified three-Profile runs and reproduction steps.