fastbrowser Skill
fastbrowser-cli is a command-line client for the FastBrowser HTTP server, which keeps a persistent
MCP connection to a Chrome browser alive so commands incur minimal latency. Each command
maps 1-to-1 to a FastBrowser tool and returns the tool's response on stdout.
Invocation
Run the CLI directly via tsx:
npx fastbrowser_cli@latest <command> [flags]
Typical Workflow
- Query the accessibility tree for specific nodes with
query_selectors(first match per selector by default; pass-a, --allfor every match). - Act on an element by its accessibility selector:
click,fill_form,press_keys. The selector can be a direct uid reference (e.g.#1_42, fastest path) or any CSS-like selector (e.g.button[name="Submit"]), which is resolved to a uid internally.
Snapshot output looks like:
uid=1_0 RootWebArea "Example Domain" url="https://example.com/"
uid=1_1 heading "Example Domain" level="1"
uid=1_2 link "More information..." url="https://www.iana.org/..."
Page Management
# List all open browser pages
npx fastbrowser_cli@latest list_pages
# Open a new page at a URL
npx fastbrowser_cli@latest new_page --url https://example.com
# Close a page by its numeric id
npx fastbrowser_cli@latest close_page --page-id 1
# Navigate the current page to a URL
npx fastbrowser_cli@latest navigate_page --url https://example.com
# Restart the daemon - run this if pages opened by the bridge were closed manually
# and the MCP connection has broken
npx fastbrowser_cli@latest server restart
Configuration
Global flags accepted by every command:
| Flag / env | Purpose | Default |
|---|---|---|
--server <url> / FASTBROWSER_SERVER |
URL of the fastbrowser_httpd daemon |
http://localhost:8787 |
--autostart / --no-autostart |
Auto-start the daemon if it is not already running | --autostart |
--mcp-target <target> / FASTBROWSER_MCP_TARGET |
Browser backend: playwright or chrome_devtools |
playwright |
The daemon binds to one backend at startup. If it is already running with a different backend, the CLI refuses the request and prints the exact restart command to switch.
# Use chrome-devtools-mcp for one command
npx fastbrowser_cli@latest --mcp-target chrome_devtools list_pages
# Switch the running daemon to a different backend
npx fastbrowser_cli@latest --mcp-target chrome_devtools server restart
Selector Language
The selector syntax is modelled on CSS selectors, adapted for accessibility tree structures.
Role selector
Matches nodes by their accessibility role.
button
link
combobox
searchbox
heading
WebArea
Universal selector
Matches any node.
*
UID selector
Matches a node by its exact unique identifier.
#4
#1_3
Attribute selectors
Attribute selectors match values inside node.attributes. The special virtual attribute name maps to node.name.
| Syntax | Semantics |
|---|---|
[attr] |
attribute is present |
[attr="value"] |
exact match |
[attr^="prefix"] |
starts with |
[attr$="suffix"] |
ends with |
[attr*="sub"] |
contains substring |
[attr~="word"] |
contains word as a whole space-separated word |
link[href]
button[disabled="true"]
link[href^="https"]
link[href$=".com"]
link[href*="example"]
button[name~="Submit"]
heading[name="Welcome"]
link[name="Click \"here\""]
Combinators
| Syntax | Semantics |
|---|---|
A B |
B is a descendant of A (any depth) |
A > B |
B is a direct child of A |
A + B |
B is the immediately following sibling of A |
A ~ B |
B is any following sibling of A |
A, B |
union — matches A or B |
WebArea link
main > button
label + textbox
link ~ link
heading, button
RootWebArea > link[href^="https"]
Positional pseudo-classes
Narrow a match by position within the parent's children array. Indexing is 1-based; the root node never matches a positional pseudo-class.
| Syntax | Semantics |
|---|---|
:first-child |
node is the first child of its parent |
:last-child |
node is the last child of its parent |
:nth-child(n) |
node is the nth child (1-based) |
link:first-child
button:last-child
menuitem:nth-child(2)
Functional pseudo-classes
Take a comma-separated selector list inside parentheses. The argument list itself supports the full selector language and may be nested.
| Syntax | Semantics |
|---|---|
:is(s1, s2, …) |
node matches any selector in the list |
:where(s1, s2, …) |
alias of :is() (no specificity in this engine) |
:not(s1, s2, …) |
node matches none of the selectors |
:has(s1, s2, …) |
node has a descendant matching any selector |
:has() walks descendants of the candidate node (excluding the node itself). Relative leading combinators (e.g. :has(> link)) are not supported.
:is(heading, button)
link:not(:first-child)
*:has(button)
*:not(:has(link))
main > *:not(button)
Examples
Sample accessibility tree:
uid=1 WebArea "Main Page"
uid=2 main
uid=3 heading "Welcome"
uid=4 link "Click here" href="https://example.com"
uid=5 button "Submit" disabled="true"
uid=6 navigation
uid=7 link "Home" href="/"
uid=8 link "About" href="/about"
Example queries on it:
linkmatches all the links (uid=4, uid=7, uid=8)navigation > linkmatches only the links that are direct children of navigation (uid=7, uid=8)link[href^="https"]matches links with an external href (uid=4)button[name="Submit"]matches the submit button by name (uid=5)*[disabled="true"]matches any disabled element (uid=5)heading, buttonmatches both headings and buttons in one query (uid=3, uid=5)#7matches a node by its UID (uid=7)link:first-childmatches uid=4 and uid=7 (first child ofmainandnavigation)link + buttonmatches uid=5 (button immediately after a link):is(heading, button)is equivalent toheading, button(uid=3, uid=5)link:not([href^="https"])matches the relative-href links (uid=7, uid=8)*:has(button)matches ancestors of a button (uid=1, uid=2)
Inspection
query_selectorsis the most efficient way to get specific elements or data from the page. Use it instead oftake_snapshotwhenever possible.- By default,
query_selectorsreturns the first match per selector (cheaper, less output). Pass-a, --allwhen you need every match — pair it with--limitto cap results per selector. - Use
--wa, --with-ancestorsto include each match's ancestor chain in the result, and--wc, --with-childrento include the descendant subtree of each match.
# Query the accessibility tree returning the FIRST match per selector (--selector is repeatable)
npx fastbrowser_cli@latest query_selectors --selector "button" --selector "link"
# Include ancestor nodes in the result
npx fastbrowser_cli@latest query_selectors --selector 'heading[level="1"]' --with-ancestors
# Include the descendant subtree of each match
npx fastbrowser_cli@latest query_selectors --selector 'main' --with-children
# Per-selector control over withAncestors / withChildren via JSON
npx fastbrowser_cli@latest query_selectors \
--selectors-json '[{"selector":"button","withAncestors":true},{"selector":"link","withChildren":true}]'
# Pass --all to return every match per selector; --limit caps results per selector (0 = unlimited)
npx fastbrowser_cli@latest query_selectors --all --selector "button" --selector "link" --limit 5
# Include ancestor nodes in the result
npx fastbrowser_cli@latest query_selectors --all --selector 'heading[level="1"]' --with-ancestors
# Per-selector control over limit / withAncestors / withChildren via JSON (with --all)
npx fastbrowser_cli@latest query_selectors --all \
--selectors-json '[{"selector":"button","limit":3,"withAncestors":true},{"selector":"link","limit":0,"withChildren":true}]'
# Take an accessibility-tree full page snapshot of the current page - very expensive, prefer targeted queries when possible
npx fastbrowser_cli@latest take_snapshot
Interaction
# Click by a direct uid reference (fast path - no accessibility-tree lookup)
npx fastbrowser_cli@latest click --selector "#1_42"
# Click by any CSS-like selector - resolved to a uid internally
npx fastbrowser_cli@latest click -s 'button[name="Submit"]'
# Fill a single form field - selector can be a uid (#1_7) or any CSS-like selector
npx fastbrowser_cli@latest fill_form -s 'textbox[name="Email"]' -v "hello@example.com"
# Press a comma-separated sequence of keys (literals and named keys both work)
npx fastbrowser_cli@latest press_keys --keys "Tab, Tab, Enter"
npx fastbrowser_cli@latest press_keys --keys "Hello, Tab, Enter"
Batch Execution
Run several commands from one invocation — each line is parsed and executed like a standalone command, reusing the persistent HTTP server session.
Syntax rules:
- One command per line, same syntax as the standalone CLI (e.g.
click -s '...') - Shell-style quoting (single and double quotes)
- Blank lines are ignored
- Lines starting with
#are comments
By default, the batch stops at the first failing line (shell set -e semantics). Pass --no-stop-on-error to log failures and continue; the process still exits non-zero at the end if any line failed.
# From a file
npx fastbrowser_cli@latest batch ./demo.fbs
# Piped on stdin
cat demo.fbs | npx fastbrowser_cli@latest batch
# Inline script
npx fastbrowser_cli@latest batch --script $'press_keys --keys "Enter"\nclick -s 'button[name^="Tout effacer"]''
# Continue through failures
npx fastbrowser_cli@latest batch --no-stop-on-error ./demo.fbs
Example demo.fbs:
# open a page and interact
new_page --url https://example.com
fill_form -s 'textbox[name="Email"]' -v 'hello@example.com'
press_keys --keys "Tab, Enter"
Command Reference
| Command | Purpose | Required flags |
|---|---|---|
list_pages |
List open browser pages | — |
new_page |
Open a new page at a URL | --url |
close_page |
Close a page by id | --page-id |
navigate_page |
Navigate current page to a URL | --url |
take_snapshot |
Dump the accessibility tree of the whole page - very expensive, prefer targeted queries (query_selectors) when possible |
— |
query_selectors |
Query a11y tree by CSS-like selector (first match per selector by default; pass -a, --all for every match, with optional --limit) |
--selector or --selectors-json |
click |
Click an element by accessibility selector | --selector / -s |
fill_form |
Fill a form field by accessibility selector | --selector / -s, --value |
press_keys |
Press a comma-separated key sequence | --keys |
batch |
Run multiple commands from a file, piped stdin, or --script inline |
one of: <file>, --script, or piped stdin |
install [skill-folder] |
Install bundled skills into <skill-folder>/skills/ (default: .) |
— |
server start |
Start the HTTP server daemon | — |
server status |
Report server running/stopped | — |
server stop |
Stop the HTTP server | — |
server restart |
Restart the HTTP server (re-establishes the MCP connection) | — |
Output & Errors
Tool output is written to stdout — one line per response content part. On failure the
CLI writes fastbrowser-cli error: <message> to stderr and exits with code 1.