agent-browser-mcp
Server Configuration
Describes the environment variables required to run the server.
| Name | Required | Description | Default |
|---|---|---|---|
| AGENT_BROWSER_MODE | No | lab prioritizes continuous automation and reuses session approvals; safe prompts for every physical-input/site-allow action. set_automation_profile changes only the current MCP process. | lab |
| AGENT_BROWSER_NO_SPAWN | No | Set to 1 to stop the MCP server from auto-starting the bridge. Use it when you run the bridge yourself. | |
| AGENT_BROWSER_TMWD_HOST | No | Bridge bind address. | 127.0.0.1 |
| AGENT_BROWSER_TMWD_PORT | No | WebSocket port. HTTP uses PORT+1, and PORT+2 is a lock socket that keeps exactly one bridge hosting. | 18765 |
| AGENT_BROWSER_LAB_NO_ELICIT | No | Set to 1 to skip physical-input and site-allow elicitation in lab. The cross-process lock and quiet-input gate still apply. | |
| AGENT_BROWSER_PREFERRED_BROWSER | No | chrome, edge, or opera. Which browser wins when several are connected and no tab is specified. | |
| AGENT_BROWSER_AUTO_BEFOREUNLOAD_HOSTS | No | In lab, ordinary open_url accepts beforeunload on matching current hosts. intent_leave=false always preserves the page. | shell.,ttyd,code-server,jupyter,vscode-web |
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": false
} |
| prompts | {
"listChanged": false
} |
| resources | {
"subscribe": false,
"listChanged": false
} |
| experimental | {} |
Tools
Functions exposed to the LLM to take actions
| Name | Description |
|---|---|
| get_automation_profileA | Return the active safe/lab automation profile. Lab is the default and skips elicitation unless BROWSERTAP_LAB_NO_ELICIT is explicitly disabled; safe requires approval for every physical action and permission allow. |
| set_automation_profileA | Set the safe or lab automation profile for this MCP process. This does not persist or reload the extension; BROWSERTAP_MODE controls the next process. |
| get_setup_statusA | Return component versions, stale-build actions, extension path, bridge ports, and connection status for setup/diagnostics. extension_build_verdict answers whether the browser worker is running this code (matches_tree / stale_worker), or says why it cannot tell (stamp_not_regenerated / unverifiable); it is decisive where version equality is not. extension_build_enforced=false means no comparison happened, so treat it as unknown rather than as a pass. extension_status_available=false means no runtime status was obtained: starting asks to wait_for_extension, then extension_unavailable asks to check_extension_connection; missing status alone never requests a reload. mcp_build_verdict and bridge_build_verdict compare each process's package-import Python and imported JavaScript source snapshot with expected_python_source_identity on disk: matches_tree, stale_process, or unverifiable. Same-version source changes require restart_mcp_session or restart_bridge; *_build_enforced=false is unknown. state_paths distinguishes missing/empty/ready/unreadable/invalid_encoding token files without exposing token content; unreadable metadata has unknown existence. On Windows, inspecting an existing token file may tighten its ACL to the current user. Bridge startup or authentication initialization may create a missing token file. A malformed remote diagnosis reports bridge_unreachable with malformed_diagnosis, not evidence of an old build. Answers while another tool is still running; default_session_id is isolated from other calls' temporary targets. capability_registry reports declared/registered tool counts, completeness and page/browser/desktop groups. Each tool's MCP annotations describe its potential effects. |
| list_tabsA | List connected tabs across all connected browsers; each tab has a browser field (chrome/edge/opera) and a session id to pass verbatim. Answers while another tool is still running. The default_session_id snapshot is isolated from other calls' temporary targets. Pass session_id explicitly when agents share one MCP process. A timed-out inventory releases only that read's bridge reservation; pending mutations keep their reservations. |
| list_all_tabsA | List every open tab, including chrome-extension:// pages that list_tabs hides. Those never become sessions (content scripts can't run there), so they have no session id — drive them with cdp_command(tab_id=...) instead. Works with no tabs open. |
| close_tabsA | Close one or more tabs by native tab id or composite session_id. Accepts a single identifier or a list; identifiers in one call must belong to the same browser. By default it closes only tabs created by this MCP task and requires the owner_id returned by open_new_tab; lifecycle generations are checked before removal. Set only_if_agent_owned=false only for an explicit operator request to close a user tab. |
| switch_tabA | Set the target tab for later calls by session id, URL substring, or browser name ('chrome'/'edge'/'opera') without focusing the browser. A URL substring must match exactly one tab; pass its full session_id when several tabs match. Use activate=true or activate_tab when foreground work is required. |
| activate_tabA | Bring a tab to the foreground and focus its window. Use this explicitly after switch_tab when foreground work is required, or to re-raise a tab the user has since clicked away from. |
| open_urlA | Navigate the current real-browser tab through CDP without raising its window. beforeunload defaults to dismiss, except lab mode auto-accepts configured shell/IDE hosts. Use accept to leave explicitly, manual to inspect, or intent_leave=false to force the conservative dismiss behavior even on a lab auto host. |
| handle_dialogB | Inspect or handle a JavaScript dialog on the requested real-browser tab. action is dismiss, accept, or manual; manual reports the dialog without choosing. |
| resolve_leave_dialogA | Resolve an intended beforeunload leave in one bounded workflow: protocol accept twice, return immediately when no dialog exists, then use a lab-only foreground Enter fallback after the normal physical-input approval gate only when protocol handling actually fails. Approval failures expose reason in the result and diagnostics: elicitation_unsupported, declined, timeout, cancelled or error. |
| open_new_tabA | Create one background browser tab, deduplicated by operation_id. Uses this MCP task's selected browser; select one with switch_tab or session_id/client_id when several are connected. Pass active=true for foreground work. Save owner_id for cleanup; ownership requires a completed record with exact client_id, tab_id and generation. Before dispatch, an unresolved probe returns unknown, may_have_created=false. When retry_safe=true, resolve its cause and retry with no operation_id. After uncertain dispatch, may_have_created=true,retry_safe=false: pass the returned operation_id, client_id and owner_id to this tool to read the same creation record without replaying. Failed recovery preserves uncertainty and ownership. A failed probe's separate reconciliation.bridge_operation.operation_id can be read with get_execute_js_result; its reservation_held=false does not prove non-creation or permit replay. After worker restart pending creates become terminal unknown; retired IDs retain replay guards. If reconciliation.resume_required=false, including initial not_found, follow its list_tabs() inspection guidance and stop repeating recovery. Matching URLs, unchanged tab counts, missing records and retired IDs prove neither non-creation nor ownership. |
| extension_pathA | Get absolute path to the unpacked Chrome extension directory for manual installation. |
| list_extensionsA | List installed browser extensions (id, name, enabled, type, version). Works with no tabs open. |
| set_extension_enabledA | Enable or disable an installed extension by id. Chrome exposes no API to INSTALL an extension, so this only toggles ones already present; use list_extensions for ids. The BTAP bridge refuses to disable itself -- nothing would be left to re-enable it -- so ask a human to press Reload on chrome://extensions to pick up a new build. |
| download_fileA | Download an http(s) URL through the real browser's native download manager, so the current browser profile's cookies and authenticated session are used. Waits for completion by default and returns the final absolute local path. directory may be any absolute local directory; completed files are moved there without replacing an existing file unless overwrite=true. A directory timeout reports directory_applied=false because Chrome may finish in its default download directory. An explicit session_id must still be live and is never replaced with another profile. Use this for attachments instead of page fetch. |
| uninstall_extensionA | Uninstall another installed extension by id. show_confirm_dialog defaults to true; set it false only for an explicitly selected disposable/test extension. The BTAP bridge cannot uninstall itself through its active connection. |
| get_bookmarksA | Return the browser bookmark tree. Works with no tabs open. |
| create_bookmarkA | Create a bookmark or folder. Supply url for a bookmark; omit url to create a folder. parent_id is optional and uses Chrome's default bookmark location when omitted. |
| remove_bookmarkA | Remove a bookmark by id. Set recursive=true only for a folder whose full subtree should be removed. First saves the complete subtree to an atomic local JSON backup; nothing is removed if the snapshot or backup fails. The managed backup subdirectory must not be a symlink or reparse point. Returns backup_path even if the deletion outcome is unknown. Backups stay in the state directory for up to 30 days, 100 files or 64 MiB; each subtree is limited to 16 MiB. The snapshot and browser removal are separate operations, so concurrent edits by the user are not transactional. |
| call_extensionA | Send a JSON message from the BTAP extension service worker to another installed extension. The target must be enabled and list this BTAP extension in externally_connectable. Works with no tabs open. |
| network_capture_startA | Start bounded CDP Network capture on a real-browser tab. Captures requests, responses, and optionally response bodies without foregrounding the tab. Call network_capture_stop from this same MCP session to return the buffer and release the debugger lease. Another session's capture returns capture_busy; use a separate tab per agent. |
| network_capture_stopA | Stop Network capture on a real-browser tab, optionally filter returned records by URL, resource type, HTTP status range, or response-body inclusion, and release its debugger lease. url_pattern uses the browser's JavaScript RegExp syntax and invalid patterns return a structured error. Only the MCP session that started the capture may stop it; another session gets capture_busy. |
| console_capture_startA | Start a bounded Runtime console and exception capture on a real-browser tab without foregrounding it. Use get_console_messages while running and console_capture_stop when done, from this same MCP session. Another session's capture returns capture_busy; use a separate tab per agent. |
| get_console_messagesA | Read a page of captured console messages and exceptions from a real-browser tab. Set clear=true to clear the full buffer after reading; only the capture's originating MCP session may clear it, otherwise capture_busy is returned. Non-clearing reads are shared. Set filter='user' to exclude extension service-worker / content-script logs and keep only the page's own main-world console output. |
| console_capture_stopA | Stop console capture on a real-browser tab, return the remaining bounded message buffer, and release its debugger lease. Only the MCP session that started the capture may stop it; another session gets capture_busy. |
| scan_pageA | Read the current page as simplified HTML/text, preserving login state from the real browser. cutlist collapses long repeated lists and reports a CSS selector for each container it collapsed, derived from that container's own structure. The built-in scan does not write page attributes, ids, or window globals. Optional extra_js runs caller code and can modify the page or send requests. Background tabs may report viewport height zero; ordinary DOM/text/API work still works there, and only visual/layout fidelity requires explicit activate_tab. The result also includes render_state/content_ready when the page can be probed: shell_only or hydrating means the SPA has not produced reliable content yet; retry scan_page or wait_for before treating an empty result as a real empty page. A timed-out built-in readiness probe releases its tab reservation; missing render fields mean readiness is unknown. observation adds current control locators, editability, recommended_tool and reasons; pass a locator unchanged as page_click/page_type selector. Its frames list gives frame paths for another scan_page call, including cross-origin/OOPIF documents. Frame scans are explicit and do not accept extra_js. Top-document extra_js preserves its existing execution semantics and omits actionable metadata. Locators are observations, not persistent refs: re-scan after page changes. verify_coordinate_target means inspect a screenshot before choosing viewport CSS coordinates; it is not an automatic click. upload_files is recommended only for top-document file inputs outside shadow roots; use locator.css as its string selector. Frame/shadow file inputs report no supported upload. max_targets caps controls and frame entries together (0 disables them, maximum 200); truncated=true means more targets or DOM nodes remain uninspected. maxchars caps content separately. Defaults: cutlist=true, maxchars=35000, max_targets=80, timeout=15 seconds. |
| wait_forA | Wait until a condition holds on the page, then return. Use this instead of polling scan_page (each scan re-serializes the whole DOM). Exactly one of selector / text / url_pattern / js must be given: selector waits for a CSS or structured-locator match, including nested same-origin/cross-origin iframe paths. Role locators exclude hidden and inert controls; disabled visible controls can still be observed. Text waits for a substring in body text, url_pattern for a regex on the URL, js for a JS expression to become truthy. Caller js is evaluated repeatedly and can have side effects; use a read-only predicate. The server schedules short synchronous page checks under one deadline. A delayed reply returns its operation_id for get_execute_js_result; it is never replayed while pending. Timed-out top-document selector/text/URL checks can release the tab while keeping that receipt: reservation_held=false permits another command. Framed checks and caller-provided js may stay reserved; when reservation_held is true or unknown, collect the original operation first. |
| wait_for_urlA | Wait for navigation to settle: blocks until the tab's URL matches url_pattern (regex, or plain substring) and — unless wait_ready=false — document.readyState is 'complete', then returns the final url, title and readyState. Use this after a click or open_url that navigates; wait_for(url_pattern=...) only checks the URL and can return while the new document is still blank. The server schedules short synchronous page checks. Delayed replies retain their operation_id for get_execute_js_result. A timed-out probe can release the tab without losing its receipt: reservation_held=false permits another command while the original reply remains collectible. When reservation_held is true or unknown, collect the original operation first. |
| scroll_pageA | Scroll the page and report the new position. scan_page omits anything past ±5000px from the current scroll offset, so on a long page: scan, then scroll, then scan again. Pass to='bottom'/'top', a pixel offset, or a CSS selector to bring into view. Defaults: to='bottom', timeout=15 seconds. |
| execute_jsA | Execute arbitrary JS, which can cause side effects, in session_id's browser tab under one total deadline. Pin an explicit session_id; fallbacks keep that target and never replay an already-started script. For complex async bodies use an explicit return in an async IIFE. accept/dismiss prepare current injectable frames on extension routes; the legacy CDP fallback covers only its current evaluation context. manual keeps native dialogs. wait=false returns operation_id after delivery acknowledgement; collect with get_execute_js_result in the same MCP session, without replay. partial/unknown results or an expired handle do not prove non-execution; inspect retry_safe before retrying. A dispatched exec_timeout retains an outcome_unknown reservation for the bounded recovery window; the deadline does not cancel page JS. Use wait_for/wait_for_url for page state instead of sleep Promises. Conversion on all routes: undefined/non-finite numbers become null; BigInt/symbol become strings; DOM/Error/functions become readable values; cycles/depth 6/iterables above 200 items have markers. JSON UTF-8 over 24 KiB or any unpaired UTF-16 uses a private JSON result_file with result_bytes, result_sha256, result_format and result_file_scope=js-value. result_file_encoding=json means JSON-decode the path once; otherwise use it directly. Parse the UTF-8 file once; it preserves the full converted value and markers. If file writing fails, parse the complete ASCII result_json once (result_json_scope=js-value); this fallback may exceed the inline limit and preserves original result/retry verdicts. |
| get_execute_js_resultA | Read or wait for an operation_id from execute_js or another timed-out bridge command, including open_new_tab's nested reconciliation.bridge_operation.operation_id. The outer tab-creation ID uses open_new_tab recovery. Call from the submitting MCP session; this tool never replays operations. timeout is 0-120 seconds. Completed results are repeatable for up to 10 minutes and 512 records, with earlier capacity eviction. Unknown/expired does not prove non-execution. After reservation expiry, the first valid terminal reply is late_result (success,data), with late_reply_age in seconds; unknown and retry_safe=false remain. Late replies neither renew retention nor reserve targets again. Known read-only wait, inventory and creation-status probes can release on timeout; reservation_held describes that probe, not a prior mutation. Large or unpaired-UTF-16 values use execute_js result_file metadata or fallback result_json, with scope=js-value. Late descriptors are inside late_result with data=null. JSON-decode the path only when result_file_encoding=json, then parse the UTF-8 file or result_json once as JSON. Export failure preserves the complete value and original retry verdict. |
| cdp_commandA | Raw CDP can cause side effects in a tab, extension target or the entire browser profile. Choose session_id (client:tabId), tab_id (number or composite), extension_id or target_id deliberately. A cross-process iframe can use target_id from DOM.describeNode.frameId of its exact parent-page element; verify the frame origin and control before input. Listed high-risk methods are blocked before dispatch unless mode=lab AND operator env BROWSERTAP_ALLOW_UNSAFE_CDP=1; safe always blocks them (raw_cdp_blocked, delivery_state=undelivered, retry_safe=false, retryable=false). This guard prevents common destructive calls; allowed CDP/JavaScript can still change pages or profile state. Inspect state after uncertain delivery before retrying. |
| save_pdfA | Print a real-browser tab to a validated PDF file through bounded CDP. save_path is RELATIVE and resolves under ~/Downloads/browsertap; an absolute path or a '..' escape is refused. The file is written atomically only after valid non-empty PDF bytes are returned; a CDP timeout invalidates and detaches the debugger lease. |
| debugger_targetsA | List every CDP-attachable target, including service workers and extension background pages that list_tabs never shows. Works with no tabs open. |
| cdp_batchB | Raw CDP batches can cause side effects across multiple targets or the entire browser profile. Pass the full JSON object with cmd='batch'; child targets may inherit or override top-level tabId. Policy checks complete for the whole batch before dispatch. Listed high-risk methods require mode=lab AND operator env BROWSERTAP_ALLOW_UNSAFE_CDP=1; safe always blocks them (raw_cdp_blocked, delivery_state=undelivered, retry_safe=false, retryable=false). Allowed CDP/JavaScript can still change pages or profile state. Inspect state after uncertain delivery before retrying a batch. |
| page_clickA | Click a CSS/structured locator or viewport coordinates in a specific real browser tab using background CDP input. Coordinates are viewport-relative CSS pixels (the space getBoundingClientRect reports), NOT the device pixels capture_page_screenshot returns -- on a scaled display divide a screenshot pixel by devicePixelRatio first. Ambiguous or unreachable targets dispatch nothing; the tab is not activated and the desktop cursor does not move. Selector offsets are measured from the element's top-left corner; an omitted axis uses the element centre. In selector mode, duplicate CSS/structured matches are reduced to visible, interactable candidates so hidden modal templates do not win, then the point is hit-tested before anything is dispatched: an element below the fold is scrolled into view, and a point owned by another element returns status 'obscured' (with occluded_by) or 'outside_viewport' having clicked nothing. Coordinate mode is not hit-tested -- coordinates name a pixel, not an element. A selector click crossing a non-identity CSS-transformed iframe returns status 'unsupported_frame_transform' without dispatch; query/type paths remain available. A structured selector may use {'selector': '#pay', 'frame': [...]} as a CSS alias, or {'frame': [...], 'x': 20, 'y': 30} to click a point inside the final same-origin or cross-origin frame; frame-point mode is not hit-tested. Nested frame locators also support OOPIFs. Framed clicks do not scroll automatically and check every parent for obstruction. A binding invalidated during the call returns stale_frame; inspect input_dispatched before recovery and never replay partial or unknown input. |
| page_typeA | Insert text into the focused element or a CSS/structured-locator field in a specific tab using background CDP input; xterm containers automatically retarget their helper textarea. Optionally clear and submit a key. Missing, ambiguous, or unusable targets dispatch nothing. CSS/structured matches are reduced to visible, interactable candidates so hidden templates do not win; the resulting input event is trusted in the page. The result includes active_element and focus_confirmed so omitted-selector input is auditable. Nested frame locators support same-origin, cross-origin and OOPIF targets. A call retains the selected document and element, then checks the focus chain before each input. Navigation or replacement returns stale_frame; inspect input_dispatched and do not replay partial or unknown input. A new independent call can locate the new document. |
| page_pressA | Press a key or comma-delimited modifier chord in a specific tab using background CDP input, without activating the tab. |
| page_dragA | Drag between viewport coordinates in a specific tab using one background CDP input sequence, without activating the tab or moving the desktop cursor. Both endpoints are viewport-relative CSS pixels, like page_click's coordinate mode, and neither is hit-tested. |
| upload_filesA | Set files on a file input, which JS cannot do (input.files is read-only). Give a CSS selector for the and absolute local paths. Runs as a single CDP batch so the DOM node ids stay valid across the sequence. |
| get_cookiesC | Get cookies for the current page or specified tab via the Chrome extension bridge. |
| set_site_permissionA | Temporarily set an origin-scoped browser site permission for 60-600 seconds. Only http/https origins and notifications, geolocation/location, camera or microphone are supported; clipboard returns unsupported because its prior state cannot be restored. safe asks on every allow; lab skips prompts by default and restores session approval only when BROWSERTAP_LAB_NO_ELICIT is explicitly disabled. Leases attempt to restore their prior setting. requires_user_action.reason distinguishes elicitation_unsupported, declined, timeout, cancelled and error; an unsuccessful approval never sends a permission grant. If restoration becomes unsupported, manual_recovery retains that setting and recovery guidance without automatic retries; an explicit reset can retry after the cause is resolved. |
| reset_site_permissionsA | Attempt to restore matching temporary site-permission leases now, including manual_recovery records. Omit origin and permission to reset every lease for the selected browser; origin accepts only http/https. Unsupported restoration preserves the prior setting and recovery guidance as manual_recovery and stops automatic retries. Resolve that cause before another explicit reset. |
| set_cookiesA | Write cookies into the real browser profile. Takes one cookie object or a list (JSON text is accepted): name is required, plus optional value/url/domain/path/expires (Unix seconds)/httpOnly/secure/sameSite. Uses CDP Network.setCookie so HttpOnly and cross-path cookies work; falls back to document.cookie only if CDP is unavailable, and then says which cookies could not carry HttpOnly. Cookies with neither url nor domain are scoped to the current page. |
| delete_cookiesA | Delete a cookie by name from the real browser profile. Scope defaults to the current page (url), or pass domain/path/url to target another scope. Uses CDP Network.deleteCookies, falling back to expiring it via document.cookie. |
| storage_getA | Read localStorage or sessionStorage. Give a key for one value, or omit it to dump every key (values are truncated past ~20k chars and truncated is reported). area='local' (default) or 'session'. |
| storage_setA | Write one key into localStorage or sessionStorage and read it back to confirm. area='local' (default) or 'session'. Values are strings; non-string values are JSON-encoded first. |
| capture_page_screenshotA | Capture a viewport, full-page, or clipped screenshot of a page/tab via CDP with optional JPEG/WebP quality. Returns text metadata plus an attached MCP image even when save_path is set; save_path only controls disk output. image_width/image_height are DEVICE pixels (CSS x devicePixelRatio), not the CSS pixels page_click takes, and |
| inspect_native_file_dialogA | Explicit desktop capability: inspect the current foreground Windows native file dialog owned by a registered Chrome or Edge process. Requires desktop_opt_in=true and the desktop extra. Verifies the OS owner/process identity, standard Shell file controls and visible Cancel button, then installs a temporary per-ticket window identity marker. Returns a 15-second ticket for cancel_native_file_dialog plus desktop/on_screen/input_quiet diagnostics. This inspection has a temporary marker side effect; it does not activate a window. Use page/CDP tools for ordinary pages. Unsupported or unverifiable native layouts are refused. |
| cancel_native_file_dialogA | Explicit desktop capability: cancel the Windows file dialog identified by a fresh inspect_native_file_dialog ticket. Requires desktop_opt_in=true and follows the current safe/lab physical-approval policy. Each opted-in attempt consumes its ticket. After the physical-input lease, enforced quiet gate, identity, foreground and Cancel hit checks, sends one bounded message to that Cancel control without activating a window. Reports cancelled only after observing the dialog HWND gone; uncertain delivery or closure is unknown with retry_safe=false. Inspect state before another action. Returns desktop, on_screen and input_quiet diagnostics; unsupported native layouts are refused. Approval failures expose reason in the result and diagnostics: elicitation_unsupported, declined, timeout, cancelled or error. |
Prompts
Interactive templates invoked by user choice
| Name | Description |
|---|---|
No prompts | |
Resources
Contextual data attached and managed by the client
| Name | Description |
|---|---|
| agent_workflow | Packaged BTAP caller workflow: target selection, observation, input, verification and owned-tab cleanup. |
| agent_recovery | Packaged BTAP recovery guide: operation receipts, uncertain delivery, page refusals and bridge diagnostics. |
TDQS
Scored across 51 tools
Most tools target distinct capabilities (tab management, page input, cookies, storage, capture, CDP), and long descriptions clarify edge cases. Minor overlap exists between handle_dialog and resolve_leave_dialog, and between list_tabs/list_all_tabs/debugger_targets, but the descriptions draw clear boundaries.
The overwhelming majority follow a verb_noun snake_case pattern (open_url, list_tabs, delete_cookies, capture_page_screenshot), with parallel pairs like network_capture_start/network_capture_stop. A few noun-led names (extension_path, debugger_targets, cdp_command, cdp_batch, debugger_targets, cdp_batch) deviate slightly but remain predictable.
51 tools is well beyond the 25+ threshold and near the 50+ extreme, making the surface heavy for agents to navigate. The breadth is partly justified by the server's wide browser-automation scope, but many niche tools (extension management, native file dialogs, bookmarks) inflate the count.
The server covers nearly the full browser lifecycle: tabs, navigation, input, screenshots, cookies, storage, uploads, downloads, dialogs, permissions, network/console capture, and diagnostics, with no major dead ends in core workflows. Minor gaps like bookmark update and extension install are absent or explicitly unsupported, but workarounds exist via execute_js/cdp_command.