From Browser Relay
Controls the user's existing Chrome via the Browser Relay CLI, preserving cookies, extensions, and SSO sessions. Use for login-protected sites, background tabs, or remote browsers.
How this skill is triggered — by the user, by Claude, or both
Slash command
/browser-relay:browser-relayThe summary Claude sees in its skill listing — used to decide when to auto-load this skill
Work alongside the user in the Chrome they already use. Browser Relay lets the
Work alongside the user in the Chrome they already use. Browser Relay lets the agent operate existing tabs through a Skill + CLI, including background tabs, without launching a blank browser profile or repeatedly pulling Chrome to the foreground. Remote Relay can reach the user's explicitly enabled browser on another machine.
Use Browser Relay when you need to:
[link], [button], [input] markersWhen NOT to use:
Check setup before the first browser action:
browser-relay doctor
If browser-relay is not installed, tell the user to install the open-source
CLI, then rerun the check:
npm install -g @linsoai/browser-relay
browser-relay path
The user must load the directory printed by browser-relay path from
chrome://extensions and explicitly enable the extension. Do not install it,
enable Remote Relay, or invent a Remote Device ID without the user. A ready
setup has the relay server running, the extension connected, and at least one
regular Chrome tab attached.
Relay URL: http://127.0.0.1:18795
WebSocket: ws://127.0.0.1:18795/extension
No authentication needed — the relay only accepts connections from localhost.
Remote mode ("Remote Relay") lets an agent drive a browser on another machine.
The user opens the extension Options, turns on Remote Relay, and copies the
generated remote-device-id — a secret capability like br-xxx. Only then use
remote flags (the hosted hub relay.linso.ai is the default host):
browser-relay tabs --remote-device-id br-xxx
browser-relay remote add mymac br-xxx # or save an alias once…
browser-relay tabs --remote mymac # …and use the short name (remote ls / rm to manage)
Never invent or guess a remote-device-id; it is a secret generated by the
extension. The local relay stays bound to localhost — remote mode connects the
browser out to the hub and does not need it.
When shell access is available, use the browser-relay CLI for browser
interaction. Do not hand-write curl for normal agent browsing tasks. The CLI
avoids JSON escaping, keeps commands short, and prints compact output by
default.
Use the HTTP API directly only when you are writing code, tests, scripts, or an
integration against Browser Relay, or when the CLI is unavailable. Use --json
only when you need the full API response.
browser-relay tabs
browser-relay console --tab <tabId> --limit 50
browser-relay network --tab <tabId> --type response --status 500 --limit 20
browser-relay snapshot --tab <tabId> --max-length 20000
browser-relay wait 'button[type=submit]' --state visible --timeout 10000 --tab <tabId>
browser-relay click 'button[type=submit]' --tab <tabId>
browser-relay type 'hello world' --selector 'input[name=q]' --clear --submit --tab <tabId>
browser-relay key Control+L --tab <tabId>
browser-relay scroll down --amount 1000 --tab <tabId>
browser-relay download-start https://example.com/file.pdf --filename files/file.pdf
browser-relay downloads --limit 20
browser-relay screenshot /tmp/page.png --full-page --tab <tabId>
browser-relay eval 'document.title' --tab <tabId>
For maintenance, upgrade Browser Relay with:
browser-relay update
Then refresh this Skill for the active agent (for example, codex or
claude-code) and let Browser Relay verify the installed copy:
browser-relay skill install --agent codex
For long text or JavaScript, avoid shell escaping with stdin:
printf '%s' "$TEXT" | browser-relay type --selector textarea --stdin --tab <tabId>
browser-relay eval --stdin --tab <tabId> < script.js
The HTTP API below is for code, tests, custom tools, and low-level debugging. For interactive agent work, prefer the CLI workflow above.
Errors are structured across HTTP, CLI --json, and MCP tool errors:
{ "ok": false, "code": "invalid_request", "error": "url is required", "message": "url is required", "status": 400, "retryable": false }
Agents should branch on code rather than matching localized/free-form error
text. retryable: true means a reconnect/retry is reasonable.
List all attached browser tabs.
GET http://127.0.0.1:18795/api/tabs
Returns: { ok: true, tabs: [{ id, title, url }] }
Navigate a tab to a URL.
POST http://127.0.0.1:18795/api/navigate
Header: Content-Type: application/json
Body: { "url": "https://example.com", "tabId?": "optional-tab-id" }
Read captured console, page error, and browser log entries.
GET http://127.0.0.1:18795/api/console?tabId=<id>&limit=100&level=error&clear=false
POST http://127.0.0.1:18795/api/console/clear
Body: { "tabId?": "...", "level?": "error" }
Use this after actions that may trigger frontend errors or warnings.
Read captured request/response/finished/failed network events. Sensitive
headers such as Authorization, Cookie, Proxy-Authorization, and
Set-Cookie are redacted; request bodies are not captured.
GET http://127.0.0.1:18795/api/network?tabId=<id>&type=response&status=500&limit=100&clear=false
POST http://127.0.0.1:18795/api/network/clear
Body: { "tabId?": "...", "type?": "request|response|finished|failed", "method?": "GET", "status?": 500, "requestId?": "...", "url?": "substring" }
Use this after actions that fail silently, after form submits, or when console errors imply an API request failed.
Get a text representation of the current page (interactive elements annotated).
GET http://127.0.0.1:18795/api/snapshot?tabId=<id>&format=text&maxLength=100000
Format can be "text" (annotated DOM) or "html" (raw HTML).
Wait for a CSS selector to be attached to the DOM or become visible. Prefer this over fixed sleeps after navigation or actions.
POST http://127.0.0.1:18795/api/wait
Body: {
"selector": "button.submit",
"state?": "attached|visible",
"timeoutMs?": 5000,
"pollMs?": 100,
"tabId?": "..."
}
state defaults to visible. timeoutMs accepts 1–20000 and pollMs
accepts 50–1000. A timeout returns code: "wait_timeout" with
retryable: true; a tab closing or the extension disconnecting fails
immediately instead of waiting for the timeout.
Click an element by CSS selector. Scrolls into view first, uses real mouse events.
POST http://127.0.0.1:18795/api/click
Body: { "selector": "button.submit", "tabId?": "...", "doubleClick?": false }
Type text into an input field. Optionally clear and submit.
POST http://127.0.0.1:18795/api/type
Body: {
"text": "hello world",
"selector?": "input[name='q']",
"clear?": true,
"submit?": true,
"tabId?": "..."
}
Scroll the page.
POST http://127.0.0.1:18795/api/scroll
Body: { "direction": "down|up|top|bottom", "amount?": 800, "tabId?": "..." }
Press a key or keyboard shortcut using real keyboard events.
POST http://127.0.0.1:18795/api/key
Body: { "key?": "Enter", "combo?": "Control+L", "tabId?": "..." }
Use combo for shortcuts (Control+L, Meta+K, Shift+Tab) and key
for single keys (Enter, Escape, ArrowDown, a).
Capture a PNG screenshot (base64).
POST/GET http://127.0.0.1:18795/api/screenshot?tabId=<id>&fullPage=true
Full-page captures use layout metrics plus a clipped screenshot when possible,
then fall back to Chrome's captureBeyondViewport path. Returns:
{ ok: true, data: "base64...", format: "png", fullPage, strategy, width?, height?, bytes }
Evaluate arbitrary JavaScript in the page. The escape hatch.
POST http://127.0.0.1:18795/api/eval
Body: { "expression": "document.querySelector('h1').innerText", "tabId?": "..." }
Get the URL of an image/media/link element.
POST http://127.0.0.1:18795/api/download
Body: { "selector": "img.profile-pic", "tabId?": "..." }
Start a real Chrome download from a URL using the user's browser profile.
POST http://127.0.0.1:18795/api/download/start
Body: {
"url": "https://example.com/file.pdf",
"filename?": "files/file.pdf",
"saveAs?": false,
"conflictAction?": "uniquify|overwrite|prompt"
}
Returns: { ok: true, downloadId, id, options }
List Chrome downloads plus recent Browser Relay download events.
GET http://127.0.0.1:18795/api/downloads?limit=20&state=complete
POST http://127.0.0.1:18795/api/downloads/clear
Use this after browser_download_start to verify completion or diagnose interruptions.
Real downloads require the extension's downloads permission. If Browser Relay
was already loaded in Chrome before this capability was installed, reload the
unpacked extension in chrome://extensions.
When asked to do something with a web page:
browser-relay tabs first — discover available tabs and their URLsbrowser-relay navigate if needed — go to the target pagebrowser-relay snapshot — understand the page structurebrowser-relay click, browser-relay type, browser-relay key, browser-relay scroll) one at a timebrowser-relay wait for the next expected element after navigation or an action; do not guess with fixed sleepsbrowser-relay console if the page behaves unexpectedly or after risky actionsbrowser-relay network if a request fails, hangs, or the UI changes without visible errorsbrowser-relay download-start and browser-relay downloads for real file downloads# 1. List tabs
browser-relay tabs
# t_A7k2Pm9QxL Google https://google.com
# 2. Take snapshot to see the page
browser-relay snapshot --tab t_A7k2Pm9QxL
# [input type=text name=q placeholder="Search Google"]
# [button "Google Search"]
# 3. Type into the search box
browser-relay type 'browser relay' --selector 'input[name=q]' --submit --tab t_A7k2Pm9QxL
# 4. Wait for results instead of sleeping
browser-relay wait 'a[href*="github.com"]' --state visible --timeout 10000 --tab t_A7k2Pm9QxL
# 5. New snapshot after navigation
browser-relay snapshot --tab t_A7k2Pm9QxL
# 6. Click a result
browser-relay click 'a[href*="github.com"]' --tab t_A7k2Pm9QxL
Add to your MCP config (~/.claude/mcp.json or equivalent):
{
"mcpServers": {
"browser": {
"command": "browser-relay-mcp",
"env": {
"BROWSER_RELAY_URL": "http://127.0.0.1:18795"
}
}
}
}
| Error | Meaning | Fix |
|---|---|---|
| Extension not connected | Relay server is running but no browser extension connected | Check that Chrome is running with the Browser Relay extension installed |
| No attached tabs | Extension connected but no tab is attached | The extension auto-attaches all regular tabs. Make sure at least one non-chrome:// tab is open |
| Element not found: selector | The CSS selector did not match anything on the page | Try a different selector, or take a snapshot first to inspect the DOM |
| wait_timeout | The selector did not reach the requested attached/visible state before timeout | Re-snapshot the page, check the selector, or retry when the page is expected to load more slowly |
| Session with given id not found (-32001) | The relay holds stale session state (e.g. after an extension reload) | Run browser-relay fix — restarts the relay and clears stale sessions; the extension reconnects automatically |
curl http://127.0.0.1:18795/
# → OK
curl http://127.0.0.1:18795/api/debug
# → { "version": "<package-version>", "connected": true/false, "tabCount": N }
npx claudepluginhub reliefeai/browser-relayControls local Chrome/Chromium browser via CDP for tab management, signed-in profiles, anonymous sessions, screenshots, console logs, network capture, form filling, and PDF export. Use when tasks require an actual browser with state.
Controls Chrome via DevTools Protocol for navigating, clicking, typing, multi-tab management, and content extraction with auto-screenshots.
Drives an existing browser via CDP for authenticated, visual, or interactive web automation. Use when static fetches are insufficient and you need real browser interaction.