Browser MCP Tools

stygian-browser exposes browser automation tools including a runner-first acquisition path plus pool stats.


Enabling

[dependencies]
stygian-browser = { version = "*", features = ["mcp"] }

To run as a standalone MCP server:

cargo run --example mcp_server -p stygian-browser --features mcp

When using the aggregator, tools keep their browser_ prefix.


Session lifecycle

Browser sessions are identified by a session_id (ULID string). The lifecycle is:

browser_acquire → browser_navigate → browser_eval / browser_screenshot / browser_content → browser_release

Always call browser_release when done; unreleased sessions hold a browser process open against the pool's max limit.

Runner-first alternative:

browser_acquire_and_extract

Use this tool when you want acquisition strategy escalation and extraction in one call. The mode field accepts fast, resilient, hostile, and investigate.


Tools

browser_acquire

Acquire a browser session from the warm pool. Returns within ~100 ms for warm pools, ~2 s for cold launch.

ParameterTypeRequiredDescription
stealth_levelstringnone | basic | advanced (default: advanced)
tls_profilestringTLS fingerprint profile name — e.g. chrome131, firefox133, safari18, edge131
webrtc_policystringallow_all | disable_non_proxied | block_all (default from pool config)
cdp_fix_modestringCDP leak mitigation mode: addBinding | isolatedWorld | enableDisable | none
proxystringProxy URL for this session — e.g. http://user:pass@proxy:8080

Returns:

{
  "session_id": "01HV4...",
  "requested_metadata": {
    "stealth_level": "advanced",
    "tls_profile": "chrome131"
  }
}

browser_navigate

Navigate the browser to a URL and wait for the page to load.

ParameterTypeRequiredDescription
session_idstringSession ID from browser_acquire
urlstringTarget URL
timeout_secsintegerNavigation timeout in seconds (default: 30)

Returns:

{
  "title": "Example Domain",
  "url": "https://example.com"
}

browser_eval

Evaluate arbitrary JavaScript in the page context and return the result.

ParameterTypeRequiredDescription
session_idstringSession ID
scriptstringJavaScript expression to evaluate

Returns:

{ "result": 42 }

Example — extracting all links:

{
  "script": "Array.from(document.querySelectorAll('a')).map(a => a.href)"
}

browser_screenshot

Capture a full-page screenshot as a base64-encoded PNG.

ParameterTypeRequiredDescription
session_idstringSession ID

Returns:

{ "data": "iVBORw0KGgoAAAANSUhEUgAA..." }

The returned data field is a standard base64 PNG suitable for embedding in an <img> tag or writing directly to a .png file.


browser_content

Retrieve the current page's full outer HTML.

ParameterTypeRequiredDescription
session_idstringSession ID

Returns:

{ "html": "<!DOCTYPE html><html>...</html>" }

browser_attach (requires mcp-attach feature)

Attach an MCP session to an existing DevTools websocket endpoint (cdp_ws) or use the extension bridge contract path (extension_bridge, currently not implemented).

ParameterTypeRequiredDescription
modestringcdp_ws | extension_bridge
endpointstringRequired for cdp_ws; DevTools websocket URL
profile_hintstringOptional label for external profile identity
target_profilestringOptional tuning profile: default | reddit

cdp_ws returns a new MCP session_id that can be used with the normal browser lifecycle tools (browser_navigate, browser_eval, browser_content, browser_screenshot, browser_release).

Example (cdp_ws):

{
  "mode": "cdp_ws",
  "endpoint": "ws://127.0.0.1:9222/devtools/browser/abcd1234",
  "target_profile": "default"
}

browser_auth_session

High-level auth/session wrapper for common login workflows. This tool orchestrates browser_session_save and browser_session_restore, with optional post-step human-like interaction via browser_humanize.

ParameterTypeRequiredDescription
session_idstringSession ID
modestringcapture | resume
file_pathstringOptional snapshot file path
ttl_secsintegerOptional TTL in seconds when mode = capture
navigate_to_originbooleanWhen resuming, navigate to snapshot origin first (default: true)
interaction_levelstringOptional interaction pass: none | low | medium | high

