browser — Chrome / Edge / Brave over DevTools#
Drive a Chromium-family browser through the Chrome DevTools Protocol instead of pixels. The agent reads the page as text plus an accessibility snapshot, then clicks elements by reference. This is faster and much more reliable than screenshot → OCR → mouse for anything inside a web page.
from opendesk.tools.browser import BrowserTool
tool = BrowserTool() # needs `websockets` (pip install 'opendesk[remote]')
import { OpenDeskClient } from "@vitalops/opendesk-sdk";
const client = new OpenDeskClient();
await client.browser({ action: "launch", url: "https://example.com" });
Supported: Google Chrome, Chromium, Microsoft Edge, Brave. The binary is found automatically. To use a specific one, pass browser (chrome, chromium, edge, brave, or a full path) or set $OPENDESK_BROWSER.
Actions#
Action |
Parameters |
Description |
|---|---|---|
|
|
Start a browser with |
|
|
Connect to a browser already started with |
|
— |
List tabs: index, id, title, url ( |
|
|
Open a new tab and make it current |
|
|
Load a URL and wait for the load event |
|
|
Title, URL, visible text, and an accessibility snapshot with refs ( |
|
|
Click an element: by ref from |
|
|
Type into a field (or the focused element); |
|
|
Wait until something appears |
|
|
PNG of the tab, returned as an image |
|
|
Run a JavaScript expression and return its JSON value |
|
|
Close the current or given tab |
tab accepts an index from tabs, a target id (or its prefix), or text that appears in the tab’s title or URL. The default is the current tab.
Parameters#
Parameter |
Type |
Default |
Description |
|---|---|---|---|
|
string |
— |
One of the actions above |
|
string |
null |
URL for |
|
int |
|
DevTools port for |
|
string |
first found |
|
|
bool |
|
|
|
string |
|
Profile override; must be empty or created by opendesk |
|
int | string |
current |
Which tab to act on |
|
string |
null |
Element ref from the latest |
|
string |
null |
CSS selector |
|
string |
null |
Visible text ( |
|
string |
null |
JavaScript for |
|
bool |
|
|
|
bool |
|
|
|
bool |
|
|
|
int |
|
|
|
number |
|
Seconds for |
The JS SDK uses camelCase for the multi-word ones (profileDir, fullPage, maxChars) and also accepts the snake_case names.
What read returns#
Title: Sign in
URL: https://example.com/login
--- visible text (212 chars) ---
Sign in
Email
Password
Forgot password?
--- accessibility snapshot (6 refs; use ref=eN with click/type) ---
[e1] RootWebArea "Sign in" (focused)
[e2] heading "Sign in"
[e3] textbox "Email"
[e4] textbox "Password"
[e5] button "Continue"
[e6] link "Forgot password?"
Refs belong to the latest read of that tab. After the page changes, call read again.
click scrolls the element into view and sends a real mouse press and release at its centre. If another element covers that point, or the element has no size, it falls back to a DOM element.click(), and the result says which was used.
Profiles and safety#
launchalways passes--user-data-dir. By default that is~/.opendesk/browser-profile, so cookies and logins stay separate from the user’s everyday browser. The tool marks that directory as its own, and it refuses any directory that already has content but no marker, so it can’t take over a real profile by accident.If the port already has a DevTools endpoint that opendesk didn’t start,
launchrefuses and suggestsattach.attachconnects to any browser you started with--remote-debugging-port. Use it on a profile you are willing to automate.Every action is recorded in the session audit log as
browser.
!!! note “Local-only in v1”
browser always runs on the machine hosting the opendesk process. The MCP server does not add a peer field to it and does not route it to paired peers, in either SDK. To drive a browser on another machine, run script there or use the GUI tools with peer.
Ask Claude#
“Open Hacker News and tell me the top 5 stories”
“Log into the staging dashboard and download last week’s report”
“Fill in this form with my shipping address”
“Wait for the build page to say ‘Succeeded’, then screenshot it”
“What’s the price of the first result on this page?”
SDK examples#
params = BrowserTool.Params
await tool.execute(ctx, params(action="launch", url="https://news.ycombinator.com"))
page = await tool.execute(ctx, params(action="read"))
print(page.output) # text + [eN] refs
await tool.execute(ctx, params(action="click", text="new"))
await tool.execute(ctx, params(action="wait_for", selector=".athing"))
titles = await tool.execute(ctx, params(
action="eval",
expression="[...document.querySelectorAll('.titleline a')].slice(0,5).map(a => a.textContent)",
))
await tool.execute(ctx, params(action="type", selector="input[name=q]", text="opendesk", submit=True))
shot = await tool.execute(ctx, params(action="screenshot", full_page=True))
open("page.png", "wb").write(shot.attachments[0].content)
await client.browser({ action: "attach", port: 9222 });
const page = await client.browser({ action: "read" });
await client.browser({ action: "click", ref: "e5" });
await client.browser({ action: "type", ref: "e3", text: "me@example.com", clear: true });
const shot = await client.browser({ action: "screenshot", fullPage: true });
To shut down a browser you launched, call close_browser_session(session_id) in Python or closeBrowserSession(sessionId) in JS.
Next: fleet →