Skip to main content
Glama

Server Configuration

Describes the environment variables required to run the server.

NameRequiredDescriptionDefault
UMBRA_API_KEYSNoComma-separated list of API keys for HTTP transport authentication (optional for stdio).
UMBRA_NO_CLOAKNoSet to '1' to disable CloakBrowser and use stock Chromium.0
UMBRA_CONTAINERNoSet to '1' to indicate containerized environment (recommended in MCP stdio setup).1

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

CapabilityDetails
tools
{
  "listChanged": true
}
logging
{}
prompts
{
  "listChanged": true
}
resources
{
  "subscribe": false,
  "listChanged": true
}

Tools

Functions exposed to the LLM to take actions

NameDescription
spawnA

Open stealth tab. browser_id='alice'=isolated Chrome (own cookies/identity, ~1.5s boot). For same-identity new pages prefer navigate (cheaper). stealth_mode='minimal' (default)=mimics vanilla Chrome, 'full'=adds anti-tracking noise.

Proxy: pass proxy='http://user:pass@host:port' for one-off, OR set use_proxy_pool=True (after proxy_pool_load) to pick from the pool — optionally filter by proxy_country='US' / proxy_tag='residential'. Pool wins over proxy if both set. Auth wired via CDP (Chrome flag strips inline auth).

Ex: spawn('https://news.ycombinator.com') → {"tab_id":"t0","browser_id":"default","url":"..."} Ex: spawn('about:blank', browser_id='alice', proxy='http://1.2.3.4:8080') Ex: spawn(use_proxy_pool=True, proxy_country='US', browser_id='scraper-1')

chromium: 'cloak' (default) auto-downloads CloakBrowser's patched chromium (C++ fingerprint patches — beats JS shims). 'stock'=system chromium. Pass an absolute path to use a custom binary. Env: UMBRA_NO_CLOAK=1 forces stock; UMBRA_CLOAK_BINARY= uses that as cloak. See cloak_status.

proxy_pool_loadA

Bulk-load proxies into the pool.