Capture example:

{
  "session_id": "01HV4...",
  "mode": "capture",
  "file_path": "/tmp/reddit-session.json",
  "ttl_secs": 86400,
  "interaction_level": "none"
}

Resume example:

{
  "session_id": "01HV5...",
  "mode": "resume",
  "file_path": "/tmp/reddit-session.json",
  "navigate_to_origin": true
}

browser_session_save

Capture the current page auth/session state (cookies + localStorage) and store it in memory and optionally on disk.

ParameterTypeRequiredDescription
session_idstringSession ID
ttl_secsintegerOptional snapshot TTL
file_pathstringOptional output path for snapshot JSON
include_snapshotbooleanInclude full snapshot payload in response (default: false)

Returns:

{
  "session_id": "01HV4...",
  "origin": "https://example.com",
  "cookie_count": 5,
  "local_storage_keys": 3,
  "ttl_secs": 3600,
  "saved_to_file": "/tmp/session.json"
}

browser_session_restore

Restore session state from one of three sources: inline snapshot payload, snapshot file, or the in-memory snapshot saved previously for the same session.

ParameterTypeRequiredDescription
session_idstringSession ID
snapshotobjectInline SessionSnapshot JSON
file_pathstringPath to snapshot JSON file
use_savedbooleanUse in-memory saved snapshot if no inline/file source provided (default: true)
navigate_to_originbooleanNavigate to snapshot origin before applying state (default: true)

Returns:

{
  "session_id": "01HV4...",
  "source": "saved",
  "origin": "https://example.com",
  "cookie_count": 5,
  "local_storage_keys": 3,
  "snapshot_expired": false
}

browser_humanize

Run a human-like interaction sequence on the current page (scroll, mouse, key activity) to reduce robotic behavior patterns.

ParameterTypeRequiredDescription
session_idstringSession ID
levelstringnone | low | medium | high (default: low)
viewport_widthnumberViewport width used for interaction simulation (default: 1366)
viewport_heightnumberViewport height used for interaction simulation (default: 768)

Returns:

{
  "session_id": "01HV4...",
  "level": "low",
  "viewport_width": 1366,
  "viewport_height": 768,
  "applied": true
}

browser_query

Query all elements matching a CSS selector and return their text content (and optionally named attributes) as a structured list. Does not require deserialising the full page HTML.

ParameterTypeRequiredDescription
session_idstringSession ID from browser_acquire
urlstringURL to navigate to before querying
selectorstringCSS selector — e.g. "article.post h2"
fieldsobjectMap of { "name": "attr_name" } pairs — extra attribute values to include per node

Returns:

[
  { "text": "Post title", "href": "https://example.com/post-1" },
  { "text": "Another title", "href": "https://example.com/post-2" }
]

