lyra-browser
OfficialServer Configuration
Describes the environment variables required to run the server.
| Name | Required | Description | Default |
|---|---|---|---|
| HERMES_HOME | No | hermes: data goes to `$HERMES_HOME/browser`. | ~/.hermes |
| VEGA_DATA_DIR | No | vega: data goes to `$VEGA_DATA_DIR/browser`. | |
| LYRA_BROWSER_GUARD | No | Where the navigation guard stands: `route` (Playwright's route) or `cdp` (a second DevTools connection: every redirect hop judged, cache on, DataDome lets the browser in, a loopback debugging port, `guard_lost` if the guard fails). See Guard backends. | route |
| LYRA_BROWSER_PROXY | No | Route the browser through a proxy, e.g. `socks5://100.x.y.z:1080`. For UAT runs that must not share the operator's egress IP, since per-IP rate limits, quotas and IP-based analytics exclusion all key on it. Unset means a direct connection. | |
| LYRA_BROWSER_CLIENT | No | `vega`, `hermes` or `generic` (also `--client`). Picks default paths and window mode only, never permissions. Detected from `VEGA_DATA_DIR` → vega, `HERMES_HOME` → hermes. | detected |
| LYRA_BROWSER_DRIVER | No | Which Playwright to launch with: `auto` takes `patchright` when installed and falls back to `playwright`; either name pins it. `open_browser` reports the one in use as `driver`. | auto |
| LYRA_BROWSER_CHANNEL | No | Force one channel (`chrome`/`msedge`). Unset = try chrome→msedge→bundled. | |
| LYRA_BROWSER_DATA_DIR | No | Base data dir (profile + audit). Overrides everything. | |
| LYRA_BROWSER_HEADLESS | No | Run without a visible window (see Headless mode). Unset: headless for hermes, and on Linux with no `DISPLAY`/`WAYLAND_DISPLAY`; otherwise a window. | auto |
| LYRA_BROWSER_VIEWPORT | No | `WIDTHxHEIGHT` of the page (`390x844` for a phone layout). Headless uses it as the emulated viewport; headful as the window size. Anything unreadable falls back to the default. | 1280x800 |
| LYRA_BROWSER_GRANT_TTL | No | Seconds a grant stays usable. Single-use ones ignore this. | 600 |
| LYRA_BROWSER_CAPTURE_DIR | No | Where `screenshot`/`read_image` write PNGs. Default: vega `$VEGA_DATA_DIR/uploads/browser` (where VEGA attaches images), hermes `$HERMES_HOME/cache/browser` (where Hermes' vision may read), else `<data_dir>/captures`. | |
| LYRA_BROWSER_ENFORCEMENT | No | `observe` records what it would have blocked without blocking: navigations go through, and an undeclared download is saved (`observed: true`, audited `would_block`) instead of cancelled. | enforce |
| LYRA_BROWSER_CAPTURE_KEEP | No | Captures kept on disk; oldest are deleted first. `0` keeps all. | 200 |
| LYRA_BROWSER_DOWNLOAD_DIR | No | Where declared downloads are saved. Default `<data_dir>/downloads`. | |
| LYRA_BROWSER_ALLOW_BUNDLED | No | Allow bundled-Chromium fallback (only exists after `playwright install`). | true |
| LYRA_BROWSER_DOWNLOAD_KEEP | No | Saved downloads kept; oldest are deleted first, and only files this server saved (tracked in a ledger in the dir). `0` keeps all. | 200 |
| LYRA_BROWSER_RELEASE_GRACE | No | Seconds before a finished action's unused single-use scope is reclaimed. | 2 |
| LYRA_BROWSER_CONSENT_CHANNEL | No | `auto` asks the user over MCP and falls back to `confirm=true` only on a client that cannot be asked; `elicit` pins asking and denies otherwise; `legacy` always takes the model's word; `off` does not ask. | auto |
| LYRA_BROWSER_CONSENT_TIMEOUT | No | Seconds a prompt waits for an answer before it counts as a denial (`timed out waiting for the user`). The tool call is held that long, so for an unattended Hermes gateway set it lower — e.g. `60`. | 300 |
| LYRA_BROWSER_TRUSTED_ORIGINS | No | Sites pre-approved for `NAVIGATE`/`INTERACT`, comma- or whitespace-separated: `https://host[:port]`, a bare `host` (https only), or `*.example.com` (https subdomains, not the apex). Never covers `SUBMIT`/`UPLOAD`/`PUBLISH`/`DOWNLOAD`. See Trusted origins. | |
| LYRA_BROWSER_DOWNLOAD_TIMEOUT | No | Seconds a declared download may take to arrive once it has started before it is cancelled (`download_failed`, `timeout`). | 120 |
| LYRA_BROWSER_REQUIRE_APPROVAL | No | Ask before risky actions. `false` sets the consent channel to `off`. | true |
| LYRA_BROWSER_DOWNLOAD_MAX_BYTES | No | Largest file a download may be (200 MiB); a bigger one is cancelled and answered `download_failed` (`too_large`). | 209715200 |
| LYRA_BROWSER_OWNER_IDLE_TIMEOUT | No | Seconds a session may hold the browser without using it. Handing it on closes the window. | 900 |
| LYRA_BROWSER_TRUSTED_SEND_ORIGINS | No | Sites where *sending* is also pre-approved — `SUBMIT` and `UPLOAD` — for acceptance runs against your own product. Same entry forms, **separate list**: a site in `TRUSTED_ORIGINS` is not send-trusted by being there. Never `PUBLISH` or `DOWNLOAD`; each approval stays single-use, and it steps aside during a takeover. Audited as `consent_channel=trusted_send`. |
Instructions
Guidance the server publishes about itself, which clients place ahead of the tool catalog so the model reads it before choosing anything.
This server publishes no instructions, or was last inspected before Glama recorded them.
Capabilities
Features and capabilities supported by this server
Protocol revision2025-11-25
| Capability | Details |
|---|---|
| tools | {
"listChanged": true
} |
| logging | {} |
| prompts | {
"listChanged": false
} |
| resources | {
"subscribe": false,
"listChanged": false
} |
| extensions | {
"io.modelcontextprotocol/ui": {}
} |
| experimental | {} |
Tools
Functions exposed to the LLM to take actions
| Name | Description |
|---|---|
| open_browserA | Launch (or focus) the visible browser window the user shares with you. If no browser is installed this returns a |
| close_browserC | Close the shared window, discarding every approval it earned. This is how a session finishes: the window goes away, the grants go with it, and the next session may claim the browser. Without it the refusal another session receives would name a wait that never ends. |
| navigateA | Navigate the shared window to a URL. Going to a different site asks for permission to be on that site.
Moving within the current one does not. A URL with no host —
|
| go_backB | Go back one entry in history. Where "back" leads is not known until it happens, so this asks only to
interact with the current site; landing somewhere else is judged then.
Reports |
| reload_pageC | Reload the current page. Reports |
| clickA | Click the first element matching Set Fails fast instead of hanging. A selector that matches nothing answers
The reply says how many elements matched ( If the click makes the page navigate and the browser refuses it — no
approval covers the destination, or a form was sent without
Set A native dialog the page raised is answered at once and listed under
|
| type_textA | Fill Set Fails fast like A native dialog the page raised is answered at once and listed under
|
| press_keyA | Press a keyboard key (e.g. 'Enter', 'Escape', 'Control+a') on the page. No key is treated as special: Enter, Space and anything else all send
whatever the page decides to send, and that is judged when it happens.
Set Set A native dialog the page raised is answered at once and listed under
|
| hoverA | Move the pointer over the first element matching For menus and tooltips that only open while the pointer is on their
trigger: hover, then Fails fast like |
| scrollA | Scroll the page. Give exactly one of
Pages that load more as you reach the end (feeds, lazy lists) grow
after a scroll, so call
|
| handle_dialogA | Choose how a native dialog is answered — for the NEXT browser action only. Dialogs never block the page: every Call this immediately BEFORE the one action that raises the dialog.
The answer is for the very next browser-acting call (click, type_text, press_key, hover, scroll, navigate, go_back, reload_page, select_option, set_editor, upload_file, save_draft, publish, tabs switch or close) and only for a dialog that call raises while it runs, on any tab. It ends with that call whether or not a dialog appeared — also when the call was refused, so arm again before retrying — and after about a minute at the latest. A dialog raised later, by a page's own timer or on a page you reach afterwards, is answered by default. Reading (read_page, screenshot, get_url, wait_for ...) does not use it up. Arming again replaces it. |
| read_formA | List a form's fields with their names, types, labels and options.
Set Each field carries |
| select_optionA | Choose an option in a Give exactly one of the three. A dropdown is not a click target: this
sets the value and fires |
| set_editorA | Write into a rich-text editor — the body a form actually posts. For CKEditor, TinyMCE and similar the visible editor is an
|
| upload_fileA | Attach one or more local files to a file picker. This hands a file on this machine to a page, which is why it asks for its
own permission, once per call. |
| read_draftA | Collect what a form is about to publish, for a human to approve. Approving an item means reading the whole of it, and on a real form that
means looking in several places at once: the visible fields, the rich-text
body (a separate document Nothing is changed or sent — this is a read, so it needs no approval.
Fields carry |
| save_draftA | Save the item without showing it to anyone — the safe way to send a form. A site with no separate "save draft" button means every form POST is the one that could publish, so the difference has to be read off the page rather than chosen by the caller. This sends the form only after confirming the publish control is still on its private setting, and refuses if it is not — so it cannot be used to publish, and a page left armed by anything else will not slip out through it. Measured on KVR: a new news item opens with
Use |
| publishA | Make a draft public — the one action that sends content to an audience. Kept apart from every other tool so that writing a draft can never publish
it: this asks for A site splits this across two controls and means neither of them alone.
Measured on KVR: the publish radio (
Call this only when a human has approved the exact item. Show them what
will go public and get their answer first; the approval prompt this raises
is the second gate, not the first. Afterwards |
| get_urlA | Return the current URL and page title, and how many tabs are open ( |
| read_pageA | Read the page as text, or as a map of what can be acted on.
Pass the
|
| screenshotA | Capture the page as a PNG file and return its path. Use it to see the page — layout, images, charts, anything
|
| read_imageA | Capture one element — an This is how you look at a picture on the page: it captures the pixels
the element renders, so it works for anything drawn, not only |
| tabsA | List the browser's tabs, switch to one, or close one.
Indexes shift whenever a tab opens or closes, so list again before using
one. Tabs a page opens are followed automatically, and a popup that
closes itself (a sign-in window) returns you to where you were — this is
for when you have to choose. Switching to or closing a tab asks for
permission to use that tab's site, like any other action there;
|
| wait_forA | Wait until the page is ready, instead of sleeping or clicking to pass time. Give exactly one condition:
Waits up to Use it after |
| list_downloadsA | List the files this session has downloaded, oldest first. Each entry is |
| highlight_elementA | Outline an element in the shared window so the user can see what you mean. Requires an attended (headful) session — in headless mode there is no one
to show it to, so this returns |
| ask_user_to_doA | Ask the user to perform a step you must not do yourself. Use for credentials, CAPTCHAs, 2FA, or payment confirmation. Highlights
the relevant element when Requires an attended (headful) session — in headless mode there is no
user to ask, so this returns |
| request_takeoverB | Hand control to the user. Agent mutations are blocked until they hand back. Requires an attended (headful) session. In headless mode the takeover is refused and no state changes: blocking every mutation while waiting for a user who cannot see the window would deadlock the run. |
| resume_after_takeoverB | Resume agent control after the user has handed the session back. Releasing a takeover goes through the consent channel: an agent that can
clear its own lock can ignore the lock. On a client without elicitation
this still succeeds — the release is recorded as the model's own
assertion, which is what |
Prompts
Interactive templates invoked by user choice
| Name | Description |
|---|---|
No prompts | |
Resources
Contextual data attached and managed by the client
| Name | Description |
|---|---|
No resources | |
TDQS
Scored across 29 tools
Most tools have clearly distinct purposes within a browser-automation domain. Some potential for confusion exists between read_page (text/tree reading) and read_form (form field listing), or between navigate/go_back/reload_page. However, the detailed descriptions strongly clarify when to use each, keeping overlap manageable.
Mix of verb_noun (open_browser, close_browser, read_page, read_form, read_image) and verb-only (click, type_text, navigate, scroll, hover) patterns. The conventions are still readable and the verbs are clear, but there is no single predictable pattern across all 29 tools.
29 tools is at the high end for a browser automation server, especially since many are fine-grained actions (click, type_text, press_key, hover, scroll) that could be combined into a more general 'interact' tool. The count feels heavy for the domain, though each tool does have a specific role.
Covers a wide range of browser automation needs: navigation, reading, interaction, downloads, forms, tabs, dialogs, and session control. A few gaps remain (e.g., no explicit tool for executing JavaScript or managing cookies/local storage, no direct window resizing), but these are minor for most agent workflows.