format='lines': one URL per line (http://user:pass@host:port[#country=US,tags=a|b]) format='json': list of {url, username?, password?, country?, tags?, session_template?} OR full pool dict from proxy_pool_export. format='csv': header w/ columns url, username, password, country, tags, session_template data may be inline text OR a file path (auto-detected by existence).

rotation strategies: round_robin (default), random, least_used, best_health, sticky_browser (same browser_id always gets same entry).

Ex: proxy_pool_load(data='http://u:p@gw1:8080\nhttp://u:p@gw2:8080') Ex: proxy_pool_load(data='/path/to/proxies.csv', format='csv')

proxy_pool_addB

Add ONE proxy to the pool.

url may include inline auth (http://user:pass@host:port); explicit username/password override. session_template is a provider-specific sticky-session pattern, e.g. 'user-session-{sid}-country-{cc}'.

Ex: proxy_pool_add('http://gw.proxy.com:8080', username='u123', password='p', country='US')

proxy_pool_removeA

Remove one entry by id. Use proxy_pool_list to see ids.

Ex: proxy_pool_remove('a1b2c3d4') → {"removed":true,"total":4}

proxy_pool_clearA

Drop ALL entries. Stickies cleared. Rotation strategy preserved.

Ex: proxy_pool_clear() → {"cleared":12}

proxy_pool_listB

List all entries (creds redacted by default). Columnar.

Ex: proxy_pool_list() → {"rotation":"round_robin","entries":[...]}

proxy_pool_health_checkA

Probe every entry via tls_fetch-through-proxy + report rolling health.

NB: tls_fetch uses curl_cffi which only honors HTTP/HTTPS proxies, not SOCKS5. SOCKS5 entries get a single connect-test instead.

Ex: proxy_pool_health_check() → {"checked":12,"alive":10,"results":[...]}

proxy_pool_exportA

Dump pool state — round-trippable via proxy_pool_load(format='json'). redact=False (default) emits real creds; redact=True for safe sharing.

Ex: proxy_pool_export() → {"rotation":"...","entries":[{...creds...}]}

closeA

Close a tab. Browser stays for other tabs. Free RAM after one-off tasks.

Ex: close('t0') → {"closed":true}

list_browsersA

List browser instances + tab counts. Audit parallel sessions.

Ex: list_browsers() → {"browsers":[{"browser_id":"default","tab_count":2,"tab_ids":["t0","t1"]}]}

kill_allA

Nuke ALL browsers/tabs/handoffs/hooks. Last resort — prefer close/close_browser.

Ex: kill_all() → {"browsers":3,"tabs":7,"handoffs":1}

close_browserB

Close all tabs in a browser + stop the Chrome process. Use after dedicated isolated session.

Ex: close_browser('alice') → {"closed_browser":"alice","closed_tabs":["t3","t4"]}

list_tabsA

List open tabs w/ URL+title+browser_id. Cheap.

Ex: list_tabs() → {"tabs":[{"id":"t0","browser":"default","url":"https://...","title":"..."}]}

switch_tabA

Focus a tab. Needed for visual capture; other tools work on background tabs.

Ex: switch_tab('t1') → {"focused":"t1"}

navigateA

Navigate tab. Stealth payload + cookies persist. Cheaper than spawn — default reflex.

timeout_s: hard cap on load (default 30s). Raises on timeout (tab survives, retry/abort). Ex: navigate('t0', 'https://example.com/login') → {"url":"..."}

backB

History.back(). When: undo a nav step (e.g. clicked wrong link, retry from prev page).

forwardB

History.forward(). When: redo a nav step after back. Rare — usually you just re-navigate.

reloadA

Reload page. hard=True bypasses cache (CDN debug, fresh build).

Ex: reload('t0') → {"reloaded":true}

aria_snapshotA

ARIA tree of interactive elements w/ idx for click/type. ~50ms. Call BEFORE first interaction + after any DOM change (idx invalidates). Repeats group as [12-77] cycle×13: link('A'),link('B') (lossless — click any idx in range).

Ex: aria_snapshot('t0') → {"tree":"[0] button "Sign in"\n[1] textbox "email"\n...","count":12}

aria_clickA

Click ARIA element by idx (zero mouse, semantic). Always prefer over click_at. Get idx from aria_snapshot or find_by_text.

Ex: aria_click('t0', 3) → {"ok":true}

aria_typeA

Type into ARIA input by idx. humanize=True (default)=log-normal keystroke + pair-classification (~80-150ms/char, defeats cadence detectors). False=instant CDP keys (detectable). For multi-field use fill_form; for big paste use paste_text.

Ex: aria_type('t0', 1, 'me@example.com') → {"ok":true}

find_by_textA

Fuzzy "the button/link that says X" → ARIA idx, in one call. Skip aria_snapshot. role_hint='button'|'link'|'textbox' to disambiguate.

Ex: find_by_text('t0', 'Sign in', role_hint='button') → {"idx":3,"found":true}

fill_formA

Fill N inputs from {label:value} in one call. Labels fuzzy-match placeholder/ aria-label/. Beats N aria_type round-trips.

Ex: fill_form('t0', {'email':'a@b.c','password':'hunter2'}) → {"filled":["email","password"],"missed":[]}

current_stateA

Cheap orientation: URL+title+h1/h2+forms+interactive_count. Lighter than aria_snapshot. Good first move after navigate. Dedups identical state.

Ex: current_state('t0') → {"url":"...","title":"...","h1_h2":[...],"forms":[...],"interactive_count":42}

click_atA

Pixel click for captcha/canvas/PDF where ARIA misses. Coords from screenshot/ inspect_element rect. Default to aria_click — pixel is fragile.

Ex: click_at('t0', 320, 480) → {"ok":true}

press_keyA

Press key (Enter/Esc/Tab) + optional modifiers ['ctrl','shift','alt','meta']. Form submit, dismiss modal, Ctrl+K palette.

Ex: press_key('t0', 'Enter') / press_key('t0', 'k', modifiers=['ctrl'])

scrollA

Scroll by (dx,dy), to_bottom=True, or scrollIntoView selector. Lazy-load / reveal.

Ex: scroll('t0', to_bottom=True) / scroll('t0', selector='#footer') → {"ok":true}

paste_textA

Instant paste via CDP (no humanize, ~1000x faster). For big blobs on trusted pages. Skip on hostile sites (cadence-detectable).

Ex: paste_text('t0', long_blob) → {"ok":true,"len":1234}

hoverB

Hover at (x,y) — reveals :hover menus, tooltips, hover-to-show buttons.

Ex: hover('t0', 200, 100) → {"ok":true}

select_optionA

Set HTML .value + change/input events. For custom dropdowns use aria_click.

Ex: select_option('t0', '#country', 'BR') → {"ok":true}

wait_forA

Wait — one of: selector / url_contains / network_idle_ms. For text use wait_for_text.

Ex (DOM): wait_for('t0', selector='.submit-btn') → {"ok":true,"why":"selector_found"} Ex (URL): wait_for('t0', url_contains='/dashboard') → {"ok":true,"why":"url_match","url":"..."} Ex (network): wait_for('t0', network_idle_ms=500) → {"ok":true,"why":"network_idle"}

extract_textA

innerText of selector. pierce walks shadow+same-origin iframes. denoise (default) strips zero-width chars + collapses 3+ blank lines lossless. For full articles prefer extract_markdown.

Ex: extract_text('t0','.article',2000) → {"_untrusted":true,"text":"Body…"} Ex: extract_text('t0','body',8000,denoise=False) → byte-exact

extract_linksB

Visible links, columnar + URL footnoting (_refs table dedups host across rows; full URL = _refs[ref] + path). same_origin=True to skip outbound.

Ex: extract_links('t0', same_origin=True) → {"_untrusted":true,"links":[...],"_refs":{"1":"https://x.com"}} where rows=[text,ref,path]

grep_textB

Regex-hunt page text. Cheaper than extract_text+scan. Returns [{match,context,line}].

Ex: grep_text('t0', 'API_KEY=\w+') → {"_untrusted":true,"matches":[{"match":"API_KEY=abc","context":"...","line":42}]}

dom_queryB

querySelectorAll → element list w/ attrs (href/data-*/src/rect). pierce walks shadow + iframes. Columnar-compressed.

Ex: dom_query('t0', 'a.btn', 5) → {"_untrusted":true,"elements":{"_columnar":true,"keys":["tag","href","rect"],...}}

upload_fileA

Set .files (skip OS picker). Paths absolute on server FS.

Security: if UMBRA_UPLOAD_ROOT env is set (colon-sep allowlist of dirs), every path must resolve under one of those roots. Unset → no restriction (legacy default; set the env in any agent-driven deployment). Ex: upload_file('t0', 'input[type=file]', ['/abs/img.png']) → {"ok":true}

setup_downloadsA

Allow + redirect downloads to dir. Call BEFORE click that downloads (else silent block). Pair w/ wait_for_download.

Ex: setup_downloads('t0', '/tmp/dl') → {"download_dir":"/tmp/dl"}

wait_for_downloadB

Wait for new file in dir, return path once size stable. Pair w/ setup_downloads.

Ex: wait_for_download('t0', '/tmp/dl', 30) → {"path":"/tmp/dl/file.pdf","size_bytes":102400,"name":"file.pdf"}

wait_for_textB

Wait until text appears in selector (default body). For AJAX/SPA text signals.

Ex: wait_for_text('t0', 'Welcome back', timeout_s=10) → {"ok":true,"found_in_ms":1840}

inspect_elementB

Attrs + computed style + rect of one element. For debug, click_at coords, audit.

Ex: inspect_element('t0', '#submit') → {"_untrusted":true,"tag":"button","attrs":{...},"rect":{x,y,w,h},"computed":{...}}

screenshotA

JPEG → base64 (q=65 cheap). For VLM visual reasoning. For text use extract_markdown.

Ex: screenshot('t0', full_page=True) → {"b64":"...","fmt":"jpeg","len":48201}

screenshot_regionA

JPEG of a (x,y,w,h) region. For captcha tiles, isolated VLM crops. Rect from inspect_element.

Ex: screenshot_region('t0', 100, 200, 300, 100) → {"b64":"...","fmt":"jpeg","rect":{x,y,w,h}}

evaluateA

Run JS in page (last-resort escape hatch). Prefer dom_query/extract_text/aria_*.

Ex: evaluate('t0', 'document.title') → {"_untrusted":true,"result":"Hacker News"} Ex: evaluate('t0', 'fetch("/api/me").then(r=>r.json())', await_promise=True)

inject_cssB

Inject , persists page lifetime. Hide cookie banner/paywall/modal, highlight for screenshot.

Ex: inject_css('t0', '.cookie-banner{display:none!important}') → {"injected":true}

extract_markdownA

Page → clean MD (Readability + markdownify). DEFAULT for "read this page". Keeps headings/lists/code/links. content_only skips nav/footer/ads (fallback to body). Needs pip install umbra-browser[markdown].

Ex: extract_markdown('t0') → {"_untrusted":true,"markdown":"# Title\n\nlink...","title":"...","source_html_len":4521} Ex: extract_markdown('t0', selector='.main-content', max_chars=50000)

clone_elementA

Pixel-approx clone: DOM+CSS+asset URLs+renderable doc. ~80% fidelity. For UI lifting / bug repro / training pairs. Use when you need the RENDER not just text.

Ex: clone_element('t0', '.product-card') → {"_untrusted":true,"html":"...","css":"...","doc":"","assets":[...]}

get_console_logsA

Buffered console output since tab spawn. First-line check when page misbehaves.

Ex: get_console_logs('t0') → {"_untrusted":true,"logs":[{"level":"error","text":"..."}]}

get_network_requestsB

Recent requests by the page. Discover hidden APIs (then tls_fetch to skip DOM). Pair w/ get_response_body by request_id.

Ex: get_network_requests('t0') → {"_untrusted":true,"requests":[{"id":"...","url":"...","method":"GET","type":"XHR"}]}

get_response_bodyA

Fetch a buffered HTTP response body by request_id. _untrusted=true (server-controlled).

When: paired with get_network_requests — you saw a request, now you want its actual JSON/HTML payload (to extract data the page never rendered, or debug a 4xx/5xx response).

memory_metricsB

Performance counters: JS heap size, DOM nodes, layout count, etc.

When: profiling a slow page, hunting a memory leak across a long automation run, or deciding whether to close a tab vs reuse it.

clear_cookiesA

Wipe ALL cookies. Returns count cleared.

When: testing logged-out flow, simulating a fresh visitor without spawning a new browser, or recovering from a stuck auth state. Note: also wipes OAuth/SSO state — re-login required after.

block_urlsB

Block URL patterns (wildcards). [] to clear. Speed up + skip trackers/heavy assets.

Ex: block_urls('t0', ['googletag', '*.gif']) → {"blocked_patterns":2}

set_extra_headersB

Inject headers into every outgoing request. Auth, A/B flags, tracing.

Ex: set_extra_headers('t0', {'X-Custom':'v', 'Authorization':'Bearer ...'}) → {"set":["X-Custom","Authorization"]}

set_viewportB

Change viewport size mid-session. mobile=True triggers touch events + UA. For mobile UI / breakpoint reveal.

Ex: set_viewport('t0', 375, 667, mobile=True) → {"width":375,"height":667}

dragA

Humanized bezier-jitter mouse drag. Slider captchas, drag-drop UI, range sliders.

Ex: drag('t0', 100, 200, 400, 200) → {"ok":true}

dynamic_hookA

Network rule (legacy thin wrapper around route_add). action='block'/'fulfill'/'continue'. url_pattern=substring match. Tab-scoped. For richer matching/modify/HAR use route_add.

Ex: dynamic_hook('t0', '/api/items', 'fulfill', new_status=200, new_body='{"items":[]}')

route_addB

Install rich interception rule. action: block|fulfill|continue|modify|tee|redirect.

MATCH (AND of any provided): url_pattern substring (cheap, default) url_regex re.fullmatch method GET/POST/... resource_type Document/XHR/Fetch/Script/Stylesheet/Image/Font/Media/... header_match {header_lower: regex} — re.search per header status_min/max response-stage filter (forces response-stage interception)

route_add_manyA

Bulk-install rules in one round-trip. Each item is a dict matching route_add kwargs (must contain at least action). Returns list of installed rule ids.

route_block_setA

Toggle the engine's inherited blocking. trackers: enable/disable the bundled 3520-domain tracker blocklist (yoyo). resource_types: replace the blocked resource-type set (e.g. ['Image','Media','Font'] for fast scraping). Pass nothing to just inspect current state.

Ex: route_block_set('t0', trackers=False) → {"trackers":false,"resource_types":[]} Ex: route_block_set('t0', resource_types=['Image','Font','Media']) Ex: route_block_set('t0') → {"trackers":true,"resource_types":[],"tracker_blocks":42}

route_set_enabledA

Pause or resume a rule without removing it. Preserves hits/captures.

Ex: route_set_enabled('t0','r2',False) → {"id":"r2","enabled":false,"hits":12}

route_capturesA

Return per-rule capture buffer (filled when rule has capture=N). Each entry: {ts, url, method, status, request_headers, body, body_encoding?, error?}. Use clear=True to drain after read.

Ex: route_captures('t0','r2') → {"_untrusted":true,"id":"r2","captures":[...]}

route_removeA

Remove one rule (rule_id) or all rules (all=True). Doesn't disable Fetch domain.

Ex: route_remove('t0', 'r2') → {"removed":"r2","active_routes":3} Ex: route_remove('t0', all=True) → {"cleared":5,"active_routes":0}

route_listB

List active rules + per-rule hit counts.

Ex: route_list('t0') → {"routes":[{"id":"r0","action":"block","hits":12,...}]}

har_record_startA

Begin buffering every paused req+resp into a HAR-1.2 archive (tab-scoped). Body capture is best-effort (skipped for fulfilled/blocked requests). Forces response-stage interception to capture status+body.

Ex: har_record_start('t0') → {"recording":true,"existing_entries":0}

har_record_stopA

Stop recording (keeps buffered entries — call har_dump to retrieve, har_clear to drop).

Ex: har_record_stop('t0') → {"recording":false,"entries":42}

har_dumpA

Return recorded HAR. With path, write full JSON to disk (no truncation, no _compact) and return summary. Without path, returns the HAR inline through _compact w/ tunable max_str (per body) and max_list / max_entries (entry count).

For full-fidelity inline dump, raise both caps OR write to disk. Disk write is byte-exact.

Ex: har_dump('t0') → {"_untrusted":true,"har":{...},"entries":42} Ex: har_dump('t0', path='/tmp/session.har', clear=True) → {"wrote":"/tmp/session.har","bytes":98231,"entries":42,"cleared":true} Ex: har_dump('t0', max_str=50000) # don't truncate JSON bodies up to 50KB

har_replay_loadA

Load a HAR file (path) or raw JSON string (har_json) into the replay corpus. Subsequent matching requests (method+url, or url-only when loose=True) are fulfilled from the HAR entry instead of hitting the network. Replay is fallthrough — it only fires on requests no route_add rule already matched.

Ex: har_replay_load('t0', path='/tmp/session.har') → {"loaded":42,"loose":false} Ex: har_replay_load('t0', har_json='{"log":{"entries":[...]}}', loose=True) → {"loaded":N,"loose":true,"total_loaded":N}

har_clearB

Drop buffered HAR entries (recording=True, default) and/or the replay corpus.

Ex: har_clear('t0') → {"cleared_entries":42} Ex: har_clear('t0', recording=False, replay=True) → {"cleared_replay":120}

clear_logsB

Wipe console+network buffers. For clean post-action trace.

Ex: clear_logs('t0') → {"cleared":true}

get_cookiesB

All cookies in tab context. For session debug, audit tracking.

Ex: get_cookies('t0') → {"_untrusted":true,"cookies":[{"name":"...","value":"...","domain":"..."}]}

set_cookiesB

Set cookies (each: name/value/domain required). For ad-hoc auth injection.

Ex: set_cookies('t0', [{'name':'sid','value':'abc','domain':'.x.com','secure':True}]) → {"set":1}

check_detectionA

Stealth self-test: sannysoft (~7s) + creepjs if deep (~40s). Before sensitive nav.

Ex: check_detection() → {"sannysoft":{"passed":31,"failed":0,...},"verdict":"good"} Ex: check_detection(deep=True) → {...,"creepjs":{"detected_headless":0,"stealth":0},"verdict":"good"}

warm_sessionA

Pre-warm w/ plausible browsing (profile: general/shopping/news/minimal). Before bot-detection-heavy targets so they don't see 0-history → checkout. ~10-30s.

Ex: warm_session('shopping') → {"warmed":true,"profile":"shopping"}

rotate_fingerprintA

Re-seed canvas/audio/WebGL noise mid-session. Needs stealth_mode='full' at spawn.

Ex: rotate_fingerprint('t0') → {"new_seed":2937184832}

handoff_startA

Spin up live remote-view URL (192-bit auth token in path). For captcha/2FA/manual steps. tunnel=True (default) → public via cloudflared Quick Tunnel; falls back to 127.0.0.1. Use start/wait PAIR to announce URL before blocking. fps 3 default (8-12 local).

Ex: handoff_start('t0', 'solve recaptcha') → {"url":"https://abc.trycloudflare.com/h-XYZ/","reason":"...","next":"tell user to open URL, then call handoff_wait"}

handoff_waitB

Block until user clicks I'M DONE (or timeout). Pair with handoff_start.

Ex: handoff_wait('t0', 120) → {"completed":true,"current_url":"...","current_title":"..."}

request_user_inputA

One-shot handoff: spin up + block. URL is in return. Use only if you don't need to announce URL early; otherwise prefer handoff_start + handoff_wait pair.

Ex: request_user_input('t0', 'solve captcha', timeout_s=300) → {"url":"...","completed":true,"current_url":"..."}

tls_fetchA

Raw HTTP w/ Chrome JA3+JA4. Skip DOM when you only need JSON/HTML. ~50ms vs ~500ms. Skip on client-side-rendered SPAs. Needs pip install umbra-browser[tls].

Ex: tls_fetch('https://api.example.com/users') → {"status":200,"headers":{...},"body":"{"users":[...]}"}

session_saveA

Save cookies+localStorage encrypted (Fernet+PBKDF2-200k). Per-(domain,name). Run AFTER login on target tab. Reuse via session_load.

Ex: session_save('t0', 'github-me', 'hunter2') → {"path":"~/.local/share/umbra/sessions/github.com/github-me.fern"}

session_loadA

Decrypt + inject saved cookies/localStorage. Skips login. Load BEFORE auth-walled nav.

Ex: session_load('t0', 'github-me', 'hunter2') → {"name":"github-me","cookies":12,"localStorage_keys":4,"origin":"https://github.com"}

session_listA

List all saved session blobs (metadata, no passphrase needed).

Ex: session_list() → {"sessions":[{"name":"github-me","domain":"github.com","saved_at":...,"size":1234}]}

session_deleteB

Delete a saved session blob by name.

Ex: session_delete('github-me') → {"deleted":true}

set_verbosityA

Toggle response minification. 'compact'=default (drops nones, columnar, trunc). 'full'=raw bytes, no trunc. Flip 'full' for byte-exact ops, then back.

Ex: set_verbosity('full') → {"prev":"compact","now":"full"}

batchC

Run N tools in one round-trip, serial. Saves MCP framing + composes w/ dedup.

cleanup_staleA

Manually reap tabs idle ≥ idle_seconds + browsers with no remaining tabs.

Ex: cleanup_stale(idle_seconds=300) → {"closed_tabs":["t2"],"closed_browsers":[]}

cloak_statusA

Report CloakBrowser install state. No network.

Returns {platform, supported, kill_switch, env_binary, cache_root, installed[]}. installed lists cached release tags + entry-point paths.

cloak_installA

Download + verify the latest (or pinned) CloakBrowser chromium build.

Idempotent — already-cached installs return their path. Pass force=True to re-download, or tag='<release-tag>' to pin a specific version. Runs sha256 verification before extraction. On unsupported platforms returns {ok:false, error:...} instead of raising.

Ex: cloak_install() → {"ok":true,"path":"/home/u/.umbra/cloak/.../chrome","tag":"..."}

Prompts

Interactive templates invoked by user choice

NameDescription

No prompts

Resources

Contextual data attached and managed by the client

NameDescription

No resources

TDQS

B3.2/5.0

Scored across 87 tools

Disambiguation3/5

Most tools have distinct purposes, but there are notable overlaps: aria_click vs click_at, aria_type vs paste_text vs fill_form, extract_text vs extract_markdown vs dom_query vs grep_text, dynamic_hook vs route_add, handoff_start+handoff_wait vs request_user_input, and close vs close_browser vs kill_all vs cleanup_stale. The descriptions usually state which tool to prefer, but an agent still has to internalize many nuanced precedence rules.

Naming Consistency4/5

The surface is overwhelmingly snake_case with recognizable domain prefixes (proxy_pool_*, route_*, har_*, session_*, aria_*, extract_*, wait_for_*). Minor deviations exist, such as bare verb names (spawn, close, back, forward), noun-ish names (current_state, cloak_status), and the legacy dynamic_hook, but the convention remains readable and predictable.

Tool Count1/5

With 87 tools, the server is far beyond a well-scoped set and includes many niche capabilities that could be consolidated or made optional. The count creates a large discovery and selection burden even though the individual tools are mostly meaningful.

Completeness5/5

The tool surface covers browser lifecycle, navigation, interaction, extraction, stealth/fingerprinting, proxy pools, request routing, HAR recording/replay, cookies/sessions, downloads, uploads, handoff, batching, and cleanup. For a browser-automation server, there are no obvious core gaps or dead ends.

Maintenance

ActivityInactive
ResponsivenessOutdated data