When fields is omitted, each item contains only "text" (the element's textContent).


browser_extract

Extract structured records from a page using a root selector + per-field schema. Equivalent to calling page.extract_all::<T>() with an inline schema definition.

ParameterTypeRequiredDescription
session_idstringSession ID
urlstringURL to navigate to before extracting
root_selectorstringCSS selector for the repeating container element
schemaobjectMap of field name to { selector, attr?, required? } field descriptor

Schema field descriptor:

KeyTypeRequiredDescription
selectorstringCSS selector scoped to the root element
attrstringIf present, captures this attribute instead of textContent
requiredbooleantrue (default) — omit or set to false for optional fields

Returns:

[
  {
    "title":  "Example post",
    "url":    "https://example.com/post-1",
    "author": "Alice",
    "date":   "2025-01-15"
  }
]

Example call:

{
  "session_id":    "01HV4...",
  "url":           "https://news.example.com",
  "root_selector": "article.story",
  "schema": {
    "title":  { "selector": "h2" },
    "url":    { "selector": "h2 a", "attr": "href" },
    "author": { "selector": "span.author" },
    "date":   { "selector": "time", "attr": "datetime", "required": false }
  }
}

browser_extract_with_fallback

Structured extraction with multiple root selectors. Selectors are tried in order, and the first selector that yields results is used.

ParameterTypeRequiredDescription
session_idstringSession ID
urlstringURL to navigate to before extracting
root_selectorsarray[string]Candidate root selectors in priority order
schemaobjectSame schema object used by browser_extract
timeout_secsnumberNavigation/extraction timeout (default: 30)

Returns: same structured results array as browser_extract, plus the selected root selector.


browser_extract_resilient

Structured extraction mode that tolerates partial records by skipping invalid root nodes instead of failing the full extraction call.

ParameterTypeRequiredDescription
session_idstringSession ID
urlstringURL to navigate to before extracting
root_selectorstringRoot selector for repeated record containers
schemaobjectSame schema object used by browser_extract
timeout_secsnumberNavigation/extraction timeout (default: 30)

Returns:

{
  "url": "https://news.example.com",
  "root_selector": "article.story",
  "count": 42,
  "skipped": 3,
  "results": [
    { "title": "...", "url": "..." }
  ]
}

browser_find_similar

Find elements that are structurally similar to a reference fingerprint, even when class names or depth differ across page versions. Uses a weighted Jaccard similarity score (tag 40 %, classes 35 %, attribute names 15 %, depth 10 %).

Note: Requires the similarity feature to be enabled on stygian-browser.

ParameterTypeRequiredDescription
session_idstringSession ID
urlstringURL to navigate to before searching
fingerprintobjectElementFingerprint JSON — capture with node.fingerprint() in the Rust API
thresholdnumberMinimum similarity score 0–1 (default: 0.7)
max_resultsintegerMaximum number of matches to return (default: 10)

Returns:

[
  { "score": 0.92, "outer_html": "<div class=\"post post-featured\">...</div>" },
  { "score": 0.81, "outer_html": "<div class=\"post\">...</div>" }
]

browser_verify_stealth

Run a full stealth diagnostic: navigate to a detection-test URL and return a structured report of all signals checked (WebDriver flag, CDP artefacts, navigator properties, WebRTC leaks, TLS fingerprint, etc.).

Note: Requires the stealth feature to be enabled on stygian-browser.

ParameterTypeRequiredDescription
session_idstringSession ID
urlstringURL to navigate to before running diagnostics (e.g. https://bot.sannysoft.com)
timeout_secsintegerNavigation timeout (default: 15)

Returns: A DiagnosticReport JSON object:

{
  "checks": [
    { "id": "WebDriverFlag", "passed": true, "details": "undefined" },
    { "id": "ChromeObject", "passed": true, "details": "present" },
    { "id": "PluginCount", "passed": true, "details": "5" },
    { "id": "LanguagesPresent", "passed": true, "details": "en-US,en" },
    { "id": "CanvasConsistency", "passed": true, "details": "data:image/png;..." },
    { "id": "WebGlVendor", "passed": true, "details": "Intel Inc. -- ANGLE" },
    { "id": "AutomationGlobals", "passed": true, "details": "none" },
    { "id": "OuterWindowSize", "passed": true, "details": "1920x1080" },
    { "id": "HeadlessUserAgent", "passed": true, "details": "Mozilla/5.0..." },
    { "id": "NotificationPermission", "passed": true, "details": "default" },
    { "id": "MatchMediaPresent", "passed": true, "details": "function" },
    { "id": "ElementFromPointPresent", "passed": true, "details": "function" },
    { "id": "RequestAnimationFramePresent", "passed": true, "details": "function" },
    { "id": "GetComputedStylePresent", "passed": true, "details": "function" },
    { "id": "CssSupportsPresent", "passed": true, "details": "function" },
    { "id": "SendBeaconPresent", "passed": true, "details": "function" },
    { "id": "ExecCommandPresent", "passed": true, "details": "function" },
    { "id": "NodeJsAbsent", "passed": true, "details": "absent" }
  ],
  "passed_count": 18,
  "failed_count": 0,
  "transport": null
}

browser_release

Release a browser session back to the pool.

ParameterTypeRequiredDescription
session_idstringSession ID to release

Returns:

{ "released": true }

pool_stats

Return current browser pool statistics. No parameters required.

Returns:

{
  "active":    2,
  "max":       8,
  "available": 6
}

browser_acquire_and_extract

Run the opinionated acquisition ladder and return extraction/content output from one call.

ParameterTypeRequiredDescription
urlstringTarget URL
modestringfast | resilient | hostile | investigate
wait_for_selectorstringOptional selector gate for browser-stage success
selector_waitstringAlias for wait_for_selector
extraction_jsstringOptional JavaScript extraction expression
total_timeout_secsnumberOptional wall-clock timeout for full run (must be > 0)
browserbase_enabledbooleanOptional Browserbase stage opt-in (requires stygian-browser feature browserbase)
use_browserbasebooleanAlias for browserbase_enabled

browserbase_enabled/use_browserbase require runtime environment variables BROWSERBASE_API_KEY and BROWSERBASE_PROJECT_ID.

Returns:

{
  "success": true,
  "strategy_used": "browser_light_stealth",
  "final_url": "https://example.com",
  "status_code": 200,
  "extracted": {
    "title": "Example Domain"
  },
  "html_excerpt": "<!doctype html><html>...",
  "diagnostics": {
    "attempted": ["direct_http", "browser_light_stealth"],
    "timed_out": false,
    "failure_count": 0,
    "failures": []
  }
}

Mode examples:

fast

{
  "name": "browser_acquire_and_extract",
  "arguments": {
    "url": "https://example.com",
    "mode": "fast"
  }
}

resilient

{
  "name": "browser_acquire_and_extract",
  "arguments": {
    "url": "https://example.com/catalog",
    "mode": "resilient",
    "wait_for_selector": "article.item"
  }
}

hostile

{
  "name": "browser_acquire_and_extract",
  "arguments": {
    "url": "https://example.com/challenge",
    "mode": "hostile",
    "wait_for_selector": "main",
    "total_timeout_secs": 60,
    "browserbase_enabled": true
  }
}

investigate

{
  "name": "browser_acquire_and_extract",
  "arguments": {
    "url": "https://example.com",
    "mode": "investigate",
    "extraction_js": "({ title: document.title, url: location.href })"
  }
}

Migration note:

  • Old low-level path: browser_acquire -> browser_navigate -> browser_eval/browser_extract -> browser_release.
  • New runner path: one browser_acquire_and_extract call with explicit mode and optional browserbase_enabled.

Live Attach Test (End-to-End)

Run a local Chrome with remote debugging enabled:

/Applications/Google\ Chrome.app/Contents/MacOS/Google\ Chrome \
  --remote-debugging-port=9222 \
  --user-data-dir=/tmp/stygian-attach-profile

Resolve the websocket endpoint and run the ignored attach integration test:

export STYGIAN_ATTACH_WS_ENDPOINT=$(curl -s http://127.0.0.1:9222/json/version | jq -r .webSocketDebuggerUrl)
cargo test -p stygian-browser \
  --features "mcp,mcp-attach" \
  --test mcp_integration \
  -- --ignored --nocapture

If STYGIAN_ATTACH_WS_ENDPOINT is not set, the live attach test is skipped.


Resources

The browser MCP exposes active sessions as MCP resources, readable via resources/read.

URI patternDescription
browser://session/{session_id}State of a specific browser session

Example resources/read request:

{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "resources/read",
  "params": { "uri": "browser://session/01HV4..." }
}

Returns:

{
  "uri": "browser://session/01HV4...",
  "mimeType": "application/json",
  "text": "{ \"session_id\": \"01HV4...\", \"config\": { \"stealth_level\": \"advanced\", \"proxy\": null }, \"pool_active\": 1, \"pool_max\": 8 }"
}