browserless-mcp
OfficialThe server exposes a suite of Browserless.io web automation tools to LLM clients: scrape, search, crawl, map, audit, run custom browser code, drive a persistent agent, and inspect account/session data.
browserless_smartscraper – scrape a single page to markdown/HTML/text/links/screenshots/PDF with auto anti-bot handling.
browserless_search – web/news/image search with geo/time filters, optionally scrape each result.
browserless_map – discover all URLs on a site via sitemaps and link extraction.
browserless_crawl – crawl a site and scrape every page with depth/path controls and sitemap strategy.
browserless_performance – run Lighthouse audits for accessibility, best practices, performance, PWA, SEO, with budgets.
browserless_function – execute custom Puppeteer JS, return text or binary (image/audio/PDF) responses.
browserless_export – export a URL in native format (HTML/PDF/image), optionally bundle resources into a ZIP.
browserless_agent – drive a persistent browser session via a ReAct loop: snapshot, plan, batch commands, multi-tab, proxies, personas, file upload/download.
browserless_skill – load on-demand recipes for tricky mechanics (shadow DOM, cookies, modals, captchas, etc.).
browserless_profiles – list saved authentication profiles (cookies/storage) to reuse logged-in state.
browserless_account – read account plan, unit balance, billing period, API key names.
browserless_usage – view request/unit consumption metrics.
browserless_sessions – inspect active/persistent/replayed sessions, download replays as rrweb players.
browserless_logs – read request logs for diagnosing failures, filtered by time/endpoint/outcome.
Additionally, the server exposes MCP resources (browserless://api-docs, browserless://status) and prompts (scrape-url, extract-content).
Runs Lighthouse audits on any URL to access scores and metrics for accessibility, best practices, performance, PWA, and SEO.
Executes custom Puppeteer JavaScript on the Browserless cloud, enabling programmatic browser automation via Puppeteer scripts.
Browserless MCP Server
MCP (Model Context Protocol) server for Browserless.io — expose the Browserless smart scraper API to LLM clients like Claude Desktop, Cursor, VS Code, and Windsurf.
Quick Start
Get an API token from browserless.io (free tier available), then point your MCP client at the hosted server:
{
"mcpServers": {
"browserless": {
"url": "https://mcp.browserless.io/mcp?token=your-token-here"
}
}
}No local install — see Configuration for per-client snippets.
Related MCP server: Pinchtab MCP Wrapper
Tools
Tool | Description |
| Scrape a single webpage and return its content as markdown or HTML. Handles JavaScript-heavy pages and anti-bot measures automatically. For content across multiple pages, use |
| Search the web using Browserless and optionally scrape each result. Supports web, news, and image search with geo-targeting and time filters. |
| Discover and map all URLs on a website. Scans via sitemaps and link extraction. Returns URLs with optional titles and descriptions. Useful for site audits and content discovery. |
| Crawl a website and scrape every discovered page. Supports depth control, path filtering, sitemap strategies, and configurable scrape options. Returns scraped content and metadata for each page. |
| Run Lighthouse audits on any URL. Returns scores and metrics for accessibility, best practices, performance, PWA, and SEO. Optionally filter by category or supply performance budgets. |
| Execute custom Puppeteer JavaScript on the Browserless cloud. The function receives a |
| Export a webpage via the Browserless |
| Drive a persistent browser session via a ReAct loop: snapshot the page, plan, batch interactions (click, type, scroll, evaluate, etc.), and re-snapshot. Uses ref-based selectors derived from snapshots, supports multi-tab workflows, screenshots, captcha solving, live URLs, and file upload/download (captured downloads auto-surface as handles; bytes never enter context). |
| Load an on-demand recipe for a non-trivial page mechanic (shadow DOM, cookie consent, modals, captchas, dynamic content, snapshot misses, screenshots, tabs). Companion to |
| List the authentication profiles saved for the current token, with cookie and origin counts. Pass a profile's name as |
| Read the account behind the current token: plan, unit balance, billing period, and API key names. Never returns API token values. |
| Read request and unit consumption: successes, errors, timeouts, queueing, peak concurrency, captchas, proxy bytes and units. Optionally scoped to specific API keys. |
| Inspect the account's sessions — browsers running now, persistent sessions on dedicated workers, recorded session replays, and 1Password credential integrations. Also downloads a replay as a fully self-contained rrweb player page ( |
| Read Browserless's own record of recent requests: what was attempted, whether it failed, why it stopped, how long it took and what it cost. The tool for diagnosing a run that failed on the Browserless side. Available window is plan-dependent. |
Skills
The server ships with a built-in library of Skills — on-demand recipes the agent can load to handle tricky page mechanics. Skills auto-inject into browserless_agent responses when their triggers fire (e.g. the agent hits a cookie banner), and can also be loaded manually via the browserless_skill tool.
Skill | Source | Purpose |
| Deep selectors and iframe targeting through shadow roots. | |
| Vendor-specific dismiss recipes (OneTrust, Cookiebot, Didomi, TrustArc, etc.). | |
| Closing dialogs, alertdialogs, and overlay close-button heuristics. | |
| Using the | |
| Choosing the right | |
| Handling truncated/empty snapshots and image-rendered content. | |
| When to screenshot vs. snapshot, scope and format choices. | |
| Multi-tab workflows and peek-without-switching via |
Load a skill explicitly:
{
"method": "tools/call",
"params": {
"name": "browserless_skill",
"arguments": { "id": "cookie-consent" },
},
}Built-in proxy (browserless_agent)
Pass a top-level proxy object on browserless_agent to route the session through datacenter or residential IPs. Datacenter is cheaper per MB; residential is less likely to be blocked.
{
"method": "tools/call",
"params": {
"name": "browserless_agent",
"arguments": {
"method": "goto",
"params": { "url": "https://example.com" },
"proxy": {
"proxy": "residential",
"proxyCountry": "us",
"proxySticky": true,
},
},
},
}Field | Notes |
|
|
| ISO-2 country code ( |
| US state name with whitespace replaced by underscores ( |
| City target. Paid/enterprise plan gated — non-eligible tokens get a 401. |
| Stable IP while the underlying WebSocket stays open. Reconnects (idle drop, network blip, browser crash) allocate a new sticky id and new IP. |
| Match |
| Residential-only named preset (e.g. |
| Bring-your-own upstream, e.g. |
Note: Geo, sticky, and locale options require either a built-in
proxytier orexternalProxyServer;proxyPresetrequiresproxy: "residential". The MCP rejects unsupported combinations instead of letting the API silently ignore them.
The proxy object is read once at session creation. To change it, call close and start a new session — the agent client keys sessions on the proxy fingerprint, so passing a different config will land on a fresh WebSocket.
OS persona (browserless_agent)
Agent sessions can opt into a coherent OS persona with top-level creation options:
Field | Notes |
|
|
| Android device slug; used only with |
| Desktop screen in |
| Desktop device pixel ratio: |
| Non-negative stable desktop-device slot; the server validates the account-specific range. |
Set persona options on the first call before navigation and reuse the returned
sessionId afterward. Persona is fixed for the life of that browser session;
close it before selecting a different persona.
Configuration
The server is hosted at https://mcp.browserless.io/mcp. Authenticate via headers (preferred) or a ?token= query parameter.
Installing via an AI agent? See install.md for agent-readable setup instructions.
Using headers (recommended for clients that support them):
{
"mcpServers": {
"browserless": {
"url": "https://mcp.browserless.io/mcp",
"headers": {
"Authorization": "Bearer your-token-here"
}
}
}
}Using URL query parameters (for clients like Claude.ai custom connectors that only accept a URL):
https://mcp.browserless.io/mcp?token=your-token-hereTo connect to a specific Browserless regional endpoint, add the x-browserless-api-url header or the browserlessUrl query parameter:
{
"mcpServers": {
"browserless": {
"url": "https://mcp.browserless.io/mcp",
"headers": {
"Authorization": "Bearer your-token-here",
"x-browserless-api-url": "https://production-lon.browserless.io"
}
}
}
}https://mcp.browserless.io/mcp?token=your-token-here&browserlessUrl=https://production-lon.browserless.ioWhen both headers and query parameters are present, headers take precedence.
API URL overrides are limited to browserless.io, its subdomains, the configured BROWSERLESS_API_URL origin (same scheme, hostname, and port), and hosts listed in MCP_ALLOWED_API_URL_HOSTS. Paths are allowed, but credentials, query strings, and fragments (including bare ? or #) are not. Without an override, the operator-configured URL is used unchanged.
Claude Desktop
Add to your claude_desktop_config.json:
{
"mcpServers": {
"browserless": {
"url": "https://mcp.browserless.io/mcp?token=your-token-here"
}
}
}Cursor
Add to your Cursor MCP settings:
{
"mcpServers": {
"browserless": {
"url": "https://mcp.browserless.io/mcp?token=your-token-here"
}
}
}VS Code
Add to your VS Code settings (settings.json):
{
"mcp": {
"servers": {
"browserless": {
"url": "https://mcp.browserless.io/mcp",
"headers": {
"Authorization": "Bearer your-token-here"
}
}
}
}
}Windsurf
Add to your Windsurf MCP configuration:
{
"mcpServers": {
"browserless": {
"url": "https://mcp.browserless.io/mcp?token=your-token-here"
}
}
}Self-Hosting
The server can also be run locally — useful for air-gapped deployments or pointing at a self-hosted Browserless instance. Clone this repo and build the Docker image:
docker build -f docker/Dockerfile -t browserless-mcp .
docker run \
-e BROWSERLESS_TOKEN=your-token \
-e BROWSERLESS_API_URL=https://your-browserless-instance.example.com \
-p 8080:8080 \
browserless-mcpThen point your MCP client at http://localhost:8080/mcp using the same header/query-parameter auth as above.
Self-hosted environment variables
Variable | Required | Default | Description |
| Yes | — | Your Browserless API token |
| No |
| API endpoint (for self-hosted Browserless) |
| No | — | Comma-separated hosts allowed for client-supplied API URL overrides, in addition to Browserless and the configured API origin |
| No |
| Account API host — backs |
| No |
| Origin serving session-replay artifacts. Replay paths are origin-checked against it |
| No |
| Transport type: |
| No |
| HTTP server port (only for |
| No |
| Request timeout in milliseconds |
| No |
| Max retry attempts for failed requests |
| No |
| Cache TTL in milliseconds (0 to disable) |
| No | — | Amplitude project API key. Sends MCP usage analytics — SDK lifecycle events plus our own tool/skill events |
| No | unset (full surface) | Serve the reduced, directory-compliant surface. Fails closed: any set value except |
Skill retrieval diagnostics
Skill Retrieval Completed emits once per actual remote skill fetch through
the existing analytics queue (when ANALYTICS_ENABLED, SQS_QUEUE_URL, and
SQS_REGION are configured). It is not duplicated through the SDK's
AMPLITUDE_API_KEY transport. Cache hits and concurrent callers sharing a fetch
do not emit another completion. Failed retrievals remain retryable on the next
call; this instrumentation adds no retries.
Fields are result=hit|miss|error, normalized domain, UUID request_id,
source, attempt, integer duration_ms, stage=fetch|decode|validate, and
available http_status. skill_count appears only on valid responses: positive
for hits, zero for misses. Errors carry error_category=timeout|network_error|http_error|invalid_json|invalid_shape.
Sources are cli_agent, script_builder, autologin, agent_run, mcp_client,
or unknown. Each fetch currently has attempt=1; no run identifier is
available at these call sites, so run_id is omitted.
Domains outside the bounded hostname format become invalid without dropping
the completion from the denominator.
Set OTEL_EXPORTER_OTLP_LOGS_ENDPOINT to a trusted collector's full /v1/logs
URL to export matching skill.retrieval.failed WARN records as OTLP/HTTP JSON.
The default is disabled. Exports have a one-second deadline, at most 16 in-flight
requests, and no retry. Caught queue/skill analytics errors produce
skill.telemetry.delivery_failed with originating_event and the fixed
diagnostic category delivery_error, at most once per minute per process.
Exporter failures are swallowed without recursively reporting themselves.
No new log contains tokens, prompts, full URLs, response bodies, or recipe text.
The queue retains its existing authentication field, separately from log fields.
Example failure attributes:
{
"event.name": "skill.retrieval.failed",
"result": "error",
"domain": "shop.example",
"request_id": "416e0409-25e2-4399-a3fa-6939f43a75e0",
"source": "mcp_client",
"attempt": 1,
"stage": "fetch",
"error_category": "http_error",
"http_status": 429,
"duration_ms": 17
}Retrieval error rate is error completions / all completions. Hit rate is hit
completions / valid completions. Do not add the separate server Skill Lookup
events to either denominator. Tests use local/mock sinks; a configured exporter
or console message is not proof of remote receipt.
Failure diagnostics
MCP Tool Request retains analytics_version=2, the existing coarse
error_category, status_code, timing and tool-specific properties. These
additive diagnostic fields are failure-only; a successful retry has none of them.
Property | Meaning |
|
|
|
|
| Zero-based index in the invocation's command batch, not the session-wide command counter. Omitted when setup/validation fails before a command starts. |
| The failed command's recognized typed method name. Unrecognized free-form method names are omitted to avoid emitting arbitrary input; the index still identifies the command. |
| Allowlisted structured codes: the uppercase reason names above, |
| An integer HTTP status (100–599) carried by structured error metadata. Never extracted from error prose. |
|
|
| A synthesized summary capped at 500 characters. Raw error messages, response bodies, HTML, scripts, selectors, credentials, cookies, authorization headers and URLs are never copied into this field. |
status_code keeps its original tool-specific meaning; the new status fields
do not replace it or turn successful target-page HTTP responses into failures.
HTTP failures retain API response status even when thrown. Codes are retained
when already available in structured errors or the JSON body read by the existing
4xx error handler; diagnostics do not read additional bodies on 5xx failures.
An unsuccessful search without structured evidence reports error_reason=unknown
and error_message="Unclassified search failure.". Its legacy user_error
category remains for chart compatibility, not as evidence of caller fault.
Example breakdowns: filter success=false and group by tool → error_reason;
for agent calls, group by failed_method → error_reason; for HTTP failures,
group by error_status_origin → error_status_code. Missing fields in older
events mean unavailable instrumentation, not an unknown failure. There is no
historical backfill. Verify representative received events after deployment
before treating these properties as available in production.
MCP Resources
Resource URI | Description |
| Smart scraper API documentation |
| Live service health status |
MCP Prompts
Prompt | Description |
| Scrape a webpage and summarize its content |
| Extract specific information from a webpage |
Development
npm install
npm run build
npm test
npm run coverageTests
The test suite uses Mocha with Chai and Sinon. Specs live alongside the code in test/ (test/lib/, test/tools/, test/prompts/, test/resources/, test/integration/) and run against the compiled output in build/.
npm test— compiles TypeScript and runs every*.spec.jsunderbuild/test/. No external services orBROWSERLESS_TOKENare required; the API client is stubbed.npm run coverage— runs the suite under c8 with the thresholds configured inpackage.json(lines ≥ 80%, branches ≥ 70%, functions ≥ 80%).
Tests run automatically on every pull request via the Test workflow on Node 24. PRs must keep the suite green before they can merge.
API Token
Get your API token at browserless.io. The token authenticates all requests to the Browserless API.
License
SSPL-1.0
Available Tools
16 toolsbrowserless_accountARead-onlyIdempotent
Read the Browserless account behind the current API token: plan, unit balance, billing period, and the names of the account API keys. Use it to answer "what plan am I on", "how many units are left", or "which keys exist". Read-only, and never returns API token values.
| Name | Required | Description | Default |
|---|---|---|---|
| action | Yes | Which part of the account to read. `billing` returns the plan, unit balance and billing period; `keys` lists the account API keys by name. | |
| _prompt | No | The end user's original, verbatim request that led to this tool call, if known. Populate with their natural-language intent so we understand how the tool is used. Do NOT include secrets, passwords, API keys, tokens, or other credentials. Omit if unavailable. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
The annotations already declare readOnlyHint, idempotentHint, and destructiveHint, and the description adds useful behavioral context: it reads data tied to the current API token and never returns API token values. This goes beyond the annotations with a meaningful security-relevant guarantee.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is compact and front-loaded: it states the resource, the exact data available, the supported question types, and an important safety guarantee in just two sentences. Every clause earns its place with no fluff.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a simple two-parameter read-only tool with a full input schema and rich annotations, the description is complete. It tells the agent what data the tool returns, how to select the sub-reading via examples, and that no credentials are exposed, which is sufficient for safe and correct invocation.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 100%, so the action enum and _prompt are already well documented. The description adds conceptual mapping between user questions and the action, but does not substantially extend the parameter semantics beyond what the schema already provides.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description is highly specific: it names the exact resource (Browserless account behind the current API token) and the exact information returned (plan, unit balance, billing period, API key names). It also cites concrete user questions the tool answers, making its purpose unmistakable and easily distinguishable from operation-focused siblings.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description clearly states when to use the tool by mapping it to natural-language questions like 'what plan am I on' and 'which keys exist'. It does not explicitly name sibling tools or describe when not to use it, but the context is clear enough for an agent to route to it correctly.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
browserless_agentADestructive
READ CAREFULLY: Execute browser commands in persistent agent session.
Core Loop (ReAct: Reason → Act → Observe)
Plan + check for a site recipe — restate the goal, decide the target host, then
browserless_skill { site: "<host>" }(see above). Load and follow any matching recipe before writing your own plan. Never jump straight togoto.goto — waits "domcontentloaded"
snapshot — returns interactive + informational elements (button, link, textbox, combobox, checkbox, heading, img+alt) with ref= selectors
Plan all actions from snapshot
Batch execute
Re-snapshot only if page changed
Repeat → close when done
Ending the session (REQUIRED)
An open session holds one of the account's concurrent browsers until it idles out — leaving it open is not free, and stacking them starves the next task.
Task complete? Close it. Send
{ "method": "close" }as its own call, as the last thing you do. This is the default for one-shot work (a lookup, a scrape, a form submit): close without asking.Ask instead of guessing only when follow-up in the SAME browser is genuinely likely (the user said "then...", you're mid-flow on a logged-in site, or the result invites a next step). Say the browser is still open, ask whether to close it, and close it as soon as they're done.
Never end your reply with a live session and no mention of it. Either it's closed, or you told the user it's open and why.
Site recipes (site-specific, NOT auto-injected) — CHECK FIRST
Many specific sites (marketplaces, gov portals, travel, real-estate, etc.) have a tuned recipe for a given task — proven selectors, API shortcuts, proxy needs, and known gotchas that a from-scratch plan will miss. These are not auto-injected; you must ask for them, and a recipe overrides any plan you'd build yourself (including "just use a prefiltered URL + evaluate").
This is step 0 of every task — do it before your first goto. The moment you know the target host (the user named the site, or you resolved which site to use), call browserless_skill { site: "<host>" } — e.g. { site: "airbnb.com" }. If it lists a recipe matching your task, load it with browserless_skill { id: "<host>/<slug>" } and follow it. Only when there's no match do you plan the steps yourself. Skipping this check on a supported site is a mistake — it's one cheap call.
Report the outcome (only if you loaded a site recipe). As your final command in the run, send { method: "reportSkillOutcome", params: { domain: "<host>", task: "<slug>", success: <bool> } } inside commands — where domain/task are the loaded recipe's <host>/<slug> and success is whether the recipe actually got you the result. This refines shared recipes and retires ones that stop working. Send it once, and only when you loaded a recipe — never for a self-planned run. Send it as your last command before any close (close ends the run and anything after it is dropped).
On failure, add one bounded failure_reason: authentication_required (login needed), site_changed (recipe no longer matches the site), blocked (access denied or challenge), timeout (operation timed out), missing_data (required output absent), incorrect_result (output present but wrong), or unknown (cause unclear). For example: { method: "reportSkillOutcome", params: { domain: "example.com", task: "search", success: false, failure_reason: "missing_data" } }. On success, omit failure_reason; success with a failure reason is invalid. Boolean-only legacy reports remain valid; failures default to unknown. These are agent-reported results, not independent validation. Never supply outcome_source: the server assigns provenance. Do not put URLs, secrets, prompts, copied content, or free-form explanations in outcome fields.
Proxy (optional)
Proxy config is a top-level proxy object on the tool call — it is applied when the session is opened. NEVER call proxy as a method inside commands — a { method: "proxy", ... } JSON-RPC mutation does NOT change the upstream proxy on an already-open session and will silently no-op.
If there is credible evidence the task needs a proxy, you MUST pass proxy options on the very FIRST call (before any goto/snapshot), because the config is read once at session creation. Credible signals include: the user asks for a specific country/region/locale; the target site is known to geo-restrict or block datacenter IPs (streaming, ticketing, retail, banking, real-estate, news paywalls); a prior attempt returned 403/451/captcha/"unusual traffic"/"access denied"; the user explicitly mentions residential / sticky IP / proxy.
If you already opened a session without a proxy and now realize one is needed, you must close and start a new session with the proxy options set — there is no in-session switch.
Use
proxy: { proxy: "datacenter" }when a lower-cost IP is sufficient. Useproxy: { proxy: "residential" }when the target is known to block datacenter IPs or the cheaper tier still hits a challenge.Inside the object:
proxyCountry: "us"— geo (ISO-2);proxyState/proxyCity(paid plans, 401 otherwise);proxySticky: true— stable IP;proxyLocaleMatch: true— match locale;proxyPreset— residential-only named config;externalProxyServer: "http://u:p@host:port"— bring your own (http(s) only)Geo/sticky/locale options require a built-in proxy tier or
externalProxyServer;proxyPresetrequiresproxy: "residential"
OS persona (optional)
The top-level emulationOs, emulatedDevice, screen, deviceScaleFactor, and deviceSlot options are read once when the session opens. Put them on the very first call, before any goto, then keep using the returned sessionId; close and open a new session to change them.
Reach for emulationOs only when there is evidence of platform fingerprinting: a Cloudflare or similar interstitial that never resolves, a hard block on an otherwise healthy page, or a site known to inspect the operating system. Start with emulationOs: "windows" unless the task or site requires another OS. Use emulatedDevice only with Android; desktop screen, deviceScaleFactor, and deviceSlot refine a desktop persona.
Auth
Never log in by default. Never invent or assume credentials exist (no "test credentials", no "your account"). If the snapshot contains a sign-in link OR you're about to mention "sign in" / "log in" / "auth required" — even as a suggested option to the user — call browserless_skill { id: "autonomous-login" } first, then follow its gates. The skill decides whether login is appropriate and whether credentials are in scope; do not skip it just because no password field is on the page yet.
After a loadSecret login, screenshot, PDF, liveURL, and page-content reads (evaluate/html/text) stay blocked until the credential is cleared. A full-page (main-frame) navigation clears it automatically; a single-page app that logs in without one — or only changes route client-side — does not. Once the credential is no longer on screen, send { method: "clearSecrets" } before the first capture. A CaptureBlockedError after login means this step was skipped.
Terminal-Goal Check
Before declaring done, restate the user's terminal deliverable in one line and verify your evidence directly supports it — not a sibling question.
Empty-state substitution. An empty/zero/null result from a resource that normally requires auth, scope, or filter context is evidence the precondition wasn't met — not evidence the question is answered. Empty cart while logged out, zero results while geo-restricted, empty inbox while unauthenticated: precondition failure → fix the precondition (often: load autonomous-login), don't return the empty result as the answer.
Multi-step preconditions. When the task names multiple steps ("go to X, then Y, report Z"), evaluate preconditions for the full chain before treating any step as optional. A blocker on step N blocks the whole task even if step 1 returned data.
Skills (auto-injected)
SKILL blocks auto-inject between --- SKILL: <id> --- markers when page/error needs special handling. Read carefully.
Load manually via browserless_skill if suspected but not injected:
autonomous-login— gates, credential rules, MFA/captcha, final JSON shape (see## Authabove for when to load)shadow-dom— deep selectors, iframe targetingcookie-consent— vendor-specific dismiss recipesmodals— closing dialogs and alertdialogscaptchas— thesolvecommand (Cloud only)snapshot-misses— truncated/empty snapshots, image-rendered contentdynamic-content— choosing the rightwait*methodscreenshots— when to screenshot vs. snapshot, scope and format choicesvision-fallback— click by coordinate when the snapshot can't surface an elementtabs— multi-tab workflows, peek-without-switching
Snapshot Rules
Until you snapshot a page, you CANNOT click/type/interact — snapshot first, no exceptions
NEVER guess, assume, or infer selectors — CSS selectors from your training data are wrong. ONLY use ref= / deep-ref= from latest snapshot
Snapshot STALE after: click, goto, select, navigation
Snapshot VALID after: type, hover, scroll, evaluate
Expect new content? → re-snapshot
Element roles in snapshot (link, button, textbox, combobox, checkbox, heading) tell you what each does
Snapshot lines may include
desc="...",action=METHOD URL,autocomplete=..., and intent markers (⚠ destructive,⚠ sign-out,sign-in,reset)Before activating or navigating to a control marked
⚠ destructiveor⚠ sign-out, confirm that the action is actually intended; an unlabeled destructive control is a common trapSnapshots after the first return a diff vs. your previous snapshot: only
+new /~changed /-removed elements, plus a count of unchanged ones omitted. Unchanged elements stay valid — keep using their refs from the earlier snapshot. If that earlier snapshot is no longer in your context (summarized/trimmed away), requestsnapshot { full: true }to get the complete element list again.
Selectors
Use ref= (CSS) or deep-ref= (starts
<) exactly as shown in snapshotExample:
[3] button "Sign In" ref=button#submit→"button#submit"deep-ref for shadow DOM / iframes — see
shadow-domskill
Iframes
Snapshots include a Frames list (cross-origin iframes) when present. Elements inside a frame are tagged [frame#N] and carry a deep-ref=< *url* css selector that already pierces the frame — pass it as-is to click/type/hover/checkbox. No frame switching needed. captcha/payment widgets (reCAPTCHA, hCaptcha, Stripe, Turnstile) show up here. shadow-dom skill auto-loads when frames present.
Tabs
Snapshots include tabs + activeTargetId — no getTabs needed. Multi-tab / snapshot { targetId } in tabs skill (auto-loads when >1 tab).
Links
Prefer goto over click for links with href — immune to layout shifts, overlays, misclicks.
Example: [5] a "About" ref=a[href='/about'] → goto { url: "https://ex.com/about" }
Only click when href is javascript: / # / missing.
Content Extraction
Check in-memory snapshot (text/values already there)
text { selector } — from specific element
evaluate { content } — JS (IIFE):
(() => { return ... })()html { selector } — raw HTML
Files (upload / download)
To download a file, DRIVE THE BROWSER — do not curl/wget/fetch the file yourself as a first move. Many real downloads (login/cookie-gated, generated server-side on demand, or triggered by a click whose response headers force the download) have NO fetchable URL — a direct fetch silently gets the wrong bytes, an HTML error page, or 403. Click/goto in the agent and collect from the auto-surfaced ledger. The ONLY time a direct fetch is correct: the ledger hands you a URL to use — the single-use /download/<id> URL, or an over-cap sourceUrl. Reaching for curl first is a bug, not a shortcut.
NEVER read a file's bytes or base64 into this conversation, and NEVER split/reassemble/inline base64 by hand. That is the wrong tool and will stall.
Upload a local file (stdio):
uploadFile { selector, files: [{ path }] }— the server reads + encodes it only inside the download directory or a directory explicitly allowed by the local operator throughBROWSERLESS_UPLOAD_DIRS. Symlink targets must also be inside an allowed directory. Ask the operator about rejected paths; do not bypass the restriction by reading or moving the file yourself.Upload a local file (HTTP): the server can't read your disk. Stage it once over HTTP, then use the handle:
curl -s -F file=@"/path/to/file" "<MCP_BASE_URL>/upload?token=<TOKEN>"→ returns{ "handle": "browserless-download://…" }→uploadFile { files: [{ handle }] }. (The path-rejection error gives you the exact command with your token + URL filled in.)Re-upload something from
getDownloads: pass itshandle(works in both modes).Download: just trigger it in the agent (click a download link, or goto the file URL). The captured file auto-surfaces as a notification on the agent response (filename/size/handle), never the bytes — the server waits for it to finish (bounded by size), so it usually lands on that same call. stdio: file already saved, you get its path. HTTP: a single-use
curl … /download/<id>?token=URL — fetch only if you need it. Files over the cap aren't transferred — you get the source URL to fetch directly. Path/handle reuses inuploadFile. (No separate download tool — use the agent.)base64
contentis a LAST RESORT — tiny inline data only.Full recipe:
file-transfersskill.
Batching — Maximize Per Call
Plan ALL actions from snapshot before next snapshot.
Process:
Classify actions: safe (type, hover, scroll, evaluate, select, checkbox) vs. page-changing (click, goto)
Batch: safe FIRST → page-changing LAST
For forms: if submit button is in snapshot, batch type + click in one call
Don't batch across navigations
Example form:
{ "commands": [
{ "method": "type", "params": { "selector": "input#email", "text": "j@d.com" } },
{ "method": "click", "params": { "selector": "button#submit" } }
] }Async
After async triggers (search, submit), use wait* before snapshot — waitForResponse best when API URL known. dynamic-content skill auto-loads on timeout. Never evaluate with setTimeout.
Error Recovery
Errors tagged Category: <NAME>:
SELECTOR_MISS — re-snapshot; retry
< selectorif not already deep-refSESSION_LOST — a fresh session was opened automatically; re-goto + snapshot (prior state gone)
UNAUTHORIZED / FORBIDDEN — pick different path
NOT_FOUND — different URL
SERVER_ERROR — backoff, retry once
NAVIGATION_FAILED — verify URL
TIMEOUT — longer wait or different signal
INVALID_PARAMS — fix params (schema authoritative)
UNKNOWN_METHOD — no such method; pick one from the schema
SCRIPT_ERROR — your
evaluatescript threw; page still alive, fix the scriptUNKNOWN — re-snapshot + re-plan
! NOTICE: URL changed cross-origin = prior plan/refs invalid, re-plan.
Never retry same failed action without re-snapshot.
Methods (non-obvious)
goto { url, waitUntil? } — default "domcontentloaded"; prefer over click for links
snapshot { maxElements?, targetId? } — cap 500; targetId peeks non-active tab
evaluate { content } — IIFE only
waitForSelector { selector, timeout? } — set 5000-10000ms
waitForResponse { url?, statuses?, timeout? } — url is glob
"*api/results*"createTab { url?, activate?, waitUntil? } — default activate: true; false = background
close — own call, NOT batched; only when task complete (premature close discards page state)
See schema for: screenshot, solve, back, forward, reload, click, type, select, checkbox, hover, scroll, text, html, waitForNavigation, waitForTimeout, waitForRequest, liveURL, getTabs, switchTab, closeTab
Runtime: LOCAL (stdio)
Before any file transfer, know your mode: this server runs over stdio, on the same machine as your files. To UPLOAD a local file, pass its path to uploadFile (files: [{ path }]). The path and any symlink target must be inside the download directory or a directory the local operator explicitly allowed through BROWSERLESS_UPLOAD_DIRS. Ask the operator about rejected paths; do not read or move the file to bypass the restriction. Do NOT base64 the file or read its bytes into the conversation. DOWNLOADS are saved to local disk; the agent response gives you the path.
Repetition self-check
When a tool response contains REPETITION WARNING, re-read your plan and compare your completed steps with the intended progress. Do not repeat the same batch blindly: choose a materially different approach within the task's constraints, or stop and report what is blocked and what you tried. Repetition is a signal to check progress, not proof of failure; continue a repeated action only when you can identify concrete progress or a task-required reason.
| Name | Required | Description | Default |
|---|---|---|---|
| os | No | Desktop OS to spoof for the stealth fingerprint (navigator.platform, UA, UA-CH client hints, GPU/font signals). Defaults to "windows" so the agent presents a coherent, low-risk desktop identity instead of its native Linux (a Chrome-masked UA over "Linux x86_64" is a bot tell that anti-bot checks flag). Forwarded to the browser as ?emulationOs; read once at session creation. | |
| proxy | No | Residential, datacenter, or external proxy config. Read once at session creation. Changing requires close() + a new session call. | |
| method | No | The BQL method to execute (used for single-command calls). When using "commands" array, this field is ignored. | |
| params | No | Parameters for the method (used for single-command calls). | |
| record | No | Arm screen-video recording at session launch. Use `startRecording` / `stopRecording`; stopping returns a single-use WebM link, never bytes. | |
| screen | No | Desktop screen as WIDTHxHEIGHT, with each dimension from 640 through 7680. Ignored for Android. | |
| _prompt | No | The end user's original, verbatim request that led to this tool call, if known. Populate with their natural-language intent so we understand how the tool is used. Do NOT include secrets, passwords, API keys, tokens, or other credentials. Omit if unavailable. | |
| profile | No | Optional name of an authentication profile to hydrate into the browser when the agent session connects. The profile's cookies, localStorage, and IndexedDB are restored into the session before the request runs. The profile must already exist for the API token in use — create one with Browserless.saveProfile in a live agent session first. `profile` binds each call to its hydrated session — you MUST pass it on every call in a multi-call flow, not just the first. A call that omits `profile` runs in the default, un-hydrated session and will look logged out; if that happens, re-issue the call WITH `profile` before concluding the session expired. A different `profile` value opens a separate session. | |
| commands | No | Optional: batch multiple commands in one call. When provided, "method" and "params" are ignored and commands are executed sequentially. Only the final result is returned. Use this to batch actions that share the same page state (e.g. filling a form: type email + type password + click submit). Do NOT batch across navigations. | |
| humanlike | No | Human-like cursor movement + pacing for clicks/scrolls. Improves the passive score of invisible anti-bot challenges (which weight real mouse/interaction signals). Defaults on. Forwarded as ?humanlike; read once at session creation. | |
| rationale | No | A short user-facing reason for this call. HARD BUDGET: 50 characters. Surfaced live in interactive UIs as the progress label. Write it for a human watching, in present-continuous form ("Logging in", "Filling the search form", "Checking the time", "Closing the cookie banner"). If your first draft is longer than 50 chars, REWORD IT to fit — compress to the essence; do NOT just chop. Bad: "Read page title and body text to determine why snapshot is empty" (64). Good: "Diagnosing empty snapshot" (24). Bad: "Filling out a very detailed multi-field signup form" (51). Good: "Filling the signup form" (23). Never use jargon, raw method names ("evaluate", "click"), JS, full URLs, or credentials. Include exactly one per `browserless_agent` call, even when batching commands. | |
| sessionId | No | The `sessionId` returned by your previous browserless_agent call in this conversation. Echo it back on EVERY subsequent call — it binds this conversation to its live browser and its page state (current URL, cookies, filled forms, open tabs). Omit it only on the first call; omitting it later abandons the current browser and starts a blank one, losing everything the session had done. Only ever pass a value the server returned — never invent one. | |
| deviceSlot | No | Stable desktop device slot. The server validates the account-specific upper bound. | |
| emulationOs | No | OS persona for platform spoofing. Set on the first call before navigation. | |
| createProfile | No | Open this session in profile-creation mode. The MCP tool POSTs /profile with these params, attaches the agent WS to the returned creation session (non-headless, 10-minute keepalive), and expects a saveProfile call before close. Mutually exclusive with `profile`. Load the `auth-profile` skill (via browserless_skill) for the full create-then-save recipe. | |
| integrationId | No | Optional 1Password integration id (e.g. "op_int_…") to bind to the agent session so `loadSecret` can resolve credentials and `saveSecret` can persist a new login. Find it via GET /integrations/onepassword. Bind it on EVERY call in a multi-call flow (like `profile`); a call that omits it runs with no vault bound and credential commands return CredentialNotResolved. `saveSecret` requires a write-enabled connection. Pair with `allowedDomains` to permit filling on the target sites. | |
| allowedDomains | No | Origins where a resolved secret may be filled, e.g. ["https://gymshark.com"]. Only meaningful with `integrationId`. Defaults to the integration's configured origins; set it to fill on additional sites. loadSecret is refused on any origin not covered here. | |
| emulatedDevice | No | Android device slug, used only with emulationOs="android". Unknown slugs select a seeded device. | |
| keepSessionAlive | No | Keep the browser pooled for reuse when you echo the returned session id (default true). Set false for a one-shot call: close the browser after the command batch and download drain, freeing its concurrency slot. Ignored for profile creation and attached sessions, whose lifetimes are managed separately. | |
| deviceScaleFactor | No | Desktop device pixel ratio. Ignored for Android. | |
| requiredCapabilities | No | Capabilities the planned flow requires (for example "vision", "os-spoofing", "datacenter-proxy", or "secret-capture"). Browserless checks the selected route and plan before opening a browser and names an available route on failure. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations only convey readOnlyHint=false, openWorldHint=true, destructiveHint=true; the description goes far beyond them. It discloses session costs (open sessions hold concurrency slots), the silent no-op of a proxy method inside commands, read-once-at-creation semantics for proxy/OS options, capture blocking after loadSecret login until clearSecrets, confirmation required before '⚠ destructive'/'⚠ sign-out' controls, empty-state-as-precondition-failure, download auto-surfacing without bytes, and stale-snapshot invalidation rules. Nothing contradicts the annotations, and every major behavioral trap is disclosed.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The structure is exemplary — numbered core loop, bolded rules, headed sections, concrete JSON examples, front-loaded priorities. But the description is a sprawling multi-thousand-word operations manual with clear redundancy: the close protocol appears in the core loop, the separate 'Ending the session (REQUIRED)' section, and the Methods list; upload path restrictions appear in both the Files section and the Runtime LOCAL section; proxy read-once semantics are restated in the description and the schema. Not every sentence earns its place, though the organization keeps it from scoring lower.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Given 21 parameters, no output schema, nested objects, and a complex session/method model, the description is remarkably complete. It covers return semantics the schema cannot (snapshot diff behavior, auto-surfaced download notifications, sessionId binding), documents all non-obvious methods with defaults, enumerates the full error taxonomy, and explains cross-cutting protocols (auth, tabs, iframes, batching, async waits, file transfers, repetition handling). There is no output schema to lean on, and the description still leaves no operational gap an agent would need.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100%, so the baseline is 3, and the schema already documents each parameter's mechanics. The description adds strategic decision guidance on top: proxy must precede any goto and cannot be changed mid-session, sessionId must be echoed or state is lost, profile must be passed on every call in a flow, rationale must be one per call under 50 chars in present-continuous form, and emulationOs should only be set with fingerprinting evidence. Some parameters (screen, deviceScaleFactor, record, requiredCapabilities) are left to the schema, so not a 5.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The opening line, 'Execute browser commands in persistent agent session,' names a specific verb and resource and immediately signals the tool's defining trait (persistent sessions) relative to one-shot siblings like browserless_crawl or browserless_smartscraper. The sibling differentiation is implicit rather than explicit — the description never names a sibling to contrast against — but the core-loop protocol (goto → snapshot → plan → batch → close) leaves no doubt about the tool's role.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description is dense with explicit when-to guidance: check browserless_skill at step 0 before any goto, close by default when a task completes, use a proxy only with credible evidence and pass it on the first call, reach for emulationOs only under fingerprinting evidence, prefer goto over click for links, and never curl a download first. It routes to alternatives (skills, the site-recipe tool) extensively, but never explicitly contrasts this tool with sibling browserless_* tools (e.g., when to use smartscraper/crawl instead), which keeps it a step below 5.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
browserless_crawlARead-only
Crawl a website and scrape every discovered page using Browserless. Starts from a seed URL and follows links up to a configurable depth. Supports sitemap discovery, path filtering, subdomain handling, and custom scrape options. Returns scraped content (markdown/HTML) for each page along with metadata. Useful for comprehensive site analysis, content extraction, and data gathering.
| Name | Required | Description | Default |
|---|---|---|---|
| url | Yes | The URL to crawl (must be http or https) | |
| delay | No | Delay between requests in milliseconds (default: 200) | |
| limit | No | Maximum number of pages to crawl (default: 100) | |
| _prompt | No | The end user's original, verbatim request that led to this tool call, if known. Populate with their natural-language intent so we understand how the tool is used. Do NOT include secrets, passwords, API keys, tokens, or other credentials. Omit if unavailable. | |
| profile | No | Optional name of an authentication profile to hydrate into the browser before each page is scraped. The profile's cookies, localStorage, and IndexedDB are restored into the session before the request runs. The profile must already exist for the API token in use — create one with Browserless.saveProfile in a live agent session first. | |
| sitemap | No | Sitemap handling: "auto" (default), "force", "skip" | auto |
| timeout | No | HTTP request timeout in milliseconds for API calls (default: 30000) | |
| maxDepth | No | Maximum link-follow depth from the root URL (default: 5) | |
| maxRetries | No | Number of retry attempts per failed page (default: 1) | |
| maxWaitTime | No | Maximum time in ms to wait for crawl completion when waitForCompletion is true (default: 300000 = 5 minutes) | |
| excludePaths | No | Regex patterns for URL paths to exclude | |
| includePaths | No | Regex patterns for URL paths to include | |
| pollInterval | No | Polling interval in ms when waiting for completion (default: 5000) | |
| scrapeOptions | No | Options controlling how each page is scraped | |
| allowSubdomains | No | Whether to follow links to subdomains | |
| waitForCompletion | No | Whether to wait for crawl completion (default: true). If false, returns immediately with crawl ID. | |
| allowExternalLinks | No | Whether to follow links to external domains |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare readOnlyHint=true and destructiveHint=false, so the description adds value by detailing the return type ('scraped content (markdown/HTML) for each page along with metadata') and key behaviors like depth control and sitemap handling. No contradiction.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Three sentences front-load the primary action, add operational details, and conclude with use cases. Every sentence is informative with no redundancy. Ideal conciseness for an AI agent.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Despite the tool having 17 parameters and a nested object (scrapeOptions), the description only covers a few high-level features (depth, sitemap, filtering, scraping). It omits important context like the async behavior (waitForCompletion), profile authentication, retries, limits, and delays. No output schema exists, so the description should provide more detail on return structure.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100%, so the description does not need to repeat parameter details. It adds high-level context by mentioning sitemap, filtering, and scrape options, which reinforces the schema but does not introduce new meaning beyond what the schema already provides. Baseline score of 3 is appropriate.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description clearly states it crawls a website and scrapes discovered pages, starting from a seed URL with configurable depth. It mentions sitemap discovery, path filtering, subdomain handling, and custom scrape options, effectively distinguishing it from siblings like browserless_search or browserless_agent.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description notes the tool is 'useful for comprehensive site analysis, content extraction, and data gathering,' which implies usage context. However, it does not explicitly state when not to use it or provide alternatives among the sibling tools, leaving room for ambiguity in selection.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
browserless_exportARead-only
Export a webpage from a URL via the Browserless /export API. Fetches the URL and returns its content in the native format (HTML, PDF, image, etc.). Automatically detects the content type. Set includeResources=true to bundle all page assets (CSS, JS, images) into a ZIP archive for offline use.
| Name | Required | Description | Default |
|---|---|---|---|
| url | Yes | The URL to export (must be http or https) | |
| _prompt | No | The end user's original, verbatim request that led to this tool call, if known. Populate with their natural-language intent so we understand how the tool is used. Do NOT include secrets, passwords, API keys, tokens, or other credentials. Omit if unavailable. | |
| profile | No | Optional name of an authentication profile to hydrate into the browser before the page is exported. The profile's cookies, localStorage, and IndexedDB are restored into the session before the request runs. The profile must already exist for the API token in use — create one with Browserless.saveProfile in a live agent session first. | |
| timeout | No | Request timeout in milliseconds | |
| bestAttempt | No | When true, proceed even if awaited events fail or timeout. | |
| gotoOptions | No | Puppeteer Page.goto() options for navigation | |
| waitForTimeout | No | Milliseconds to wait after page load before exporting | |
| includeResources | No | When true, bundle all linked resources (CSS, JS, images) into a ZIP file. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations (readOnlyHint, openWorldHint, destructiveHint) indicate safe read operation. Description adds that it uses /export API, auto-detects content type, and can bundle resources with includeResources. No contradictions.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Three sentences, front-loaded with main purpose, no redundant information. Every sentence contributes value.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Given no output schema, the description explains return formats (HTML, PDF, image, ZIP) and mentions profile and resource bundling. Missing error behavior or return structure, but adequate for the complexity.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100%; all parameters have descriptions. The description adds context for includeResources and profile, but adds little beyond what the schema already provides. Baseline 3 is appropriate.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description clearly states the tool exports a webpage via Browserless /export API, returns native format, and auto-detects content type. It distinguishes from siblings like browserless_crawl and browserless_search by focusing on single-page export.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
No explicit guidance on when to use this tool versus siblings. It implies usage for single-page export but does not compare with other Browserless tools or state prerequisites.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
browserless_functionADestructive
Execute custom Puppeteer JavaScript code on the Browserless cloud. Your function receives a Puppeteer page object and optional context data. Return { data, type } to control the response payload and Content-Type.
For binary outputs, set type to a real MIME so the bytes come back as a proper content block instead of base64 text:
image/png/image/jpeg/image/webp→ vision content block (~1.5K tokens)audio/mpeg/audio/wav→ audio content blockapplication/pdfand other binaries → resource content block (attachment)
Text responses are capped at 200,000 characters (~50K tokens). Larger text payloads will be rejected — filter or summarize inside your function, or switch to a binary type if you actually meant to return bytes.
Useful for complex scraping, form filling, or any browser automation that requires custom code.
| Name | Required | Description | Default |
|---|---|---|---|
| code | Yes | JavaScript (ESM) code to execute. The default export receives { page, context } and should return { data, type } where data is the response payload and type is the Content-Type string. | |
| _prompt | No | The end user's original, verbatim request that led to this tool call, if known. Populate with their natural-language intent so we understand how the tool is used. Do NOT include secrets, passwords, API keys, tokens, or other credentials. Omit if unavailable. | |
| context | No | Optional context object passed to the function as the second argument. | |
| profile | No | Optional name of an authentication profile to hydrate into the browser before the function executes. The profile's cookies, localStorage, and IndexedDB are restored into the session before the request runs. The profile must already exist for the API token in use — create one with Browserless.saveProfile in a live agent session first. | |
| timeout | No | Request timeout in milliseconds |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations indicate destructiveHint=true and openWorldHint=true, and the description adds context on binary output types, text caps, and return format. It doesn't contradict annotations and adds value beyond them, but could be more explicit about potential side effects or error behavior.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is well-structured: starts with main purpose, then details return requirements, binary handling, and text limitations. Every sentence adds value with no redundancy. It is appropriately sized for the tool's complexity.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Despite no output schema, the description covers execution, return, and constraints well. Missing elements include error handling, behavior on code exceptions, and more details on the profile parameter's lifecycle. Overall, very good but not exhaustive.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 100%, so baseline is 3. The description significantly enriches understanding by explaining the return format ({ data, type }), binary handling, and text cap, which are not in the schema. This adds substantial context for agent usage.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description clearly states the tool executes custom Puppeteer JavaScript code, with specific verb 'Execute custom Puppeteer JavaScript code'. It distinguishes itself from sibling tools by emphasizing custom code for complex scraping, form filling, or automation, while siblings like browserless_smartscraper or browserless_search target simpler or specific tasks.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description provides clear context on when to use: 'complex scraping, form filling, or any browser automation that requires custom code.' It also gives return format instructions and text limitations. However, it does not explicitly state when not to use or compare to sibling alternatives, leaving some guidance to inference.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
browserless_link_checkoutADestructive
Create, resume, cancel, or report a Stripe Link checkout in the exact active browser session. Create requires the latest browserless_agent sessionId and payment-field deep selectors. Resume retrieves and fills only after Link approval; payment credentials never reach this tool.
| Name | Required | Description | Default |
|---|---|---|---|
| cart | No | Required for create. | |
| step | No | Optional for report. | |
| tags | No | Optional for report. | |
| action | Yes | Checkout step to run. | |
| _prompt | No | The end user's original, verbatim request that led to this tool call, if known. Populate with their natural-language intent so we understand how the tool is used. Do NOT include secrets, passwords, API keys, tokens, or other credentials. Omit if unavailable. | |
| outcome | No | Required for report. | |
| currency | No | Required for create. | |
| merchant | No | Required for create. | |
| selectors | No | Required for create. | |
| checkout_id | No | Required for resume, cancel, and report. | |
| amount_minor | No | Required for create. | |
| browser_session_handle | Yes | Opaque sessionId returned by the browserless_agent call that has the active checkout page |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already mark this as non-read-only and destructive, and the description adds meaningful behavioral context: the operation targets an exact existing session, resume must follow Link approval, and payment credentials never reach the tool. These are details not available from the schema or annotations.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Three short sentences lead with the operation verbs, then add the two most important preconditions. There is no filler or repetition of schema content; every sentence carries distinct guidance.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
The tool is complex (12 params, four modes, nested objects, no output schema), and the description clearly covers create and resume, the most stateful modes. Cancel and report semantics are left to the verb name plus schema parameter requirements, and there is no return-value description, but annotations and detailed parameter schema fill most gaps.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
The schema already documents every parameter with per-action requirements (e.g., 'Required for create'), so the description's main contribution is the temporal nuance 'latest browserless_agent sessionId' and grouping selectors as payment fields. That is useful but modest against a 100%-covered schema, so baseline 3 is appropriate.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description names four explicit operations (create, resume, cancel, report) on a specific resource (Stripe Link checkout) and scopes them to 'the exact active browser session', which separates it from session management or connection siblings. The verb+resource pairing is unambiguous and not a restatement of the tool name.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
It gives clear preconditions: create needs the latest browserless_agent sessionId and payment-field deep selectors, and resume is valid only after Link approval. It does not explicitly name when-not-to-use alternatives or detail cancellation/report branching, so it stops short of a 5.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
browserless_link_connectADestructive
Manage the Browserless Stripe Link wallet. Use action "status" to check availability, "connect" to get a Stripe-owned authorization URL, or "disconnect" to remove the connection. Never ask the user for raw card details.
| Name | Required | Description | Default |
|---|---|---|---|
| action | Yes | Check Stripe Link status, start the connection flow, or disconnect the wallet. | |
| _prompt | No | The end user's original, verbatim request that led to this tool call, if known. Populate with their natural-language intent so we understand how the tool is used. Do NOT include secrets, passwords, API keys, tokens, or other credentials. Omit if unavailable. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Beyond annotations, the description discloses that connect returns a Stripe-owned authorization URL, disconnect removes the connection, and raw card details should never be requested. No contradictions with annotations; destructiveHint=true aligns with 'remove the connection.'
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Three concise sentences with no filler: the resource and action options are front-loaded, and the safety instruction earns its place. The description is easy to scan and directly supports invocation.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a two-parameter enum-based tool, the description covers the available actions and a key safety constraint. No output schema exists, but the connect action's output (a Stripe-owned authorization URL) is stated; status and disconnect outcomes are sufficiently described for the tool's complexity.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is already 100%, but the description enriches the action parameter by defining each enum value ('check availability', 'get a Stripe-owned authorization URL', 'remove the connection'). It also supplies an important safety qualifier about card details that is not in the schema.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description states a clear resource ('Browserless Stripe Link wallet') and enumerates three concrete verbs (status, connect, disconnect), so an agent knows what the tool does. It doesn't explicitly contrast with sibling tools like browserless_link_checkout, but the wallet-management scope is specific enough to differentiate.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
It maps each supported action to its intended purpose ('status' for availability, 'connect' for authorization URL, 'disconnect' for removal) and adds the safety rule not to ask for raw card details. It doesn't explicitly discuss when to prefer this tool over sibling tools, but within the tool the usage context is clear.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
browserless_logsARead-onlyIdempotent
Read Browserless's own record of the account's recent requests: what was attempted, whether it failed, why it stopped, how long it took and what it cost. This is the tool for diagnosing a run that failed on the Browserless side rather than in your own code. The window available depends on the account plan; the server reports the limit if a range is refused. Read-only.
| Name | Required | Description | Default |
|---|---|---|---|
| url | No | Filter by the target URL of the request. | |
| limit | No | Maximum entries to return, 1-100. Defaults to 50. | |
| order | No | Timestamp order. Defaults to newest first. | |
| cursor | No | Opaque cursor returned as `nextCursor` by the previous page. | |
| levels | No | Severity levels to include. Omit for all levels. | |
| reason | No | Filter by the specific failure reason within a category. | |
| _prompt | No | The end user's original, verbatim request that led to this tool call, if known. Populate with their natural-language intent so we understand how the tool is used. Do NOT include secrets, passwords, API keys, tokens, or other credentials. Omit if unavailable. | |
| endTime | No | Exclusive RFC 3339 end time. Defaults to now when omitted. | |
| outcome | No | Filter by request outcome, e.g. `failed` or `succeeded`. | |
| apiKeyId | No | Restrict to one API key, by id. Get ids from browserless_account with action "keys". | |
| category | No | Filter by failure category, e.g. `browserless_refused`, `browserless_killed`, `target_error`. | |
| endpoint | No | Filter by endpoint, e.g. `/chromium/bql` or `/screenshot`. | |
| requestId | No | Return only entries for one request id. | |
| startTime | No | Inclusive RFC 3339 start time. Omit to let the account’s plan decide how far back to look — the available window is plan-dependent and the server rejects a range that exceeds it. | |
| eventNames | No | Filter by lifecycle event name, e.g. `request.failed`, `bql.*.failed`. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already provide readOnlyHint=true, idempotentHint=true, and destructiveHint=false. The description agrees by stating 'Read-only.' It adds useful behavioral context beyond the annotations: the plan-dependent retention window, server refusal boundaries, and what data is recorded. This is transparent and does not contradict the annotations.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The entire tool description is three tight sentences with no fluff. It opens with the primary action and then layers the diagnostic purpose, the plan-window caveat, and the read-only safety hint. This is efficient, well-structured, and every sentence earns its place.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Even with no output schema, the description tells the agent exactly what the log entries will include and what can be diagnosed. It also covers an important edge case (server may refuse a range and report a limit) and the read-only nature of the tool. Given the richness of the 15-parameter schema, the description supplies the missing non-schema context.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
All 15 parameters are fully documented in the input schema with examples and descriptions, so the schema description coverage is 100%. The tool description itself does not need to add much about the parameters; it simply confirms the purpose and the return fields. This meets the baseline of 3 because the heavy lifting is already done in the schema.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description uses a specific verb ('Read') and a specific resource ('Browserless's own record of the account's recent requests'), then lists the record's value: whether it failed, why it stopped, duration, cost. It does not explicitly name sibling tools, but its diagnostic framing ('diagnosing a run that failed on the Browserless side rather than in your own code') differentiates it from the other browserless_* tools. This is slightly below a 5 because sibling differentiation is implicit rather than explicit.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description gives a clear when-to-use: it is the tool for diagnosing failures on the Browserless side, not your own code. It also warns that the available time window is plan-dependent and that the server may refuse overlong ranges. It does not explicitly say when to use alternative tools instead, but the guidance is still strong enough to steer an agent.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
browserless_mapARead-only
Discover and map all URLs on a website using Browserless. Scans a site via sitemaps and link extraction to find all pages. Returns a list of URLs with optional titles and descriptions. Use the search parameter to order results by relevance to a query. Useful for site audits, content discovery, and building site maps.
| Name | Required | Description | Default |
|---|---|---|---|
| url | Yes | The base URL to start mapping from (must be http or https) | |
| limit | No | Maximum number of links to return (default: 100, max: 5000) | |
| search | No | Search query to order results by relevance | |
| _prompt | No | The end user's original, verbatim request that led to this tool call, if known. Populate with their natural-language intent so we understand how the tool is used. Do NOT include secrets, passwords, API keys, tokens, or other credentials. Omit if unavailable. | |
| sitemap | No | Sitemap handling: "include" (default), "skip", "only" | include |
| timeout | No | Request timeout in milliseconds | |
| includeSubdomains | No | Include URLs from subdomains (default: true) | |
| ignoreQueryParameters | No | Exclude URLs with query parameters (default: true) |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare readOnlyHint, openWorldHint, and destructiveHint. Description adds beyond that by specifying method (sitemaps and link extraction) and that it returns a list of URLs with optional titles/descriptions, which is useful context.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Two concise sentences. The first front-loads the purpose, the second adds features and use cases. No unnecessary words.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
With 8 parameters and no output schema, the description explains the tool's core functionality and use cases but lacks details on output format or behavior under different parameter combinations. It is adequate but not fully complete.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100%, so baseline is 3. Description adds value by explaining the search parameter orders results by relevance to a query, which is not fully clear from the schema alone. Other parameters are adequately described in the schema.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
Clearly states the tool discovers and maps all URLs on a website using sitemaps and link extraction. Distinguishes from sibling tools like browserless_crawl (crawling) and browserless_search (searching within pages).
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Mentions use cases like site audits and content discovery, and explains the search parameter for relevance ordering. However, it does not explicitly state when not to use this tool or provide alternatives among siblings.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
browserless_performanceARead-only
Run a Lighthouse performance audit on any URL via the Browserless /performance API. Returns scores and metrics for accessibility, best practices, performance, PWA, and SEO. Optionally filter by category or supply performance budgets. Note: audits can take 30s–120s depending on the site.
| Name | Required | Description | Default |
|---|---|---|---|
| url | Yes | The URL to audit (must be http or https) | |
| _prompt | No | The end user's original, verbatim request that led to this tool call, if known. Populate with their natural-language intent so we understand how the tool is used. Do NOT include secrets, passwords, API keys, tokens, or other credentials. Omit if unavailable. | |
| budgets | No | Lighthouse performance budgets array. See https://developer.chrome.com/docs/lighthouse/performance/performance-budgets | |
| profile | No | Optional name of an authentication profile to hydrate into the browser before the Lighthouse audit runs. The profile's cookies, localStorage, and IndexedDB are restored into the session before the request runs. The profile must already exist for the API token in use — create one with Browserless.saveProfile in a live agent session first. | |
| timeout | No | Request timeout in milliseconds (audits can take 30s–120s) | |
| categories | No | Lighthouse categories to audit: "accessibility", "best-practices", "performance", "pwa", "seo". Omit for all categories. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
The description adds significant behavioral context beyond annotations (readOnlyHint, destructiveHint): audits take 30-120 seconds, can filter by category, and support authentication profiles. No contradictions with annotations.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is two sentences, front-loaded with the core purpose, and includes key notes about time and optionality without superfluous words. Every sentence is useful.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Despite no output schema, the description mentions return types (scores and metrics for multiple categories). Combined with parameter details and annotations, it provides complete context for an AI agent to use the tool correctly.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100%, so baseline is 3. The description adds value by explaining the timeout duration (30-120s), the necessity of existing profiles, and the optionality of budgets and categories, which goes beyond the schema descriptions.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description clearly states the tool runs a Lighthouse performance audit on any URL and returns scores for accessibility, best practices, performance, PWA, and SEO. This distinguishes it from sibling tools like browserless_crawl or browserless_search.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description provides context for using the tool (Lighthouse audit) and mentions optional filtering and budgets. It does not explicitly state when not to use it or compare to alternatives, but the purpose is clear enough for an AI agent to infer usage.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
browserless_profilesARead-only
List the authentication profiles saved for the current token. A profile is a saved logged-in browser state (cookies + storage) that can be replayed by passing its name as profile to other tools. Call this before a task that needs the browser to start signed in, to discover which profiles already exist and pick one by name. Returns each profile name plus cookie/origin counts and last-used time.
| Name | Required | Description | Default |
|---|---|---|---|
| limit | No | Maximum number of profiles to return (default: 100, max: 1000) | |
| offset | No | Number of profiles to skip for pagination (default: 0) | |
| _prompt | No | The end user's original, verbatim request that led to this tool call, if known. Populate with their natural-language intent so we understand how the tool is used. Do NOT include secrets, passwords, API keys, tokens, or other credentials. Omit if unavailable. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare readOnlyHint=true and destructiveHint=false, so the description's safety profile is consistent. The description adds valuable behavioral context: profiles are saved logged-in states (cookies+storage) and can be replayed, and the return includes name, cookie/origin counts, and last-used time. No contradictions with annotations.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is three sentences, front-loading the main purpose immediately. Every sentence serves a purpose: stating the action, defining what a profile is and its usage, and noting return fields. No wasted words.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Given there is no output schema, the description adequately explains what is returned (profile name, cookie/origin counts, last-used time). It also explains the concept of profiles and how they relate to other tools. For a list tool with pagination parameters, this is sufficiently complete.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 100% (all three parameters have descriptions). The tool description adds no information about parameters beyond what the schema provides. Per the rubric, when coverage is high, a score of 3 is the baseline. No additional value from the description.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description clearly states the tool lists authentication profiles for the current token, specifying the verb 'list', the exact resource, and the scope. It distinguishes itself by explaining what a profile is (saved browser state) and that profiles are reusable by name in other tools, differentiating it from sibling tools that perform actions like crawling or searching.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Explicitly states when to use: 'Call this before a task that needs the browser to start signed in, to discover which profiles already exist and pick one by name.' It provides clear context but does not explicitly mention when not to use or alternative tools; however, no sibling tool serves a similar purpose, so the guidance is sufficient.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
browserless_searchARead-only
Search the web using Browserless and optionally scrape each result. Performs web searches via SearXNG and can return results from web, news, or images. Optionally scrape each result URL to get markdown, HTML, links, or screenshots. Useful for research, gathering information, and finding relevant web pages.
| Name | Required | Description | Default |
|---|---|---|---|
| tbs | No | Time-based filter: "day", "week", "month", "year" | |
| lang | No | Language code for search results (default: "en") | en |
| limit | No | Maximum number of results to return (default: 10, max: 100) | |
| query | Yes | The search query string | |
| _prompt | No | The end user's original, verbatim request that led to this tool call, if known. Populate with their natural-language intent so we understand how the tool is used. Do NOT include secrets, passwords, API keys, tokens, or other credentials. Omit if unavailable. | |
| country | No | Country code for geo-targeted results | |
| sources | No | Search sources: "web", "news", "images" (default: ["web"]) | |
| timeout | No | Request timeout in milliseconds | |
| location | No | Location string for geo-targeted results | |
| categories | No | Filter by categories: "github", "research", "pdf" | |
| scrapeOptions | No | Options for scraping each search result |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations (readOnlyHint: true, destructiveHint: false) already indicate safe read operations. The description adds value by disclosing that search results can be optionally scraped (with formats like markdown, HTML, links, screenshots), implying potential performance impact from multiple requests. There is no contradiction with annotations.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is three sentences, front-loaded with the core action ('Search the web using Browserless and optionally scrape each result'), followed by a brief elaboration. Every sentence contributes meaning without redundancy or unnecessary detail.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Given the complexity (11 parameters, 100% schema coverage, no output schema), the description covers the tool's primary capabilities—searching and scraping—and hints at output formats. However, it does not specify the structure of the returned search results (e.g., whether they include titles, snippets, URLs) or the exact output when scraping is used, which would enhance completeness.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100%, with all parameters described in detail (e.g., tbs enum, limit bounds, scrapeOptions nested structure). The description does not add meaning beyond the schema—e.g., it doesn't explain when to use 'scrapeOptions' versus just searching. Baseline score of 3 is appropriate.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description clearly states the tool's purpose: 'Search the web using Browserless and optionally scrape each result.' It specifies the search engine (SearXNG), supported sources (web, news, images), and optional scraping for various formats. This differentiates it from sibling tools like browserless_crawl (multi-page crawling) or browserless_smartscraper (AI-powered scraping).
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description provides basic usage context—'Useful for research, gathering information, and finding relevant web pages'—but lacks explicit guidance on when not to use this tool or mention of alternatives. It does not address prerequisites or scenarios where other tools (e.g., browserless_crawl for deep crawling) would be more appropriate.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
browserless_sessionsARead-onlyIdempotent
Inspect the sessions on the Browserless account behind the current API token: browsers running right now, persistent sessions saved on dedicated workers, recorded session replays, and 1Password credential integrations. Use it to answer "what is running", "did my session survive", or "what got recorded". Read-only — it never stops a session. Action replay downloads one recording and returns a fully self-contained playable rrweb page, needing no network to render: display it inline if you can render HTML, otherwise build an artifact from the returned instructions so the user can watch it. Always show the replay — never just summarise it in words.
| Name | Required | Description | Default |
|---|---|---|---|
| page | No | Page number for `replays` (1-based). | |
| skip | No | Rows to skip, for paging through `active` or `integrations`. | |
| limit | No | Maximum rows to return (max 50). Applies to every action. | |
| action | Yes | Which session data to read. `active` = browsers running right now; `persistent` = saved sessions on dedicated workers, running or not; `replays` = list recorded session replays; `replay` = download one replay and render it as a playable rrweb page (needs `sessionId`); `integrations` = 1Password credential integrations. | |
| search | No | Filter `replays` by website or session id. | |
| _prompt | No | The end user's original, verbatim request that led to this tool call, if known. Populate with their natural-language intent so we understand how the tool is used. Do NOT include secrets, passwords, API keys, tokens, or other credentials. Omit if unavailable. | |
| sessionId | No | Which replay to download, for action `replay`. Get ids from action `replays`. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
The description notes it is 'Read-only — it never stops a session', matching the readOnlyHint while also spelling out the safety guarantee. It richly details the replay action's output contract, including that the rrweb page is self-contained, needs no network, and must be shown inline or as an artifact rather than summarized.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The definition is front-loaded with a clear purpose, then gives exactly the important usage context, and ends with the critical replay display instruction. Every sentence earns its place, and there is no fluff or redundant schema copying.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a tool with 7 parameters, no output schema, and no nested object structure, the description covers the main output and behavior for the key action 'replay', surfaces user-facing intent, and emphasizes the must-do UI behavior. Creators additionally receive 100% parameter schema coverage, keeping a complete enough picture for correct invocation.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
The input schema already documents all 7 parameters with descriptions, including the action enum and paging fields. These descriptions additionally calls out replay behavior, but it does not add semantic meaning to the individual parameters beyond the schema, so the baseline of 3 is appropriate.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description opens with the specific verb 'Inspect' and explicitly scopes the resource to 'sessions on the Browserless account behind the current API token'. It enumerates four concrete data kinds, which distinguishes it clearly from crawling, exporting, and performance sibling tools even without naming them.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description gives explicit use contexts with 'Use it to answer "what is running", "did my session survive", or "what got recorded"', which tells an agent when to select this tool. It does not name alternative sibling tools or describe when not to use this tool, so it stops short of a 5.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
browserless_skillARead-only
Load a Browserless agent skill on demand, or discover site-specific recipes.
Two uses:
{ site: "" } — list any site-specific recipes tuned for that host (e.g.
{ site: "ebay.com" }), returned as pointers. Do this as soon as you know the host you're about to drive; if one matches your task, load it by id. Returns a "no recipe" note when there's none.{ id: "" } — load a skill body: an in-house skill id (list below) OR a site recipe id
host/slugfrom asitelookup.
Use the in-house skills below when you suspect the page exhibits a non-trivial mechanic but no SKILL block was auto-injected. The auto-injection heuristics are conservative; calling this tool is the explicit fallback.
Available in-house skills:
shadow-dom — deep selectors, iframe URL-pattern syntax, what works through deep-ref
cookie-consent — vendor-specific dismiss recipes (OneTrust, Cookiebot, Didomi, etc.)
modals — close-button heuristics, ESC handling, alertdialog vs. dialog
snapshot-misses — truncated/empty snapshots, image-rendered content
dynamic-content — choosing the right
wait*method after async triggersscreenshots — when to screenshot vs. snapshot, scope and format choices
vision-fallback — click by coordinate when a "< " deep selector still can't surface the element (shadow DOM, nested iframe, canvas)
tabs — multi-tab workflows, peek-without-switching
autonomous-login — load before authenticating: when the user asked you to log in, when a wall blocks the task, or as soon as a password input appears. Covers the don't-login-by-default posture, contextual credential matching, MFA/captcha branches, and the required final JSON response shape.
captchas — the
solvecommand, response semantics, escalation path (Cloud-only)file-transfers —
uploadFile/getDownloads, stdio-path vs. base64 content, size caps
| Name | Required | Description | Default |
|---|---|---|---|
| id | No | The skill to load: an in-house skill id (see tool description) OR a site recipe id "host/slug" returned by a prior `site` lookup. | |
| site | No | A page host (e.g. "ebay.com"). Lists any site-specific recipes tuned for that host as pointers — then load one with its id. | |
| _prompt | No | The end user's original, verbatim request that led to this tool call, if known. Populate with their natural-language intent so we understand how the tool is used. Do NOT include secrets, passwords, API keys, tokens, or other credentials. Omit if unavailable. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare readOnlyHint=true and destructiveHint=false. The description adds meaningful behavior: the site lookup returns recipe pointers or a 'no recipe' note, id loads a skill body, and in-house skills are the explicit fallback for auto-injection misses. No contradiction with annotations.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is long but efficiently structured: a two-line summary, two numbered use modes, a short trigger rule, and a bulleted catalog of in-house skills. The most important usage guidance is front-loaded, and none of the bullet points are filler.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a tool with no output schema, the description covers the key runtime outcomes (pointers, no-recipe note, skill body), the host/id selection logic, and all available in-house skill identifiers. It stops short of specifying the exact response JSON shape, but enough is disclosed for an agent to call and interpret the tool correctly.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100% and each parameter has a schema description. The tool description adds value by explaining the id forms ('in-house skill id' vs. 'host/slug' recipe id) and the semantics of site lookup (pointers, no-recipe note), going beyond the schema's bare parameter names.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The opening sentence names a specific verb ('Load') and resource ('Browserless agent skill') and immediately adds a second distinct use ('discover site-specific recipes'). It clearly separates the two call modes and the in-house skill catalog makes the tool's scope concrete enough to distinguish it from siblings like browserless_agent or browserless_search.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description gives clear conditions: call with site as soon as host is known, load matching recipe by id, and use in-house skills when a page likely has a non-trivial mechanic but no SKILL block was auto-injected. It explicitly frames the tool as a fallback for conservative auto-injection. It does not explicitly say when not to use it versus sibling browserless_* tools, but the use cases are self-contained.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
browserless_smartscraperARead-only
Scrape a SINGLE webpage and return HTML, markdown, raw DOM text, links, screenshots, or PDFs plus page metadata. Handles JavaScript-heavy pages and anti-bot measures automatically. For content across MULTIPLE pages of a site, use browserless_crawl; to list a site's URLs, use browserless_map.
| Name | Required | Description | Default |
|---|---|---|---|
| url | Yes | The URL to scrape (must be http or https) | |
| _prompt | No | The end user's original, verbatim request that led to this tool call, if known. Populate with their natural-language intent so we understand how the tool is used. Do NOT include secrets, passwords, API keys, tokens, or other credentials. Omit if unavailable. | |
| formats | No | Output formats to include: "markdown", "html", "rawText", "screenshot", "pdf", "links". rawText is DOM text with script, style, and noscript elements removed and whitespace collapsed, or extracted text for PDF targets. Defaults to ["markdown"]. | |
| headers | No | Custom HTTP headers sent to the target site. host, authorization, proxy-authorization, cookie, set-cookie, x-forwarded-for, x-real-ip, and forwarded are removed by the API. | |
| profile | No | Optional name of an authentication profile to hydrate into the browser before scraping. The profile's cookies, localStorage, and IndexedDB are restored into the session before the request runs. The profile must already exist for the API token in use — create one with Browserless.saveProfile in a live agent session first. | |
| timeout | No | Request timeout in milliseconds | |
| waitFor | No | Milliseconds to wait after page load, from 0 to 30000. A positive value forces browser rendering. | |
| excludeTags | No | Up to 100 CSS selectors to remove from HTML webpage outputs. Malformed selectors are ignored. Cannot be combined with includeTags. | |
| includeTags | No | Up to 100 CSS selectors to keep in HTML webpage outputs. Malformed entries are ignored; if no selector matches, the scraper returns unfiltered content. Cannot be combined with excludeTags or onlyMainContent. | |
| onlyMainContent | No | For HTML webpages, remove nav, footer, aside, role=navigation, script, style, and noscript elements from DOM-derived outputs. Parsed JSON and PDF content are unchanged. Defaults to false. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare readOnlyHint=true and destructiveHint=false, so the safety profile is covered. The description adds useful behavioral context beyond those annotations by stating that it handles JavaScript-heavy pages and anti-bot measures automatically, and by stressing the single-page scope.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Three sentences with no filler. The most important constraint ('SINGLE webpage') is front-loaded, output options are listed compactly, and the sibling-tool routing is placed cleanly at the end. Every sentence earns its place.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Given the rich parameter schema and the annotations, the description covers purpose, output types, behavioral capabilities, and alternative tools. There is no output schema, but the description names return formats and metadata, which is sufficient for an agent to select and invoke the tool correctly.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 100%, so the input schema fully documents all 10 parameters. The description adds only a high-level mention of output formats, which is fine, but it doesn't go beyond what the schema already provides. Baseline 3 is appropriate.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description states a specific verb ('Scrape'), a clear resource ('a SINGLE webpage'), and enumerates the exact output types (HTML, markdown, raw DOM text, links, screenshots, PDFs, metadata). It also explicitly distinguishes itself from browserless_crawl and browserless_map, so an agent can tell them apart without opening schemas.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description gives explicit routing guidance: use this tool for a single page, browserless_crawl for multiple pages, and browserless_map for listing URLs. This directly tells an agent when this tool is appropriate and names the alternatives, leaving no ambiguity.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
browserless_usageARead-onlyIdempotent
Read request and unit consumption for the Browserless account behind the current API token: successes, errors, timeouts, queueing, peak concurrency, captchas, proxy bytes and units. Use it to answer "how much have I used" or "why is my bill high". For per-request detail on failures, use browserless_logs instead. Read-only.
| Name | Required | Description | Default |
|---|---|---|---|
| _prompt | No | The end user's original, verbatim request that led to this tool call, if known. Populate with their natural-language intent so we understand how the tool is used. Do NOT include secrets, passwords, API keys, tokens, or other credentials. Omit if unavailable. | |
| apiKeyIds | No | Restrict the numbers to specific API keys, by id. Omit for the whole account. Get ids from browserless_account with action "keys". | |
| timeframe | No | Window the usage counts cover: the last hour, day, or week. Defaults to day. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare readOnlyHint, idempotentHint, and destructiveHint false, so the safety profile is known. The description adds useful context beyond that by explaining the account-scoped nature, the breadth of metrics, and the billing-oriented purpose, which helps the agent understand what the call exposes and how it is meant to be used.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is two focused sentences: one defining scope and returned data, and one providing usage intent and sibling routing. Every sentence earns its place, with no filler, redundancy, or unnecessarily repeated annotation information.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a read-only tool with zero required parameters and no output schema, this description is sufficiently complete. It names the resource, the data categories returned, the intended usage questions, and the sibling to use for related but distinct needs, so an agent can confidently select and invoke it.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
The input schema has 100% description coverage and clearly documents all three parameters, their defaults, and restrictions. The description does not need to repeat parameter details, so the baseline score of 3 is appropriate; it adds no unique parameter nuance beyond what the schema already provides.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description clearly identifies the tool as reading request and unit consumption for the Browserless account behind the current API token, and lists the specific metrics returned. It also explicitly differentiates itself from browserless_logs by noting that per-request failure details are handled there, making purpose and boundary unambiguous.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description explicitly says when to use the tool, with example user questions like "how much have I used" and "why is my bill high". It also gives a direct exclusion by telling the agent to use browserless_logs instead when per-request failure detail is needed, which is strong alternative routing.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
Tool Schema Changelog
Recent tool additions, removals, and schema changes observed during successful MCP inspections.
3 tool updates
v1.32.0- Changed
browserless_agent3 fields changed- changed
Input schema / properties / integrationId / descriptionPrevious value: -"Optional 1Password integration id (e.g. \"op_int_…\") to bind to the agent session so `loadSecret` can resolve `op://vault/item/field` references. Find it via GET /integrations/onepassword. Bind it on EVERY call in a multi-call flow (like `profile`); a call that omits it runs with no vault bound and `loadSecret` returns CredentialNotResolved. Pair with `allowedDomains` to permit filling on the target sites."New value: +"Optional 1Password integration id (e.g. \"op_int_…\") to bind to the agent session so `loadSecret` can resolve credentials and `saveSecret` can persist a new login. Find it via GET /integrations/onepassword. Bind it on EVERY call in a multi-call flow (like `profile`); a call that omits it runs with no vault bound and credential commands return CredentialNotResolved. `saveSecret` requires a write-enabled connection. Pair with `allowedDomains` to permit filling on the target sites." - added
Input schema / properties / keepSessionAliveAdded value: +{ + "default": true, + "description": "Keep the browser pooled for reuse when you echo the returned session id (default true). Set false for a one-shot call: close the browser after the command batch and download drain, freeing its concurrency slot. Ignored for profile creation and attached sessions, whose lifetimes are managed separately.", + "type": "boolean" +} - added
Input schema / properties / requiredCapabilitiesAdded value: +{ + "description": "Capabilities the planned flow requires (for example \"vision\", \"os-spoofing\", \"datacenter-proxy\", or \"secret-capture\"). Browserless checks the selected route and plan before opening a browser and names an available route on failure.", + "items": { + "minLength": 1, + "type": "string" + }, + "type": "array" +}
- Added
browserless_link_checkout - Added
browserless_link_connect
1 tool update
v1.30.0- Changed
browserless_agent8 fields changed- added
Input schema / properties / deviceScaleFactorAdded value: +{ + "anyOf": [ + { + "const": 1, + "type": "number" + }, + { + "const": 1.25, + "type": "number" + } + ], + "description": "Desktop device pixel ratio. Ignored for Android." +} - added
Input schema / properties / deviceSlotAdded value: +{ + "description": "Stable desktop device slot. The server validates the account-specific upper bound.", + "maximum": 9007199254740991, + "minimum": 0, + "type": "integer" +} - added
Input schema / properties / emulatedDeviceAdded value: +{ + "description": "Android device slug, used only with emulationOs=\"android\". Unknown slugs select a seeded device.", + "type": "string" +} - added
Input schema / properties / emulationOsAdded value: +{ + "description": "OS persona for platform spoofing. Set on the first call before navigation.", + "enum": [ + "windows", + "macos", + "linux", + "android" + ], + "type": "string" +} - changed
Input schema / properties / proxy / descriptionPrevious value: -"Residential / external proxy config. Read once at session creation. Changing requires close() + a new session call."New value: +"Residential, datacenter, or external proxy config. Read once at session creation. Changing requires close() + a new session call." - changed
Input schema / properties / proxy / properties / proxy / descriptionPrevious value: -"Routing tier. Only \"residential\" is supported today."New value: +"Routing tier. Datacenter is cheaper per MB; residential is less likely to be blocked." - changed
Input schema / properties / proxy / properties / proxy / enumPrevious value: -[ - "residential" -]New value: +[ + "residential", + "datacenter" +] - added
Input schema / properties / screenAdded value: +{ + "description": "Desktop screen as WIDTHxHEIGHT, with each dimension from 640 through 7680. Ignored for Android.", + "type": "string" +}
2 tool updates
v1.28.1- Changed
browserless_agent4 fields changed- removed
Input schema / properties / commands / items / anyOfRemoved value: -[ - { - "oneOf": [ - { - "properties": { - "method": { - "const": "goto", - "type": "string" - }, - "params": { - "properties": { - "timeout": { - "description": "Navigation timeout in milliseconds", - "type": "number" - }, - "url": { - "description": "The URL to navigate to", - "type": "string" - }, - "waitUntil": { - "description": "When to consider navigation complete. Defaults to \"domcontentloaded\". Avoid networkidle0/networkidle2 unless explicitly needed — they hang on SPAs and dynamic sites.", - "enum": [ - "load", - "domcontentloaded", - "networkidle0", - "networkidle2" - ], - "type": "string" - } - }, - "required": [ - "url" - ], - "type": "object" - } - }, - "required": [ - "method", - "params" - ], - "type": "object" - }, - { - "properties": { - "method": { - "const": "back", - "type": "string" - }, - "params": { - "default": {}, - "properties": { - "waitUntil": { - "description": "When to consider navigation complete. Defaults to \"load\".", - "enum": [ - "load", - "domcontentloaded", - "networkidle0", - "networkidle2" - ], - "type": "string" - } - }, - "type": "object" - } - }, - "required": [ - "method" - ], - "type": "object" - }, - { - "properties": { - "method": { - "const": "forward", - "type": "string" - }, - "params": { - "default": {}, - "properties": { - "waitUntil": { - "description": "When to consider navigation complete. Defaults to \"load\".", - "enum": [ - "load", - "domcontentloaded", - "networkidle0", - "networkidle2" - ], - "type": "string" - } - }, - "type": "object" - } - }, - "required": [ - "method" - ], - "type": "object" - }, - { - "properties": { - "method": { - "const": "reload", - "type": "string" - }, - "params": { - "default": {}, - "properties": { - "waitUntil": { - "description": "When to consider navigation complete. Defaults to \"load\".", - "enum": [ - "load", - "domcontentloaded", - "networkidle0", - "networkidle2" - ], - "type": "string" - } - }, - "type": "object" - } - }, - "required": [ - "method" - ], - "type": "object" - }, - { - "properties": { - "method": { - "const": "snapshot", - "type": "string" - }, - "params": { - "default": {}, - "properties": { - "full": { - "description": "Force a complete snapshot instead of a diff. Snapshots normally return only what changed since your previous one; set full:true when you no longer have that previous snapshot in context (e.g. it was summarized away) and need the entire element list again.", - "type": "boolean" - }, - "maxElements": { - "description": "Maximum number of elements to return (default 500)", - "exclusiveMinimum": 0, - "maximum": 9007199254740991, - "type": "integer" - }, - "targetId": { - "description": "Optional tab targetId to peek at without switching the active tab. Obtain via getTabs or a prior snapshot response. Omit to snapshot the active tab.", - "type": "string" - } - }, - "type": "object" - } - }, - "required": [ - "method" - ], - "type": "object" - }, - { - "properties": { - "method": { - "const": "getTabs", - "type": "string" - }, - "params": { - "default": {}, - "properties": {}, - "type": "object" - } - }, - "required": [ - "method" - ], - "type": "object" - }, - { - "properties": { - "method": { - "const": "switchTab", - "type": "string" - }, - "params": { - "properties": { - "targetId": { - "description": "The targetId of the tab to make active (from getTabs).", - "type": "string" - } - }, - "required": [ - "targetId" - ], - "type": "object" - } - }, - "required": [ - "method", - "params" - ], - "type": "object" - }, - { - "properties": { - "method": { - "const": "createTab", - "type": "string" - }, - "params": { - "default": {}, - "properties": { - "activate": { - "description": "If true (default), switch to the new tab. If false, open it in the background and leave the current tab active.", - "type": "boolean" - }, - "url": { - "description": "URL to open in the new tab. Defaults to about:blank if omitted.", - "type": "string" - }, - "waitUntil": { - "description": "When to consider navigation complete. Only applies when activate is true. Defaults to \"domcontentloaded\".", - "enum": [ - "load", - "domcontentloaded", - "networkidle0", - "networkidle2" - ], - "type": "string" - } - }, - "type": "object" - } - }, - "required": [ - "method" - ], - "type": "object" - }, - { - "properties": { - "method": { - "const": "closeTab", - "type": "string" - }, - "params": { - "properties": { - "targetId": { - "description": "The targetId of the tab to close.", - "type": "string" - } - }, - "required": [ - "targetId" - ], - "type": "object" - } - }, - "required": [ - "method", - "params" - ], - "type": "object" - }, - { - "properties": { - "method": { - "const": "click", - "type": "string" - }, - "params": { - "properties": { - "selector": { - "description": "CSS selector of the element to click. Omit when clicking by coordinate (x/y).", - "type": "string" - }, - "x": { - "description": "Vision fallback: raw screenshot-pixel X to click. Read it off a viewport (non-fullPage) screenshot and pass it as-is — the server maps screenshot pixels to the page. Provide x and y together, without selector.", - "type": "number" - }, - "y": { - "description": "Vision fallback: raw screenshot-pixel Y to click. See x.", - "type": "number" - } - }, - "type": "object" - } - }, - "required": [ - "method", - "params" - ], - "type": "object" - }, - { - "properties": { - "method": { - "const": "type", - "type": "string" - }, - "params": { - "properties": { - "selector": { - "description": "CSS selector of the input element", - "type": "string" - }, - "text": { - "description": "Text to type into the element", - "type": "string" - } - }, - "required": [ - "selector", - "text" - ], - "type": "object" - } - }, - "required": [ - "method", - "params" - ], - "type": "object" - }, - { - "properties": { - "method": { - "const": "loadSecret", - "type": "string" - }, - "params": { - "properties": { - "ref": { - "description": "The credential reference/alias to inject (e.g. an op:// reference). The secret value is resolved server-side and typed into the field — you never see it. Use this for ALL passwords and usernames from a secrets vault; never put a secret value in `type`. Also accepts a mail://<item>/otp reference that resolves the newest emailed one-time code server-side (value never surfaced); trigger the send, then call loadSecret promptly.", - "type": "string" - }, - "selector": { - "description": "CSS selector of the input to fill. If omitted, the secret is injected into the currently focused element (click/focus the field first).", - "type": "string" - } - }, - "required": [ - "ref" - ], - "type": "object" - } - }, - "required": [ - "method", - "params" - ], - "type": "object" - }, - { - "properties": { - "method": { - "const": "clearSecrets", - "description": "Re-enable screenshot, PDF, liveURL, and page-content-read captures (evaluate/html/text/querySelector/cookies) after a loadSecret login once the credential is no longer visible. Required for single-page apps that do not navigate; replay remains masked.", - "type": "string" - }, - "params": { - "additionalProperties": false, - "default": {}, - "properties": {}, - "type": "object" - } - }, - "required": [ - "method" - ], - "type": "object" - }, - { - "properties": { - "method": { - "const": "select", - "type": "string" - }, - "params": { - "properties": { - "selector": { - "description": "CSS selector of the select element", - "type": "string" - }, - "value": { - "description": "Option value to select. The snapshot lists a select's accepted values as options=[label=value, …] (bare entries have label === value) — take the value from there instead of guessing or reading the DOM with evaluate.", - "type": "string" - } - }, - "required": [ - "selector", - "value" - ], - "type": "object" - } - }, - "required": [ - "method", - "params" - ], - "type": "object" - }, - { - "properties": { - "method": { - "const": "checkbox", - "type": "string" - }, - "params": { - "properties": { - "checked": { - "description": "Desired checked state (default: toggle)", - "type": "boolean" - }, - "selector": { - "description": "CSS selector of the checkbox element", - "type": "string" - } - }, - "required": [ - "selector" - ], - "type": "object" - } - }, - "required": [ - "method", - "params" - ], - "type": "object" - }, - { - "properties": { - "method": { - "const": "hover", - "type": "string" - }, - "params": { - "properties": { - "selector": { - "description": "CSS selector of the element to hover over", - "type": "string" - } - }, - "required": [ - "selector" - ], - "type": "object" - } - }, - "required": [ - "method", - "params" - ], - "type": "object" - }, - { - "properties": { - "method": { - "const": "scroll", - "type": "string" - }, - "params": { - "default": {}, - "properties": { - "direction": { - "description": "Scroll direction. Defaults to \"down\".", - "enum": [ - "up", - "down", - "left", - "right" - ], - "type": "string" - }, - "selector": { - "description": "CSS selector of element to scroll (omit for page scroll)", - "type": "string" - } - }, - "type": "object" - } - }, - "required": [ - "method" - ], - "type": "object" - }, - { - "properties": { - "method": { - "const": "evaluate", - "type": "string" - }, - "params": { - "properties": { - "content": { - "description": "JavaScript code to execute (use IIFE syntax)", - "type": "string" - } - }, - "required": [ - "content" - ], - "type": "object" - } - }, - "required": [ - "method", - "params" - ], - "type": "object" - }, - { - "properties": { - "method": { - "const": "text", - "type": "string" - }, - "params": { - "default": {}, - "properties": { - "selector": { - "description": "CSS selector to extract text from", - "type": "string" - } - }, - "type": "object" - } - }, - "required": [ - "method" - ], - "type": "object" - }, - { - "properties": { - "method": { - "const": "html", - "type": "string" - }, - "params": { - "default": {}, - "properties": { - "selector": { - "description": "CSS selector to get HTML from", - "type": "string" - } - }, - "type": "object" - } - }, - "required": [ - "method" - ], - "type": "object" - }, - { - "properties": { - "method": { - "const": "waitForSelector", - "type": "string" - }, - "params": { - "properties": { - "selector": { - "description": "CSS selector to wait for", - "type": "string" - }, - "timeout": { - "description": "Timeout in milliseconds (recommend 5000-10000)", - "type": "number" - } - }, - "required": [ - "selector" - ], - "type": "object" - } - }, - "required": [ - "method", - "params" - ], - "type": "object" - }, - { - "properties": { - "method": { - "const": "waitForNavigation", - "type": "string" - }, - "params": { - "default": {}, - "properties": { - "timeout": { - "description": "Timeout in milliseconds (default 30000)", - "type": "number" - } - }, - "type": "object" - } - }, - "required": [ - "method" - ], - "type": "object" - }, - { - "properties": { - "method": { - "const": "waitForTimeout", - "type": "string" - }, - "params": { - "properties": { - "time": { - "description": "Time to wait in milliseconds (e.g., 3000 for 3 seconds)", - "type": "number" - } - }, - "required": [ - "time" - ], - "type": "object" - } - }, - "required": [ - "method", - "params" - ], - "type": "object" - }, - { - "properties": { - "method": { - "const": "waitForRequest", - "type": "string" - }, - "params": { - "properties": { - "method": { - "description": "HTTP method to match (e.g., \"GET\", \"POST\")", - "type": "string" - }, - "timeout": { - "description": "Timeout in milliseconds (default 30000)", - "type": "number" - }, - "url": { - "description": "URL pattern to match (glob-style, e.g., \"*api/results*\")", - "type": "string" - } - }, - "type": "object" - } - }, - "required": [ - "method", - "params" - ], - "type": "object" - }, - { - "properties": { - "method": { - "const": "waitForResponse", - "type": "string" - }, - "params": { - "properties": { - "statuses": { - "description": "HTTP status codes to match (e.g., [200, 201])", - "items": { - "type": "number" - }, - "type": "array" - }, - "timeout": { - "description": "Timeout in milliseconds (default 30000)", - "type": "number" - }, - "url": { - "description": "URL pattern to match (glob-style, e.g., \"*api/results*\")", - "type": "string" - } - }, - "type": "object" - } - }, - "required": [ - "method", - "params" - ], - "type": "object" - }, - { - "properties": { - "method": { - "const": "liveURL", - "type": "string" - }, - "params": { - "default": {}, - "properties": { - "interactable": { - "description": "Allow interaction via the live URL", - "type": "boolean" - }, - "quality": { - "description": "Image quality (1-100)", - "maximum": 100, - "minimum": 1, - "type": "integer" - }, - "resizable": { - "description": "Allow resizing the browser viewport", - "type": "boolean" - }, - "timeout": { - "description": "How long the live URL stays active (ms)", - "type": "number" - }, - "type": { - "description": "Image format for the stream", - "enum": [ - "jpeg", - "png" - ], - "type": "string" - } - }, - "type": "object" - } - }, - "required": [ - "method" - ], - "type": "object" - }, - { - "properties": { - "method": { - "const": "solve", - "type": "string" - }, - "params": { - "default": {}, - "properties": { - "timeout": { - "description": "How long to wait for the captcha to appear (ms). Default 30000. Does not bound the solver itself once a captcha is found.", - "exclusiveMinimum": 0, - "maximum": 9007199254740991, - "type": "integer" - }, - "type": { - "description": "Captcha type to solve. Omit to auto-detect.", - "enum": [ - "cloudflare", - "hcaptcha", - "recaptcha", - "recaptchaV3", - "geetest", - "normal", - "friendlyCaptcha", - "capy", - "textCaptcha", - "amazonWaf", - "dataDome", - "akamai", - "lemin", - "mtcaptcha", - "slider" - ], - "type": "string" - }, - "wait": { - "description": "Wait for the captcha to appear before solving (default true). Set false if you have already verified the widget is on screen.", - "type": "boolean" - } - }, - "type": "object" - } - }, - "required": [ - "method" - ], - "type": "object" - }, - { - "properties": { - "method": { - "const": "screenshot", - "type": "string" - }, - "params": { - "default": {}, - "properties": { - "clip": { - "description": "Region of the page to capture. Mutually exclusive with selector/fullPage.", - "properties": { - "height": { - "description": "Height of the clip, in CSS pixels (>0)", - "minimum": 1, - "type": "number" - }, - "scale": { - "description": "Scale factor of the clip (default 1, >0)", - "exclusiveMinimum": 0, - "type": "number" - }, - "width": { - "description": "Width of the clip, in CSS pixels (>0)", - "minimum": 1, - "type": "number" - }, - "x": { - "description": "X coordinate of the top-left corner, in CSS pixels", - "type": "number" - }, - "y": { - "description": "Y coordinate of the top-left corner, in CSS pixels", - "type": "number" - } - }, - "required": [ - "x", - "y", - "width", - "height" - ], - "type": "object" - }, - "fullPage": { - "description": "Capture the entire scrollable page (default false)", - "type": "boolean" - }, - "omitBackground": { - "description": "Hide default white background for transparent screenshots", - "type": "boolean" - }, - "quality": { - "description": "Image quality 0-100. Applies to jpeg/webp only.", - "maximum": 100, - "minimum": 0, - "type": "number" - }, - "selector": { - "description": "CSS selector of an element to screenshot. Mutually exclusive with fullPage/clip.", - "type": "string" - }, - "timeout": { - "description": "Timeout in milliseconds (default 30000)", - "type": "number" - }, - "toDisk": { - "description": "Save the screenshot to disk instead of returning it inline. You will NOT see the image; the response gives a reusable handle (local path in stdio, single-use GET URL over HTTP) exactly like a download — reuse it with uploadFile or hand it to the user. Use when you only need the file later, not to look at now (see file-transfers).", - "type": "boolean" - }, - "type": { - "description": "Image format. Default \"png\". Use \"jpeg\" for smaller payloads on large pages.", - "enum": [ - "jpeg", - "png", - "webp" - ], - "type": "string" - }, - "waitForImages": { - "description": "Wait for all images on the page to load before capturing", - "type": "boolean" - } - }, - "type": "object" - } - }, - "required": [ - "method" - ], - "type": "object" - }, - { - "properties": { - "method": { - "const": "uploadFile", - "type": "string" - }, - "params": { - "properties": { - "files": { - "description": "Files to attach. Combined decoded size is capped (server default 10MB, hard max 50MB).", - "items": { - "properties": { - "content": { - "description": "Base64-encoded file content. LAST RESORT — only for tiny data you already hold inline. Do NOT read a file into the conversation, and never split/reassemble base64 by hand: use `path` (stdio) or `handle` so the server moves the bytes.", - "type": "string" - }, - "handle": { - "description": "A download handle from a prior getDownloads (a path in stdio mode, a `browserless-download://` URI in HTTP mode). The MCP server reads the stored file — works in both transports and keeps the bytes out of the conversation. Use this to re-upload a file you just downloaded.", - "type": "string" - }, - "mimeType": { - "description": "MIME type; inferred from the extension when omitted.", - "type": "string" - }, - "name": { - "description": "Filename reported to the page. Defaults to the basename of `path`, else \"file\".", - "type": "string" - }, - "path": { - "description": "Local filesystem path to read and upload. stdio (local) mode only — the MCP server reads and base64-encodes it. In HTTP mode use `handle` or `content` instead.", - "type": "string" - } - }, - "type": "object" - }, - "minItems": 1, - "type": "array" - }, - "selector": { - "description": "CSS selector of the <input type=\"file\"> element", - "type": "string" - } - }, - "required": [ - "selector", - "files" - ], - "type": "object" - } - }, - "required": [ - "method", - "params" - ], - "type": "object" - }, - { - "properties": { - "method": { - "const": "getDownloads", - "type": "string" - }, - "params": { - "default": {}, - "properties": {}, - "type": "object" - } - }, - "required": [ - "method" - ], - "type": "object" - }, - { - "properties": { - "method": { - "const": "startRecording", - "type": "string" - }, - "params": { - "default": {}, - "properties": { - "height": { - "type": "number" - }, - "width": { - "type": "number" - } - }, - "type": "object" - } - }, - "required": [ - "method" - ], - "type": "object" - }, - { - "properties": { - "method": { - "const": "stopRecording", - "type": "string" - }, - "params": { - "default": {}, - "properties": {}, - "type": "object" - } - }, - "required": [ - "method" - ], - "type": "object" - }, - { - "properties": { - "method": { - "const": "close", - "type": "string" - }, - "params": { - "default": {}, - "properties": {}, - "type": "object" - } - }, - "required": [ - "method" - ], - "type": "object" - } - ] - }, - { - "properties": { - "method": { - "description": "The BQL method name", - "type": "string" - }, - "params": { - "additionalProperties": {}, - "default": {}, - "description": "Parameters for the method", - "propertyNames": { - "type": "string" - }, - "type": "object" - } - }, - "required": [ - "method" - ], - "type": "object" - } -] - added
Input schema / properties / commands / items / propertiesAdded value: +{ + "method": { + "description": "The BQL method name, e.g. \"goto\", \"click\", \"type\", \"snapshot\", \"screenshot\". See the tool description for the full command list.", + "type": "string" + }, + "params": { + "additionalProperties": {}, + "description": "Parameters for the method (see the tool description).", + "propertyNames": { + "type": "string" + }, + "type": "object" + } +} - added
Input schema / properties / commands / items / requiredAdded value: +[ + "method" +] - added
Input schema / properties / commands / items / typeAdded value: +"object"
- Changed
browserless_smartscraper8 fields changed- added
Input schema / properties / excludeTagsAdded value: +{ + "description": "Up to 100 CSS selectors to remove from HTML webpage outputs. Malformed selectors are ignored. Cannot be combined with includeTags.", + "items": { + "type": "string" + }, + "maxItems": 100, + "type": "array" +} - changed
Input schema / properties / formats / descriptionPrevious value: -"Output formats to include: \"markdown\", \"html\", \"screenshot\", \"pdf\", \"links\". Defaults to [\"markdown\"]."New value: +"Output formats to include: \"markdown\", \"html\", \"rawText\", \"screenshot\", \"pdf\", \"links\". rawText is DOM text with script, style, and noscript elements removed and whitespace collapsed, or extracted text for PDF targets. Defaults to [\"markdown\"]." - changed
Input schema / properties / formats / items / enumPrevious value: -[ - "markdown", - "html", - "screenshot", - "pdf", - "links" -]New value: +[ + "markdown", + "html", + "rawText", + "screenshot", + "pdf", + "links" +] - added
Input schema / properties / formats / minItemsAdded value: +1 - added
Input schema / properties / headersAdded value: +{ + "additionalProperties": false, + "description": "Custom HTTP headers sent to the target site. host, authorization, proxy-authorization, cookie, set-cookie, x-forwarded-for, x-real-ip, and forwarded are removed by the API.", + "propertyNames": { + "type": "string" + }, + "type": "object" +} - added
Input schema / properties / includeTagsAdded value: +{ + "description": "Up to 100 CSS selectors to keep in HTML webpage outputs. Malformed entries are ignored; if no selector matches, the scraper returns unfiltered content. Cannot be combined with excludeTags or onlyMainContent.", + "items": { + "type": "string" + }, + "maxItems": 100, + "type": "array" +} - added
Input schema / properties / onlyMainContentAdded value: +{ + "default": false, + "description": "For HTML webpages, remove nav, footer, aside, role=navigation, script, style, and noscript elements from DOM-derived outputs. Parsed JSON and PDF content are unchanged. Defaults to false.", + "type": "boolean" +} - added
Input schema / properties / waitForAdded value: +{ + "description": "Milliseconds to wait after page load, from 0 to 30000. A positive value forces browser rendering.", + "maximum": 30000, + "minimum": 0, + "type": "integer" +}
1 tool update
v1.27.0- Changed
browserless_agent4 fields changed- changed
Input schema / properties / commands / items / anyOfPrevious value: -[ - { - "oneOf": [ - { - "properties": { - "method": { - "const": "goto", - "type": "string" - }, - "params": { - "properties": { - "timeout": { - "description": "Navigation timeout in milliseconds", - "type": "number" - }, - "url": { - "description": "The URL to navigate to", - "type": "string" - }, - "waitUntil": { - "description": "When to consider navigation complete. Defaults to \"domcontentloaded\". Avoid networkidle0/networkidle2 unless explicitly needed — they hang on SPAs and dynamic sites.", - "enum": [ - "load", - "domcontentloaded", - "networkidle0", - "networkidle2" - ], - "type": "string" - } - }, - "required": [ - "url" - ], - "type": "object" - } - }, - "required": [ - "method", - "params" - ], - "type": "object" - }, - { - "properties": { - "method": { - "const": "back", - "type": "string" - }, - "params": { - "default": {}, - "properties": { - "waitUntil": { - "description": "When to consider navigation complete. Defaults to \"load\".", - "enum": [ - "load", - "domcontentloaded", - "networkidle0", - "networkidle2" - ], - "type": "string" - } - }, - "type": "object" - } - }, - "required": [ - "method" - ], - "type": "object" - }, - { - "properties": { - "method": { - "const": "forward", - "type": "string" - }, - "params": { - "default": {}, - "properties": { - "waitUntil": { - "description": "When to consider navigation complete. Defaults to \"load\".", - "enum": [ - "load", - "domcontentloaded", - "networkidle0", - "networkidle2" - ], - "type": "string" - } - }, - "type": "object" - } - }, - "required": [ - "method" - ], - "type": "object" - }, - { - "properties": { - "method": { - "const": "reload", - "type": "string" - }, - "params": { - "default": {}, - "properties": { - "waitUntil": { - "description": "When to consider navigation complete. Defaults to \"load\".", - "enum": [ - "load", - "domcontentloaded", - "networkidle0", - "networkidle2" - ], - "type": "string" - } - }, - "type": "object" - } - }, - "required": [ - "method" - ], - "type": "object" - }, - { - "properties": { - "method": { - "const": "snapshot", - "type": "string" - }, - "params": { - "default": {}, - "properties": { - "full": { - "description": "Force a complete snapshot instead of a diff. Snapshots normally return only what changed since your previous one; set full:true when you no longer have that previous snapshot in context (e.g. it was summarized away) and need the entire element list again.", - "type": "boolean" - }, - "maxElements": { - "description": "Maximum number of elements to return (default 500)", - "exclusiveMinimum": 0, - "maximum": 9007199254740991, - "type": "integer" - }, - "targetId": { - "description": "Optional tab targetId to peek at without switching the active tab. Obtain via getTabs or a prior snapshot response. Omit to snapshot the active tab.", - "type": "string" - } - }, - "type": "object" - } - }, - "required": [ - "method" - ], - "type": "object" - }, - { - "properties": { - "method": { - "const": "getTabs", - "type": "string" - }, - "params": { - "default": {}, - "properties": {}, - "type": "object" - } - }, - "required": [ - "method" - ], - "type": "object" - }, - { - "properties": { - "method": { - "const": "switchTab", - "type": "string" - }, - "params": { - "properties": { - "targetId": { - "description": "The targetId of the tab to make active (from getTabs).", - "type": "string" - } - }, - "required": [ - "targetId" - ], - "type": "object" - } - }, - "required": [ - "method", - "params" - ], - "type": "object" - }, - { - "properties": { - "method": { - "const": "createTab", - "type": "string" - }, - "params": { - "default": {}, - "properties": { - "activate": { - "description": "If true (default), switch to the new tab. If false, open it in the background and leave the current tab active.", - "type": "boolean" - }, - "url": { - "description": "URL to open in the new tab. Defaults to about:blank if omitted.", - "type": "string" - }, - "waitUntil": { - "description": "When to consider navigation complete. Only applies when activate is true. Defaults to \"domcontentloaded\".", - "enum": [ - "load", - "domcontentloaded", - "networkidle0", - "networkidle2" - ], - "type": "string" - } - }, - "type": "object" - } - }, - "required": [ - "method" - ], - "type": "object" - }, - { - "properties": { - "method": { - "const": "closeTab", - "type": "string" - }, - "params": { - "properties": { - "targetId": { - "description": "The targetId of the tab to close.", - "type": "string" - } - }, - "required": [ - "targetId" - ], - "type": "object" - } - }, - "required": [ - "method", - "params" - ], - "type": "object" - }, - { - "properties": { - "method": { - "const": "click", - "type": "string" - }, - "params": { - "properties": { - "selector": { - "description": "CSS selector of the element to click", - "type": "string" - } - }, - "required": [ - "selector" - ], - "type": "object" - } - }, - "required": [ - "method", - "params" - ], - "type": "object" - }, - { - "properties": { - "method": { - "const": "type", - "type": "string" - }, - "params": { - "properties": { - "selector": { - "description": "CSS selector of the input element", - "type": "string" - }, - "text": { - "description": "Text to type into the element", - "type": "string" - } - }, - "required": [ - "selector", - "text" - ], - "type": "object" - } - }, - "required": [ - "method", - "params" - ], - "type": "object" - }, - { - "properties": { - "method": { - "const": "loadSecret", - "type": "string" - }, - "params": { - "properties": { - "ref": { - "description": "The credential reference/alias to inject (e.g. an op:// reference). The secret value is resolved server-side and typed into the field — you never see it. Use this for ALL passwords and usernames from a secrets vault; never put a secret value in `type`. Also accepts a mail://<item>/otp reference that resolves the newest emailed one-time code server-side (value never surfaced); trigger the send, then call loadSecret promptly.", - "type": "string" - }, - "selector": { - "description": "CSS selector of the input to fill. If omitted, the secret is injected into the currently focused element (click/focus the field first).", - "type": "string" - } - }, - "required": [ - "ref" - ], - "type": "object" - } - }, - "required": [ - "method", - "params" - ], - "type": "object" - }, - { - "properties": { - "method": { - "const": "select", - "type": "string" - }, - "params": { - "properties": { - "selector": { - "description": "CSS selector of the select element", - "type": "string" - }, - "value": { - "description": "Option value to select. The snapshot lists a select's accepted values as options=[label=value, …] (bare entries have label === value) — take the value from there instead of guessing or reading the DOM with evaluate.", - "type": "string" - } - }, - "required": [ - "selector", - "value" - ], - "type": "object" - } - }, - "required": [ - "method", - "params" - ], - "type": "object" - }, - { - "properties": { - "method": { - "const": "checkbox", - "type": "string" - }, - "params": { - "properties": { - "checked": { - "description": "Desired checked state (default: toggle)", - "type": "boolean" - }, - "selector": { - "description": "CSS selector of the checkbox element", - "type": "string" - } - }, - "required": [ - "selector" - ], - "type": "object" - } - }, - "required": [ - "method", - "params" - ], - "type": "object" - }, - { - "properties": { - "method": { - "const": "hover", - "type": "string" - }, - "params": { - "properties": { - "selector": { - "description": "CSS selector of the element to hover over", - "type": "string" - } - }, - "required": [ - "selector" - ], - "type": "object" - } - }, - "required": [ - "method", - "params" - ], - "type": "object" - }, - { - "properties": { - "method": { - "const": "scroll", - "type": "string" - }, - "params": { - "default": {}, - "properties": { - "direction": { - "description": "Scroll direction. Defaults to \"down\".", - "enum": [ - "up", - "down", - "left", - "right" - ], - "type": "string" - }, - "selector": { - "description": "CSS selector of element to scroll (omit for page scroll)", - "type": "string" - } - }, - "type": "object" - } - }, - "required": [ - "method" - ], - "type": "object" - }, - { - "properties": { - "method": { - "const": "evaluate", - "type": "string" - }, - "params": { - "properties": { - "content": { - "description": "JavaScript code to execute (use IIFE syntax)", - "type": "string" - } - }, - "required": [ - "content" - ], - "type": "object" - } - }, - "required": [ - "method", - "params" - ], - "type": "object" - }, - { - "properties": { - "method": { - "const": "text", - "type": "string" - }, - "params": { - "default": {}, - "properties": { - "selector": { - "description": "CSS selector to extract text from", - "type": "string" - } - }, - "type": "object" - } - }, - "required": [ - "method" - ], - "type": "object" - }, - { - "properties": { - "method": { - "const": "html", - "type": "string" - }, - "params": { - "default": {}, - "properties": { - "selector": { - "description": "CSS selector to get HTML from", - "type": "string" - } - }, - "type": "object" - } - }, - "required": [ - "method" - ], - "type": "object" - }, - { - "properties": { - "method": { - "const": "waitForSelector", - "type": "string" - }, - "params": { - "properties": { - "selector": { - "description": "CSS selector to wait for", - "type": "string" - }, - "timeout": { - "description": "Timeout in milliseconds (recommend 5000-10000)", - "type": "number" - } - }, - "required": [ - "selector" - ], - "type": "object" - } - }, - "required": [ - "method", - "params" - ], - "type": "object" - }, - { - "properties": { - "method": { - "const": "waitForNavigation", - "type": "string" - }, - "params": { - "default": {}, - "properties": { - "timeout": { - "description": "Timeout in milliseconds (default 30000)", - "type": "number" - } - }, - "type": "object" - } - }, - "required": [ - "method" - ], - "type": "object" - }, - { - "properties": { - "method": { - "const": "waitForTimeout", - "type": "string" - }, - "params": { - "properties": { - "time": { - "description": "Time to wait in milliseconds (e.g., 3000 for 3 seconds)", - "type": "number" - } - }, - "required": [ - "time" - ], - "type": "object" - } - }, - "required": [ - "method", - "params" - ], - "type": "object" - }, - { - "properties": { - "method": { - "const": "waitForRequest", - "type": "string" - }, - "params": { - "properties": { - "method": { - "description": "HTTP method to match (e.g., \"GET\", \"POST\")", - "type": "string" - }, - "timeout": { - "description": "Timeout in milliseconds (default 30000)", - "type": "number" - }, - "url": { - "description": "URL pattern to match (glob-style, e.g., \"*api/results*\")", - "type": "string" - } - }, - "type": "object" - } - }, - "required": [ - "method", - "params" - ], - "type": "object" - }, - { - "properties": { - "method": { - "const": "waitForResponse", - "type": "string" - }, - "params": { - "properties": { - "statuses": { - "description": "HTTP status codes to match (e.g., [200, 201])", - "items": { - "type": "number" - }, - "type": "array" - }, - "timeout": { - "description": "Timeout in milliseconds (default 30000)", - "type": "number" - }, - "url": { - "description": "URL pattern to match (glob-style, e.g., \"*api/results*\")", - "type": "string" - } - }, - "type": "object" - } - }, - "required": [ - "method", - "params" - ], - "type": "object" - }, - { - "properties": { - "method": { - "const": "liveURL", - "type": "string" - }, - "params": { - "default": {}, - "properties": { - "interactable": { - "description": "Allow interaction via the live URL", - "type": "boolean" - }, - "quality": { - "description": "Image quality (1-100)", - "maximum": 100, - "minimum": 1, - "type": "integer" - }, - "resizable": { - "description": "Allow resizing the browser viewport", - "type": "boolean" - }, - "timeout": { - "description": "How long the live URL stays active (ms)", - "type": "number" - }, - "type": { - "description": "Image format for the stream", - "enum": [ - "jpeg", - "png" - ], - "type": "string" - } - }, - "type": "object" - } - }, - "required": [ - "method" - ], - "type": "object" - }, - { - "properties": { - "method": { - "const": "solve", - "type": "string" - }, - "params": { - "default": {}, - "properties": { - "timeout": { - "description": "How long to wait for the captcha to appear (ms). Default 30000. Does not bound the solver itself once a captcha is found.", - "exclusiveMinimum": 0, - "maximum": 9007199254740991, - "type": "integer" - }, - "type": { - "description": "Captcha type to solve. Omit to auto-detect.", - "enum": [ - "cloudflare", - "hcaptcha", - "recaptcha", - "recaptchaV3", - "geetest", - "normal", - "friendlyCaptcha", - "capy", - "textCaptcha", - "amazonWaf", - "dataDome", - "akamai", - "lemin", - "mtcaptcha", - "slider" - ], - "type": "string" - }, - "wait": { - "description": "Wait for the captcha to appear before solving (default true). Set false if you have already verified the widget is on screen.", - "type": "boolean" - } - }, - "type": "object" - } - }, - "required": [ - "method" - ], - "type": "object" - }, - { - "properties": { - "method": { - "const": "screenshot", - "type": "string" - }, - "params": { - "default": {}, - "properties": { - "clip": { - "description": "Region of the page to capture. Mutually exclusive with selector/fullPage.", - "properties": { - "height": { - "description": "Height of the clip, in CSS pixels (>0)", - "minimum": 1, - "type": "number" - }, - "scale": { - "description": "Scale factor of the clip (default 1, >0)", - "exclusiveMinimum": 0, - "type": "number" - }, - "width": { - "description": "Width of the clip, in CSS pixels (>0)", - "minimum": 1, - "type": "number" - }, - "x": { - "description": "X coordinate of the top-left corner, in CSS pixels", - "type": "number" - }, - "y": { - "description": "Y coordinate of the top-left corner, in CSS pixels", - "type": "number" - } - }, - "required": [ - "x", - "y", - "width", - "height" - ], - "type": "object" - }, - "fullPage": { - "description": "Capture the entire scrollable page (default false)", - "type": "boolean" - }, - "omitBackground": { - "description": "Hide default white background for transparent screenshots", - "type": "boolean" - }, - "quality": { - "description": "Image quality 0-100. Applies to jpeg/webp only.", - "maximum": 100, - "minimum": 0, - "type": "number" - }, - "selector": { - "description": "CSS selector of an element to screenshot. Mutually exclusive with fullPage/clip.", - "type": "string" - }, - "timeout": { - "description": "Timeout in milliseconds (default 30000)", - "type": "number" - }, - "toDisk": { - "description": "Save the screenshot to disk instead of returning it inline. You will NOT see the image; the response gives a reusable handle (local path in stdio, single-use GET URL over HTTP) exactly like a download — reuse it with uploadFile or hand it to the user. Use when you only need the file later, not to look at now (see file-transfers).", - "type": "boolean" - }, - "type": { - "description": "Image format. Default \"png\". Use \"jpeg\" for smaller payloads on large pages.", - "enum": [ - "jpeg", - "png", - "webp" - ], - "type": "string" - }, - "waitForImages": { - "description": "Wait for all images on the page to load before capturing", - "type": "boolean" - } - }, - "type": "object" - } - }, - "required": [ - "method" - ], - "type": "object" - }, - { - "properties": { - "method": { - "const": "uploadFile", - "type": "string" - }, - "params": { - "properties": { - "files": { - "description": "Files to attach. Combined decoded size is capped (server default 10MB, hard max 50MB).", - "items": { - "properties": { - "content": { - "description": "Base64-encoded file content. LAST RESORT — only for tiny data you already hold inline. Do NOT read a file into the conversation, and never split/reassemble base64 by hand: use `path` (stdio) or `handle` so the server moves the bytes.", - "type": "string" - }, - "handle": { - "description": "A download handle from a prior getDownloads (a path in stdio mode, a `browserless-download://` URI in HTTP mode). The MCP server reads the stored file — works in both transports and keeps the bytes out of the conversation. Use this to re-upload a file you just downloaded.", - "type": "string" - }, - "mimeType": { - "description": "MIME type; inferred from the extension when omitted.", - "type": "string" - }, - "name": { - "description": "Filename reported to the page. Defaults to the basename of `path`, else \"file\".", - "type": "string" - }, - "path": { - "description": "Local filesystem path to read and upload. stdio (local) mode only — the MCP server reads and base64-encodes it. In HTTP mode use `handle` or `content` instead.", - "type": "string" - } - }, - "type": "object" - }, - "minItems": 1, - "type": "array" - }, - "selector": { - "description": "CSS selector of the <input type=\"file\"> element", - "type": "string" - } - }, - "required": [ - "selector", - "files" - ], - "type": "object" - } - }, - "required": [ - "method", - "params" - ], - "type": "object" - }, - { - "properties": { - "method": { - "const": "getDownloads", - "type": "string" - }, - "params": { - "default": {}, - "properties": {}, - "type": "object" - } - }, - "required": [ - "method" - ], - "type": "object" - }, - { - "properties": { - "method": { - "const": "close", - "type": "string" - }, - "params": { - "default": {}, - "properties": {}, - "type": "object" - } - }, - "required": [ - "method" - ], - "type": "object" - } - ] - }, - { - "properties": { - "method": { - "description": "The BQL method name", - "type": "string" - }, - "params": { - "additionalProperties": {}, - "default": {}, - "description": "Parameters for the method", - "propertyNames": { - "type": "string" - }, - "type": "object" - } - }, - "required": [ - "method" - ], - "type": "object" - } -]New value: +[ + { + "oneOf": [ + { + "properties": { + "method": { + "const": "goto", + "type": "string" + }, + "params": { + "properties": { + "timeout": { + "description": "Navigation timeout in milliseconds", + "type": "number" + }, + "url": { + "description": "The URL to navigate to", + "type": "string" + }, + "waitUntil": { + "description": "When to consider navigation complete. Defaults to \"domcontentloaded\". Avoid networkidle0/networkidle2 unless explicitly needed — they hang on SPAs and dynamic sites.", + "enum": [ + "load", + "domcontentloaded", + "networkidle0", + "networkidle2" + ], + "type": "string" + } + }, + "required": [ + "url" + ], + "type": "object" + } + }, + "required": [ + "method", + "params" + ], + "type": "object" + }, + { + "properties": { + "method": { + "const": "back", + "type": "string" + }, + "params": { + "default": {}, + "properties": { + "waitUntil": { + "description": "When to consider navigation complete. Defaults to \"load\".", + "enum": [ + "load", + "domcontentloaded", + "networkidle0", + "networkidle2" + ], + "type": "string" + } + }, + "type": "object" + } + }, + "required": [ + "method" + ], + "type": "object" + }, + { + "properties": { + "method": { + "const": "forward", + "type": "string" + }, + "params": { + "default": {}, + "properties": { + "waitUntil": { + "description": "When to consider navigation complete. Defaults to \"load\".", + "enum": [ + "load", + "domcontentloaded", + "networkidle0", + "networkidle2" + ], + "type": "string" + } + }, + "type": "object" + } + }, + "required": [ + "method" + ], + "type": "object" + }, + { + "properties": { + "method": { + "const": "reload", + "type": "string" + }, + "params": { + "default": {}, + "properties": { + "waitUntil": { + "description": "When to consider navigation complete. Defaults to \"load\".", + "enum": [ + "load", + "domcontentloaded", + "networkidle0", + "networkidle2" + ], + "type": "string" + } + }, + "type": "object" + } + }, + "required": [ + "method" + ], + "type": "object" + }, + { + "properties": { + "method": { + "const": "snapshot", + "type": "string" + }, + "params": { + "default": {}, + "properties": { + "full": { + "description": "Force a complete snapshot instead of a diff. Snapshots normally return only what changed since your previous one; set full:true when you no longer have that previous snapshot in context (e.g. it was summarized away) and need the entire element list again.", + "type": "boolean" + }, + "maxElements": { + "description": "Maximum number of elements to return (default 500)", + "exclusiveMinimum": 0, + "maximum": 9007199254740991, + "type": "integer" + }, + "targetId": { + "description": "Optional tab targetId to peek at without switching the active tab. Obtain via getTabs or a prior snapshot response. Omit to snapshot the active tab.", + "type": "string" + } + }, + "type": "object" + } + }, + "required": [ + "method" + ], + "type": "object" + }, + { + "properties": { + "method": { + "const": "getTabs", + "type": "string" + }, + "params": { + "default": {}, + "properties": {}, + "type": "object" + } + }, + "required": [ + "method" + ], + "type": "object" + }, + { + "properties": { + "method": { + "const": "switchTab", + "type": "string" + }, + "params": { + "properties": { + "targetId": { + "description": "The targetId of the tab to make active (from getTabs).", + "type": "string" + } + }, + "required": [ + "targetId" + ], + "type": "object" + } + }, + "required": [ + "method", + "params" + ], + "type": "object" + }, + { + "properties": { + "method": { + "const": "createTab", + "type": "string" + }, + "params": { + "default": {}, + "properties": { + "activate": { + "description": "If true (default), switch to the new tab. If false, open it in the background and leave the current tab active.", + "type": "boolean" + }, + "url": { + "description": "URL to open in the new tab. Defaults to about:blank if omitted.", + "type": "string" + }, + "waitUntil": { + "description": "When to consider navigation complete. Only applies when activate is true. Defaults to \"domcontentloaded\".", + "enum": [ + "load", + "domcontentloaded", + "networkidle0", + "networkidle2" + ], + "type": "string" + } + }, + "type": "object" + } + }, + "required": [ + "method" + ], + "type": "object" + }, + { + "properties": { + "method": { + "const": "closeTab", + "type": "string" + }, + "params": { + "properties": { + "targetId": { + "description": "The targetId of the tab to close.", + "type": "string" + } + }, + "required": [ + "targetId" + ], + "type": "object" + } + }, + "required": [ + "method", + "params" + ], + "type": "object" + }, + { + "properties": { + "method": { + "const": "click", + "type": "string" + }, + "params": { + "properties": { + "selector": { + "description": "CSS selector of the element to click. Omit when clicking by coordinate (x/y).", + "type": "string" + }, + "x": { + "description": "Vision fallback: raw screenshot-pixel X to click. Read it off a viewport (non-fullPage) screenshot and pass it as-is — the server maps screenshot pixels to the page. Provide x and y together, without selector.", + "type": "number" + }, + "y": { + "description": "Vision fallback: raw screenshot-pixel Y to click. See x.", + "type": "number" + } + }, + "type": "object" + } + }, + "required": [ + "method", + "params" + ], + "type": "object" + }, + { + "properties": { + "method": { + "const": "type", + "type": "string" + }, + "params": { + "properties": { + "selector": { + "description": "CSS selector of the input element", + "type": "string" + }, + "text": { + "description": "Text to type into the element", + "type": "string" + } + }, + "required": [ + "selector", + "text" + ], + "type": "object" + } + }, + "required": [ + "method", + "params" + ], + "type": "object" + }, + { + "properties": { + "method": { + "const": "loadSecret", + "type": "string" + }, + "params": { + "properties": { + "ref": { + "description": "The credential reference/alias to inject (e.g. an op:// reference). The secret value is resolved server-side and typed into the field — you never see it. Use this for ALL passwords and usernames from a secrets vault; never put a secret value in `type`. Also accepts a mail://<item>/otp reference that resolves the newest emailed one-time code server-side (value never surfaced); trigger the send, then call loadSecret promptly.", + "type": "string" + }, + "selector": { + "description": "CSS selector of the input to fill. If omitted, the secret is injected into the currently focused element (click/focus the field first).", + "type": "string" + } + }, + "required": [ + "ref" + ], + "type": "object" + } + }, + "required": [ + "method", + "params" + ], + "type": "object" + }, + { + "properties": { + "method": { + "const": "clearSecrets", + "description": "Re-enable screenshot, PDF, liveURL, and page-content-read captures (evaluate/html/text/querySelector/cookies) after a loadSecret login once the credential is no longer visible. Required for single-page apps that do not navigate; replay remains masked.", + "type": "string" + }, + "params": { + "additionalProperties": false, + "default": {}, + "properties": {}, + "type": "object" + } + }, + "required": [ + "method" + ], + "type": "object" + }, + { + "properties": { + "method": { + "const": "select", + "type": "string" + }, + "params": { + "properties": { + "selector": { + "description": "CSS selector of the select element", + "type": "string" + }, + "value": { + "description": "Option value to select. The snapshot lists a select's accepted values as options=[label=value, …] (bare entries have label === value) — take the value from there instead of guessing or reading the DOM with evaluate.", + "type": "string" + } + }, + "required": [ + "selector", + "value" + ], + "type": "object" + } + }, + "required": [ + "method", + "params" + ], + "type": "object" + }, + { + "properties": { + "method": { + "const": "checkbox", + "type": "string" + }, + "params": { + "properties": { + "checked": { + "description": "Desired checked state (default: toggle)", + "type": "boolean" + }, + "selector": { + "description": "CSS selector of the checkbox element", + "type": "string" + } + }, + "required": [ + "selector" + ], + "type": "object" + } + }, + "required": [ + "method", + "params" + ], + "type": "object" + }, + { + "properties": { + "method": { + "const": "hover", + "type": "string" + }, + "params": { + "properties": { + "selector": { + "description": "CSS selector of the element to hover over", + "type": "string" + } + }, + "required": [ + "selector" + ], + "type": "object" + } + }, + "required": [ + "method", + "params" + ], + "type": "object" + }, + { + "properties": { + "method": { + "const": "scroll", + "type": "string" + }, + "params": { + "default": {}, + "properties": { + "direction": { + "description": "Scroll direction. Defaults to \"down\".", + "enum": [ + "up", + "down", + "left", + "right" + ], + "type": "string" + }, + "selector": { + "description": "CSS selector of element to scroll (omit for page scroll)", + "type": "string" + } + }, + "type": "object" + } + }, + "required": [ + "method" + ], + "type": "object" + }, + { + "properties": { + "method": { + "const": "evaluate", + "type": "string" + }, + "params": { + "properties": { + "content": { + "description": "JavaScript code to execute (use IIFE syntax)", + "type": "string" + } + }, + "required": [ + "content" + ], + "type": "object" + } + }, + "required": [ + "method", + "params" + ], + "type": "object" + }, + { + "properties": { + "method": { + "const": "text", + "type": "string" + }, + "params": { + "default": {}, + "properties": { + "selector": { + "description": "CSS selector to extract text from", + "type": "string" + } + }, + "type": "object" + } + }, + "required": [ + "method" + ], + "type": "object" + }, + { + "properties": { + "method": { + "const": "html", + "type": "string" + }, + "params": { + "default": {}, + "properties": { + "selector": { + "description": "CSS selector to get HTML from", + "type": "string" + } + }, + "type": "object" + } + }, + "required": [ + "method" + ], + "type": "object" + }, + { + "properties": { + "method": { + "const": "waitForSelector", + "type": "string" + }, + "params": { + "properties": { + "selector": { + "description": "CSS selector to wait for", + "type": "string" + }, + "timeout": { + "description": "Timeout in milliseconds (recommend 5000-10000)", + "type": "number" + } + }, + "required": [ + "selector" + ], + "type": "object" + } + }, + "required": [ + "method", + "params" + ], + "type": "object" + }, + { + "properties": { + "method": { + "const": "waitForNavigation", + "type": "string" + }, + "params": { + "default": {}, + "properties": { + "timeout": { + "description": "Timeout in milliseconds (default 30000)", + "type": "number" + } + }, + "type": "object" + } + }, + "required": [ + "method" + ], + "type": "object" + }, + { + "properties": { + "method": { + "const": "waitForTimeout", + "type": "string" + }, + "params": { + "properties": { + "time": { + "description": "Time to wait in milliseconds (e.g., 3000 for 3 seconds)", + "type": "number" + } + }, + "required": [ + "time" + ], + "type": "object" + } + }, + "required": [ + "method", + "params" + ], + "type": "object" + }, + { + "properties": { + "method": { + "const": "waitForRequest", + "type": "string" + }, + "params": { + "properties": { + "method": { + "description": "HTTP method to match (e.g., \"GET\", \"POST\")", + "type": "string" + }, + "timeout": { + "description": "Timeout in milliseconds (default 30000)", + "type": "number" + }, + "url": { + "description": "URL pattern to match (glob-style, e.g., \"*api/results*\")", + "type": "string" + } + }, + "type": "object" + } + }, + "required": [ + "method", + "params" + ], + "type": "object" + }, + { + "properties": { + "method": { + "const": "waitForResponse", + "type": "string" + }, + "params": { + "properties": { + "statuses": { + "description": "HTTP status codes to match (e.g., [200, 201])", + "items": { + "type": "number" + }, + "type": "array" + }, + "timeout": { + "description": "Timeout in milliseconds (default 30000)", + "type": "number" + }, + "url": { + "description": "URL pattern to match (glob-style, e.g., \"*api/results*\")", + "type": "string" + } + }, + "type": "object" + } + }, + "required": [ + "method", + "params" + ], + "type": "object" + }, + { + "properties": { + "method": { + "const": "liveURL", + "type": "string" + }, + "params": { + "default": {}, + "properties": { + "interactable": { + "description": "Allow interaction via the live URL", + "type": "boolean" + }, + "quality": { + "description": "Image quality (1-100)", + "maximum": 100, + "minimum": 1, + "type": "integer" + }, + "resizable": { + "description": "Allow resizing the browser viewport", + "type": "boolean" + }, + "timeout": { + "description": "How long the live URL stays active (ms)", + "type": "number" + }, + "type": { + "description": "Image format for the stream", + "enum": [ + "jpeg", + "png" + ], + "type": "string" + } + }, + "type": "object" + } + }, + "required": [ + "method" + ], + "type": "object" + }, + { + "properties": { + "method": { + "const": "solve", + "type": "string" + }, + "params": { + "default": {}, + "properties": { + "timeout": { + "description": "How long to wait for the captcha to appear (ms). Default 30000. Does not bound the solver itself once a captcha is found.", + "exclusiveMinimum": 0, + "maximum": 9007199254740991, + "type": "integer" + }, + "type": { + "description": "Captcha type to solve. Omit to auto-detect.", + "enum": [ + "cloudflare", + "hcaptcha", + "recaptcha", + "recaptchaV3", + "geetest", + "normal", + "friendlyCaptcha", + "capy", + "textCaptcha", + "amazonWaf", + "dataDome", + "akamai", + "lemin", + "mtcaptcha", + "slider" + ], + "type": "string" + }, + "wait": { + "description": "Wait for the captcha to appear before solving (default true). Set false if you have already verified the widget is on screen.", + "type": "boolean" + } + }, + "type": "object" + } + }, + "required": [ + "method" + ], + "type": "object" + }, + { + "properties": { + "method": { + "const": "screenshot", + "type": "string" + }, + "params": { + "default": {}, + "properties": { + "clip": { + "description": "Region of the page to capture. Mutually exclusive with selector/fullPage.", + "properties": { + "height": { + "description": "Height of the clip, in CSS pixels (>0)", + "minimum": 1, + "type": "number" + }, + "scale": { + "description": "Scale factor of the clip (default 1, >0)", + "exclusiveMinimum": 0, + "type": "number" + }, + "width": { + "description": "Width of the clip, in CSS pixels (>0)", + "minimum": 1, + "type": "number" + }, + "x": { + "description": "X coordinate of the top-left corner, in CSS pixels", + "type": "number" + }, + "y": { + "description": "Y coordinate of the top-left corner, in CSS pixels", + "type": "number" + } + }, + "required": [ + "x", + "y", + "width", + "height" + ], + "type": "object" + }, + "fullPage": { + "description": "Capture the entire scrollable page (default false)", + "type": "boolean" + }, + "omitBackground": { + "description": "Hide default white background for transparent screenshots", + "type": "boolean" + }, + "quality": { + "description": "Image quality 0-100. Applies to jpeg/webp only.", + "maximum": 100, + "minimum": 0, + "type": "number" + }, + "selector": { + "description": "CSS selector of an element to screenshot. Mutually exclusive with fullPage/clip.", + "type": "string" + }, + "timeout": { + "description": "Timeout in milliseconds (default 30000)", + "type": "number" + }, + "toDisk": { + "description": "Save the screenshot to disk instead of returning it inline. You will NOT see the image; the response gives a reusable handle (local path in stdio, single-use GET URL over HTTP) exactly like a download — reuse it with uploadFile or hand it to the user. Use when you only need the file later, not to look at now (see file-transfers).", + "type": "boolean" + }, + "type": { + "description": "Image format. Default \"png\". Use \"jpeg\" for smaller payloads on large pages.", + "enum": [ + "jpeg", + "png", + "webp" + ], + "type": "string" + }, + "waitForImages": { + "description": "Wait for all images on the page to load before capturing", + "type": "boolean" + } + }, + "type": "object" + } + }, + "required": [ + "method" + ], + "type": "object" + }, + { + "properties": { + "method": { + "const": "uploadFile", + "type": "string" + }, + "params": { + "properties": { + "files": { + "description": "Files to attach. Combined decoded size is capped (server default 10MB, hard max 50MB).", + "items": { + "properties": { + "content": { + "description": "Base64-encoded file content. LAST RESORT — only for tiny data you already hold inline. Do NOT read a file into the conversation, and never split/reassemble base64 by hand: use `path` (stdio) or `handle` so the server moves the bytes.", + "type": "string" + }, + "handle": { + "description": "A download handle from a prior getDownloads (a path in stdio mode, a `browserless-download://` URI in HTTP mode). The MCP server reads the stored file — works in both transports and keeps the bytes out of the conversation. Use this to re-upload a file you just downloaded.", + "type": "string" + }, + "mimeType": { + "description": "MIME type; inferred from the extension when omitted.", + "type": "string" + }, + "name": { + "description": "Filename reported to the page. Defaults to the basename of `path`, else \"file\".", + "type": "string" + }, + "path": { + "description": "Local filesystem path to read and upload. stdio (local) mode only — the MCP server reads and base64-encodes it. In HTTP mode use `handle` or `content` instead.", + "type": "string" + } + }, + "type": "object" + }, + "minItems": 1, + "type": "array" + }, + "selector": { + "description": "CSS selector of the <input type=\"file\"> element", + "type": "string" + } + }, + "required": [ + "selector", + "files" + ], + "type": "object" + } + }, + "required": [ + "method", + "params" + ], + "type": "object" + }, + { + "properties": { + "method": { + "const": "getDownloads", + "type": "string" + }, + "params": { + "default": {}, + "properties": {}, + "type": "object" + } + }, + "required": [ + "method" + ], + "type": "object" + }, + { + "properties": { + "method": { + "const": "startRecording", + "type": "string" + }, + "params": { + "default": {}, + "properties": { + "height": { + "type": "number" + }, + "width": { + "type": "number" + } + }, + "type": "object" + } + }, + "required": [ + "method" + ], + "type": "object" + }, + { + "properties": { + "method": { + "const": "stopRecording", + "type": "string" + }, + "params": { + "default": {}, + "properties": {}, + "type": "object" + } + }, + "required": [ + "method" + ], + "type": "object" + }, + { + "properties": { + "method": { + "const": "close", + "type": "string" + }, + "params": { + "default": {}, + "properties": {}, + "type": "object" + } + }, + "required": [ + "method" + ], + "type": "object" + } + ] + }, + { + "properties": { + "method": { + "description": "The BQL method name", + "type": "string" + }, + "params": { + "additionalProperties": {}, + "default": {}, + "description": "Parameters for the method", + "propertyNames": { + "type": "string" + }, + "type": "object" + } + }, + "required": [ + "method" + ], + "type": "object" + } +] - added
Input schema / properties / humanlikeAdded value: +{ + "description": "Human-like cursor movement + pacing for clicks/scrolls. Improves the passive score of invisible anti-bot challenges (which weight real mouse/interaction signals). Defaults on. Forwarded as ?humanlike; read once at session creation.", + "type": "boolean" +} - added
Input schema / properties / osAdded value: +{ + "description": "Desktop OS to spoof for the stealth fingerprint (navigator.platform, UA, UA-CH client hints, GPU/font signals). Defaults to \"windows\" so the agent presents a coherent, low-risk desktop identity instead of its native Linux (a Chrome-masked UA over \"Linux x86_64\" is a bot tell that anti-bot checks flag). Forwarded to the browser as ?emulationOs; read once at session creation.", + "enum": [ + "windows", + "macos", + "linux" + ], + "type": "string" +} - added
Input schema / properties / recordAdded value: +{ + "description": "Arm screen-video recording at session launch. Use `startRecording` / `stopRecording`; stopping returns a single-use WebM link, never bytes.", + "type": "boolean" +}
1 tool update
v1.24.0- Changed
browserless_agent3 fields changed- added
Input schema / properties / allowedDomainsAdded value: +{ + "description": "Origins where a resolved secret may be filled, e.g. [\"https://gymshark.com\"]. Only meaningful with `integrationId`. Defaults to the integration's configured origins; set it to fill on additional sites. loadSecret is refused on any origin not covered here.", + "items": { + "minLength": 1, + "type": "string" + }, + "type": "array" +} - changed
Input schema / properties / commands / items / anyOfPrevious value: -[ - { - "oneOf": [ - { - "properties": { - "method": { - "const": "goto", - "type": "string" - }, - "params": { - "properties": { - "timeout": { - "description": "Navigation timeout in milliseconds", - "type": "number" - }, - "url": { - "description": "The URL to navigate to", - "type": "string" - }, - "waitUntil": { - "description": "When to consider navigation complete. Defaults to \"domcontentloaded\". Avoid networkidle0/networkidle2 unless explicitly needed — they hang on SPAs and dynamic sites.", - "enum": [ - "load", - "domcontentloaded", - "networkidle0", - "networkidle2" - ], - "type": "string" - } - }, - "required": [ - "url" - ], - "type": "object" - } - }, - "required": [ - "method", - "params" - ], - "type": "object" - }, - { - "properties": { - "method": { - "const": "back", - "type": "string" - }, - "params": { - "default": {}, - "properties": { - "waitUntil": { - "description": "When to consider navigation complete. Defaults to \"load\".", - "enum": [ - "load", - "domcontentloaded", - "networkidle0", - "networkidle2" - ], - "type": "string" - } - }, - "type": "object" - } - }, - "required": [ - "method" - ], - "type": "object" - }, - { - "properties": { - "method": { - "const": "forward", - "type": "string" - }, - "params": { - "default": {}, - "properties": { - "waitUntil": { - "description": "When to consider navigation complete. Defaults to \"load\".", - "enum": [ - "load", - "domcontentloaded", - "networkidle0", - "networkidle2" - ], - "type": "string" - } - }, - "type": "object" - } - }, - "required": [ - "method" - ], - "type": "object" - }, - { - "properties": { - "method": { - "const": "reload", - "type": "string" - }, - "params": { - "default": {}, - "properties": { - "waitUntil": { - "description": "When to consider navigation complete. Defaults to \"load\".", - "enum": [ - "load", - "domcontentloaded", - "networkidle0", - "networkidle2" - ], - "type": "string" - } - }, - "type": "object" - } - }, - "required": [ - "method" - ], - "type": "object" - }, - { - "properties": { - "method": { - "const": "snapshot", - "type": "string" - }, - "params": { - "default": {}, - "properties": { - "full": { - "description": "Force a complete snapshot instead of a diff. Snapshots normally return only what changed since your previous one; set full:true when you no longer have that previous snapshot in context (e.g. it was summarized away) and need the entire element list again.", - "type": "boolean" - }, - "maxElements": { - "description": "Maximum number of elements to return (default 500)", - "exclusiveMinimum": 0, - "maximum": 9007199254740991, - "type": "integer" - }, - "targetId": { - "description": "Optional tab targetId to peek at without switching the active tab. Obtain via getTabs or a prior snapshot response. Omit to snapshot the active tab.", - "type": "string" - } - }, - "type": "object" - } - }, - "required": [ - "method" - ], - "type": "object" - }, - { - "properties": { - "method": { - "const": "getTabs", - "type": "string" - }, - "params": { - "default": {}, - "properties": {}, - "type": "object" - } - }, - "required": [ - "method" - ], - "type": "object" - }, - { - "properties": { - "method": { - "const": "switchTab", - "type": "string" - }, - "params": { - "properties": { - "targetId": { - "description": "The targetId of the tab to make active (from getTabs).", - "type": "string" - } - }, - "required": [ - "targetId" - ], - "type": "object" - } - }, - "required": [ - "method", - "params" - ], - "type": "object" - }, - { - "properties": { - "method": { - "const": "createTab", - "type": "string" - }, - "params": { - "default": {}, - "properties": { - "activate": { - "description": "If true (default), switch to the new tab. If false, open it in the background and leave the current tab active.", - "type": "boolean" - }, - "url": { - "description": "URL to open in the new tab. Defaults to about:blank if omitted.", - "type": "string" - }, - "waitUntil": { - "description": "When to consider navigation complete. Only applies when activate is true. Defaults to \"domcontentloaded\".", - "enum": [ - "load", - "domcontentloaded", - "networkidle0", - "networkidle2" - ], - "type": "string" - } - }, - "type": "object" - } - }, - "required": [ - "method" - ], - "type": "object" - }, - { - "properties": { - "method": { - "const": "closeTab", - "type": "string" - }, - "params": { - "properties": { - "targetId": { - "description": "The targetId of the tab to close.", - "type": "string" - } - }, - "required": [ - "targetId" - ], - "type": "object" - } - }, - "required": [ - "method", - "params" - ], - "type": "object" - }, - { - "properties": { - "method": { - "const": "click", - "type": "string" - }, - "params": { - "properties": { - "selector": { - "description": "CSS selector of the element to click", - "type": "string" - } - }, - "required": [ - "selector" - ], - "type": "object" - } - }, - "required": [ - "method", - "params" - ], - "type": "object" - }, - { - "properties": { - "method": { - "const": "type", - "type": "string" - }, - "params": { - "properties": { - "selector": { - "description": "CSS selector of the input element", - "type": "string" - }, - "text": { - "description": "Text to type into the element", - "type": "string" - } - }, - "required": [ - "selector", - "text" - ], - "type": "object" - } - }, - "required": [ - "method", - "params" - ], - "type": "object" - }, - { - "properties": { - "method": { - "const": "loadSecret", - "type": "string" - }, - "params": { - "properties": { - "ref": { - "description": "The credential reference/alias to inject (e.g. an op:// reference). The secret value is resolved server-side and typed into the field — you never see it. Use this for ALL passwords and usernames from a secrets vault; never put a secret value in `type`.", - "type": "string" - }, - "selector": { - "description": "CSS selector of the input to fill. If omitted, the secret is injected into the currently focused element (click/focus the field first).", - "type": "string" - } - }, - "required": [ - "ref" - ], - "type": "object" - } - }, - "required": [ - "method", - "params" - ], - "type": "object" - }, - { - "properties": { - "method": { - "const": "select", - "type": "string" - }, - "params": { - "properties": { - "selector": { - "description": "CSS selector of the select element", - "type": "string" - }, - "value": { - "description": "Option value to select. The snapshot lists a select's accepted values as options=[label=value, …] (bare entries have label === value) — take the value from there instead of guessing or reading the DOM with evaluate.", - "type": "string" - } - }, - "required": [ - "selector", - "value" - ], - "type": "object" - } - }, - "required": [ - "method", - "params" - ], - "type": "object" - }, - { - "properties": { - "method": { - "const": "checkbox", - "type": "string" - }, - "params": { - "properties": { - "checked": { - "description": "Desired checked state (default: toggle)", - "type": "boolean" - }, - "selector": { - "description": "CSS selector of the checkbox element", - "type": "string" - } - }, - "required": [ - "selector" - ], - "type": "object" - } - }, - "required": [ - "method", - "params" - ], - "type": "object" - }, - { - "properties": { - "method": { - "const": "hover", - "type": "string" - }, - "params": { - "properties": { - "selector": { - "description": "CSS selector of the element to hover over", - "type": "string" - } - }, - "required": [ - "selector" - ], - "type": "object" - } - }, - "required": [ - "method", - "params" - ], - "type": "object" - }, - { - "properties": { - "method": { - "const": "scroll", - "type": "string" - }, - "params": { - "default": {}, - "properties": { - "direction": { - "description": "Scroll direction. Defaults to \"down\".", - "enum": [ - "up", - "down", - "left", - "right" - ], - "type": "string" - }, - "selector": { - "description": "CSS selector of element to scroll (omit for page scroll)", - "type": "string" - } - }, - "type": "object" - } - }, - "required": [ - "method" - ], - "type": "object" - }, - { - "properties": { - "method": { - "const": "evaluate", - "type": "string" - }, - "params": { - "properties": { - "content": { - "description": "JavaScript code to execute (use IIFE syntax)", - "type": "string" - } - }, - "required": [ - "content" - ], - "type": "object" - } - }, - "required": [ - "method", - "params" - ], - "type": "object" - }, - { - "properties": { - "method": { - "const": "text", - "type": "string" - }, - "params": { - "default": {}, - "properties": { - "selector": { - "description": "CSS selector to extract text from", - "type": "string" - } - }, - "type": "object" - } - }, - "required": [ - "method" - ], - "type": "object" - }, - { - "properties": { - "method": { - "const": "html", - "type": "string" - }, - "params": { - "default": {}, - "properties": { - "selector": { - "description": "CSS selector to get HTML from", - "type": "string" - } - }, - "type": "object" - } - }, - "required": [ - "method" - ], - "type": "object" - }, - { - "properties": { - "method": { - "const": "waitForSelector", - "type": "string" - }, - "params": { - "properties": { - "selector": { - "description": "CSS selector to wait for", - "type": "string" - }, - "timeout": { - "description": "Timeout in milliseconds (recommend 5000-10000)", - "type": "number" - } - }, - "required": [ - "selector" - ], - "type": "object" - } - }, - "required": [ - "method", - "params" - ], - "type": "object" - }, - { - "properties": { - "method": { - "const": "waitForNavigation", - "type": "string" - }, - "params": { - "default": {}, - "properties": { - "timeout": { - "description": "Timeout in milliseconds (default 30000)", - "type": "number" - } - }, - "type": "object" - } - }, - "required": [ - "method" - ], - "type": "object" - }, - { - "properties": { - "method": { - "const": "waitForTimeout", - "type": "string" - }, - "params": { - "properties": { - "time": { - "description": "Time to wait in milliseconds (e.g., 3000 for 3 seconds)", - "type": "number" - } - }, - "required": [ - "time" - ], - "type": "object" - } - }, - "required": [ - "method", - "params" - ], - "type": "object" - }, - { - "properties": { - "method": { - "const": "waitForRequest", - "type": "string" - }, - "params": { - "properties": { - "method": { - "description": "HTTP method to match (e.g., \"GET\", \"POST\")", - "type": "string" - }, - "timeout": { - "description": "Timeout in milliseconds (default 30000)", - "type": "number" - }, - "url": { - "description": "URL pattern to match (glob-style, e.g., \"*api/results*\")", - "type": "string" - } - }, - "type": "object" - } - }, - "required": [ - "method", - "params" - ], - "type": "object" - }, - { - "properties": { - "method": { - "const": "waitForResponse", - "type": "string" - }, - "params": { - "properties": { - "statuses": { - "description": "HTTP status codes to match (e.g., [200, 201])", - "items": { - "type": "number" - }, - "type": "array" - }, - "timeout": { - "description": "Timeout in milliseconds (default 30000)", - "type": "number" - }, - "url": { - "description": "URL pattern to match (glob-style, e.g., \"*api/results*\")", - "type": "string" - } - }, - "type": "object" - } - }, - "required": [ - "method", - "params" - ], - "type": "object" - }, - { - "properties": { - "method": { - "const": "liveURL", - "type": "string" - }, - "params": { - "default": {}, - "properties": { - "interactable": { - "description": "Allow interaction via the live URL", - "type": "boolean" - }, - "quality": { - "description": "Image quality (1-100)", - "maximum": 100, - "minimum": 1, - "type": "integer" - }, - "resizable": { - "description": "Allow resizing the browser viewport", - "type": "boolean" - }, - "timeout": { - "description": "How long the live URL stays active (ms)", - "type": "number" - }, - "type": { - "description": "Image format for the stream", - "enum": [ - "jpeg", - "png" - ], - "type": "string" - } - }, - "type": "object" - } - }, - "required": [ - "method" - ], - "type": "object" - }, - { - "properties": { - "method": { - "const": "solve", - "type": "string" - }, - "params": { - "default": {}, - "properties": { - "timeout": { - "description": "How long to wait for the captcha to appear (ms). Default 30000. Does not bound the solver itself once a captcha is found.", - "exclusiveMinimum": 0, - "maximum": 9007199254740991, - "type": "integer" - }, - "type": { - "description": "Captcha type to solve. Omit to auto-detect.", - "enum": [ - "cloudflare", - "hcaptcha", - "recaptcha", - "recaptchaV3", - "geetest", - "normal", - "friendlyCaptcha", - "capy", - "textCaptcha", - "amazonWaf", - "dataDome", - "akamai", - "lemin", - "mtcaptcha", - "slider" - ], - "type": "string" - }, - "wait": { - "description": "Wait for the captcha to appear before solving (default true). Set false if you have already verified the widget is on screen.", - "type": "boolean" - } - }, - "type": "object" - } - }, - "required": [ - "method" - ], - "type": "object" - }, - { - "properties": { - "method": { - "const": "screenshot", - "type": "string" - }, - "params": { - "default": {}, - "properties": { - "clip": { - "description": "Region of the page to capture. Mutually exclusive with selector/fullPage.", - "properties": { - "height": { - "description": "Height of the clip, in CSS pixels (>0)", - "minimum": 1, - "type": "number" - }, - "scale": { - "description": "Scale factor of the clip (default 1, >0)", - "exclusiveMinimum": 0, - "type": "number" - }, - "width": { - "description": "Width of the clip, in CSS pixels (>0)", - "minimum": 1, - "type": "number" - }, - "x": { - "description": "X coordinate of the top-left corner, in CSS pixels", - "type": "number" - }, - "y": { - "description": "Y coordinate of the top-left corner, in CSS pixels", - "type": "number" - } - }, - "required": [ - "x", - "y", - "width", - "height" - ], - "type": "object" - }, - "fullPage": { - "description": "Capture the entire scrollable page (default false)", - "type": "boolean" - }, - "omitBackground": { - "description": "Hide default white background for transparent screenshots", - "type": "boolean" - }, - "quality": { - "description": "Image quality 0-100. Applies to jpeg/webp only.", - "maximum": 100, - "minimum": 0, - "type": "number" - }, - "selector": { - "description": "CSS selector of an element to screenshot. Mutually exclusive with fullPage/clip.", - "type": "string" - }, - "timeout": { - "description": "Timeout in milliseconds (default 30000)", - "type": "number" - }, - "toDisk": { - "description": "Save the screenshot to disk instead of returning it inline. You will NOT see the image; the response gives a reusable handle (local path in stdio, single-use GET URL over HTTP) exactly like a download — reuse it with uploadFile or hand it to the user. Use when you only need the file later, not to look at now (see file-transfers).", - "type": "boolean" - }, - "type": { - "description": "Image format. Default \"png\". Use \"jpeg\" for smaller payloads on large pages.", - "enum": [ - "jpeg", - "png", - "webp" - ], - "type": "string" - }, - "waitForImages": { - "description": "Wait for all images on the page to load before capturing", - "type": "boolean" - } - }, - "type": "object" - } - }, - "required": [ - "method" - ], - "type": "object" - }, - { - "properties": { - "method": { - "const": "uploadFile", - "type": "string" - }, - "params": { - "properties": { - "files": { - "description": "Files to attach. Combined decoded size is capped (server default 10MB, hard max 50MB).", - "items": { - "properties": { - "content": { - "description": "Base64-encoded file content. LAST RESORT — only for tiny data you already hold inline. Do NOT read a file into the conversation, and never split/reassemble base64 by hand: use `path` (stdio) or `handle` so the server moves the bytes.", - "type": "string" - }, - "handle": { - "description": "A download handle from a prior getDownloads (a path in stdio mode, a `browserless-download://` URI in HTTP mode). The MCP server reads the stored file — works in both transports and keeps the bytes out of the conversation. Use this to re-upload a file you just downloaded.", - "type": "string" - }, - "mimeType": { - "description": "MIME type; inferred from the extension when omitted.", - "type": "string" - }, - "name": { - "description": "Filename reported to the page. Defaults to the basename of `path`, else \"file\".", - "type": "string" - }, - "path": { - "description": "Local filesystem path to read and upload. stdio (local) mode only — the MCP server reads and base64-encodes it. In HTTP mode use `handle` or `content` instead.", - "type": "string" - } - }, - "type": "object" - }, - "minItems": 1, - "type": "array" - }, - "selector": { - "description": "CSS selector of the <input type=\"file\"> element", - "type": "string" - } - }, - "required": [ - "selector", - "files" - ], - "type": "object" - } - }, - "required": [ - "method", - "params" - ], - "type": "object" - }, - { - "properties": { - "method": { - "const": "getDownloads", - "type": "string" - }, - "params": { - "default": {}, - "properties": {}, - "type": "object" - } - }, - "required": [ - "method" - ], - "type": "object" - }, - { - "properties": { - "method": { - "const": "close", - "type": "string" - }, - "params": { - "default": {}, - "properties": {}, - "type": "object" - } - }, - "required": [ - "method" - ], - "type": "object" - } - ] - }, - { - "properties": { - "method": { - "description": "The BQL method name", - "type": "string" - }, - "params": { - "additionalProperties": {}, - "default": {}, - "description": "Parameters for the method", - "propertyNames": { - "type": "string" - }, - "type": "object" - } - }, - "required": [ - "method" - ], - "type": "object" - } -]New value: +[ + { + "oneOf": [ + { + "properties": { + "method": { + "const": "goto", + "type": "string" + }, + "params": { + "properties": { + "timeout": { + "description": "Navigation timeout in milliseconds", + "type": "number" + }, + "url": { + "description": "The URL to navigate to", + "type": "string" + }, + "waitUntil": { + "description": "When to consider navigation complete. Defaults to \"domcontentloaded\". Avoid networkidle0/networkidle2 unless explicitly needed — they hang on SPAs and dynamic sites.", + "enum": [ + "load", + "domcontentloaded", + "networkidle0", + "networkidle2" + ], + "type": "string" + } + }, + "required": [ + "url" + ], + "type": "object" + } + }, + "required": [ + "method", + "params" + ], + "type": "object" + }, + { + "properties": { + "method": { + "const": "back", + "type": "string" + }, + "params": { + "default": {}, + "properties": { + "waitUntil": { + "description": "When to consider navigation complete. Defaults to \"load\".", + "enum": [ + "load", + "domcontentloaded", + "networkidle0", + "networkidle2" + ], + "type": "string" + } + }, + "type": "object" + } + }, + "required": [ + "method" + ], + "type": "object" + }, + { + "properties": { + "method": { + "const": "forward", + "type": "string" + }, + "params": { + "default": {}, + "properties": { + "waitUntil": { + "description": "When to consider navigation complete. Defaults to \"load\".", + "enum": [ + "load", + "domcontentloaded", + "networkidle0", + "networkidle2" + ], + "type": "string" + } + }, + "type": "object" + } + }, + "required": [ + "method" + ], + "type": "object" + }, + { + "properties": { + "method": { + "const": "reload", + "type": "string" + }, + "params": { + "default": {}, + "properties": { + "waitUntil": { + "description": "When to consider navigation complete. Defaults to \"load\".", + "enum": [ + "load", + "domcontentloaded", + "networkidle0", + "networkidle2" + ], + "type": "string" + } + }, + "type": "object" + } + }, + "required": [ + "method" + ], + "type": "object" + }, + { + "properties": { + "method": { + "const": "snapshot", + "type": "string" + }, + "params": { + "default": {}, + "properties": { + "full": { + "description": "Force a complete snapshot instead of a diff. Snapshots normally return only what changed since your previous one; set full:true when you no longer have that previous snapshot in context (e.g. it was summarized away) and need the entire element list again.", + "type": "boolean" + }, + "maxElements": { + "description": "Maximum number of elements to return (default 500)", + "exclusiveMinimum": 0, + "maximum": 9007199254740991, + "type": "integer" + }, + "targetId": { + "description": "Optional tab targetId to peek at without switching the active tab. Obtain via getTabs or a prior snapshot response. Omit to snapshot the active tab.", + "type": "string" + } + }, + "type": "object" + } + }, + "required": [ + "method" + ], + "type": "object" + }, + { + "properties": { + "method": { + "const": "getTabs", + "type": "string" + }, + "params": { + "default": {}, + "properties": {}, + "type": "object" + } + }, + "required": [ + "method" + ], + "type": "object" + }, + { + "properties": { + "method": { + "const": "switchTab", + "type": "string" + }, + "params": { + "properties": { + "targetId": { + "description": "The targetId of the tab to make active (from getTabs).", + "type": "string" + } + }, + "required": [ + "targetId" + ], + "type": "object" + } + }, + "required": [ + "method", + "params" + ], + "type": "object" + }, + { + "properties": { + "method": { + "const": "createTab", + "type": "string" + }, + "params": { + "default": {}, + "properties": { + "activate": { + "description": "If true (default), switch to the new tab. If false, open it in the background and leave the current tab active.", + "type": "boolean" + }, + "url": { + "description": "URL to open in the new tab. Defaults to about:blank if omitted.", + "type": "string" + }, + "waitUntil": { + "description": "When to consider navigation complete. Only applies when activate is true. Defaults to \"domcontentloaded\".", + "enum": [ + "load", + "domcontentloaded", + "networkidle0", + "networkidle2" + ], + "type": "string" + } + }, + "type": "object" + } + }, + "required": [ + "method" + ], + "type": "object" + }, + { + "properties": { + "method": { + "const": "closeTab", + "type": "string" + }, + "params": { + "properties": { + "targetId": { + "description": "The targetId of the tab to close.", + "type": "string" + } + }, + "required": [ + "targetId" + ], + "type": "object" + } + }, + "required": [ + "method", + "params" + ], + "type": "object" + }, + { + "properties": { + "method": { + "const": "click", + "type": "string" + }, + "params": { + "properties": { + "selector": { + "description": "CSS selector of the element to click", + "type": "string" + } + }, + "required": [ + "selector" + ], + "type": "object" + } + }, + "required": [ + "method", + "params" + ], + "type": "object" + }, + { + "properties": { + "method": { + "const": "type", + "type": "string" + }, + "params": { + "properties": { + "selector": { + "description": "CSS selector of the input element", + "type": "string" + }, + "text": { + "description": "Text to type into the element", + "type": "string" + } + }, + "required": [ + "selector", + "text" + ], + "type": "object" + } + }, + "required": [ + "method", + "params" + ], + "type": "object" + }, + { + "properties": { + "method": { + "const": "loadSecret", + "type": "string" + }, + "params": { + "properties": { + "ref": { + "description": "The credential reference/alias to inject (e.g. an op:// reference). The secret value is resolved server-side and typed into the field — you never see it. Use this for ALL passwords and usernames from a secrets vault; never put a secret value in `type`. Also accepts a mail://<item>/otp reference that resolves the newest emailed one-time code server-side (value never surfaced); trigger the send, then call loadSecret promptly.", + "type": "string" + }, + "selector": { + "description": "CSS selector of the input to fill. If omitted, the secret is injected into the currently focused element (click/focus the field first).", + "type": "string" + } + }, + "required": [ + "ref" + ], + "type": "object" + } + }, + "required": [ + "method", + "params" + ], + "type": "object" + }, + { + "properties": { + "method": { + "const": "select", + "type": "string" + }, + "params": { + "properties": { + "selector": { + "description": "CSS selector of the select element", + "type": "string" + }, + "value": { + "description": "Option value to select. The snapshot lists a select's accepted values as options=[label=value, …] (bare entries have label === value) — take the value from there instead of guessing or reading the DOM with evaluate.", + "type": "string" + } + }, + "required": [ + "selector", + "value" + ], + "type": "object" + } + }, + "required": [ + "method", + "params" + ], + "type": "object" + }, + { + "properties": { + "method": { + "const": "checkbox", + "type": "string" + }, + "params": { + "properties": { + "checked": { + "description": "Desired checked state (default: toggle)", + "type": "boolean" + }, + "selector": { + "description": "CSS selector of the checkbox element", + "type": "string" + } + }, + "required": [ + "selector" + ], + "type": "object" + } + }, + "required": [ + "method", + "params" + ], + "type": "object" + }, + { + "properties": { + "method": { + "const": "hover", + "type": "string" + }, + "params": { + "properties": { + "selector": { + "description": "CSS selector of the element to hover over", + "type": "string" + } + }, + "required": [ + "selector" + ], + "type": "object" + } + }, + "required": [ + "method", + "params" + ], + "type": "object" + }, + { + "properties": { + "method": { + "const": "scroll", + "type": "string" + }, + "params": { + "default": {}, + "properties": { + "direction": { + "description": "Scroll direction. Defaults to \"down\".", + "enum": [ + "up", + "down", + "left", + "right" + ], + "type": "string" + }, + "selector": { + "description": "CSS selector of element to scroll (omit for page scroll)", + "type": "string" + } + }, + "type": "object" + } + }, + "required": [ + "method" + ], + "type": "object" + }, + { + "properties": { + "method": { + "const": "evaluate", + "type": "string" + }, + "params": { + "properties": { + "content": { + "description": "JavaScript code to execute (use IIFE syntax)", + "type": "string" + } + }, + "required": [ + "content" + ], + "type": "object" + } + }, + "required": [ + "method", + "params" + ], + "type": "object" + }, + { + "properties": { + "method": { + "const": "text", + "type": "string" + }, + "params": { + "default": {}, + "properties": { + "selector": { + "description": "CSS selector to extract text from", + "type": "string" + } + }, + "type": "object" + } + }, + "required": [ + "method" + ], + "type": "object" + }, + { + "properties": { + "method": { + "const": "html", + "type": "string" + }, + "params": { + "default": {}, + "properties": { + "selector": { + "description": "CSS selector to get HTML from", + "type": "string" + } + }, + "type": "object" + } + }, + "required": [ + "method" + ], + "type": "object" + }, + { + "properties": { + "method": { + "const": "waitForSelector", + "type": "string" + }, + "params": { + "properties": { + "selector": { + "description": "CSS selector to wait for", + "type": "string" + }, + "timeout": { + "description": "Timeout in milliseconds (recommend 5000-10000)", + "type": "number" + } + }, + "required": [ + "selector" + ], + "type": "object" + } + }, + "required": [ + "method", + "params" + ], + "type": "object" + }, + { + "properties": { + "method": { + "const": "waitForNavigation", + "type": "string" + }, + "params": { + "default": {}, + "properties": { + "timeout": { + "description": "Timeout in milliseconds (default 30000)", + "type": "number" + } + }, + "type": "object" + } + }, + "required": [ + "method" + ], + "type": "object" + }, + { + "properties": { + "method": { + "const": "waitForTimeout", + "type": "string" + }, + "params": { + "properties": { + "time": { + "description": "Time to wait in milliseconds (e.g., 3000 for 3 seconds)", + "type": "number" + } + }, + "required": [ + "time" + ], + "type": "object" + } + }, + "required": [ + "method", + "params" + ], + "type": "object" + }, + { + "properties": { + "method": { + "const": "waitForRequest", + "type": "string" + }, + "params": { + "properties": { + "method": { + "description": "HTTP method to match (e.g., \"GET\", \"POST\")", + "type": "string" + }, + "timeout": { + "description": "Timeout in milliseconds (default 30000)", + "type": "number" + }, + "url": { + "description": "URL pattern to match (glob-style, e.g., \"*api/results*\")", + "type": "string" + } + }, + "type": "object" + } + }, + "required": [ + "method", + "params" + ], + "type": "object" + }, + { + "properties": { + "method": { + "const": "waitForResponse", + "type": "string" + }, + "params": { + "properties": { + "statuses": { + "description": "HTTP status codes to match (e.g., [200, 201])", + "items": { + "type": "number" + }, + "type": "array" + }, + "timeout": { + "description": "Timeout in milliseconds (default 30000)", + "type": "number" + }, + "url": { + "description": "URL pattern to match (glob-style, e.g., \"*api/results*\")", + "type": "string" + } + }, + "type": "object" + } + }, + "required": [ + "method", + "params" + ], + "type": "object" + }, + { + "properties": { + "method": { + "const": "liveURL", + "type": "string" + }, + "params": { + "default": {}, + "properties": { + "interactable": { + "description": "Allow interaction via the live URL", + "type": "boolean" + }, + "quality": { + "description": "Image quality (1-100)", + "maximum": 100, + "minimum": 1, + "type": "integer" + }, + "resizable": { + "description": "Allow resizing the browser viewport", + "type": "boolean" + }, + "timeout": { + "description": "How long the live URL stays active (ms)", + "type": "number" + }, + "type": { + "description": "Image format for the stream", + "enum": [ + "jpeg", + "png" + ], + "type": "string" + } + }, + "type": "object" + } + }, + "required": [ + "method" + ], + "type": "object" + }, + { + "properties": { + "method": { + "const": "solve", + "type": "string" + }, + "params": { + "default": {}, + "properties": { + "timeout": { + "description": "How long to wait for the captcha to appear (ms). Default 30000. Does not bound the solver itself once a captcha is found.", + "exclusiveMinimum": 0, + "maximum": 9007199254740991, + "type": "integer" + }, + "type": { + "description": "Captcha type to solve. Omit to auto-detect.", + "enum": [ + "cloudflare", + "hcaptcha", + "recaptcha", + "recaptchaV3", + "geetest", + "normal", + "friendlyCaptcha", + "capy", + "textCaptcha", + "amazonWaf", + "dataDome", + "akamai", + "lemin", + "mtcaptcha", + "slider" + ], + "type": "string" + }, + "wait": { + "description": "Wait for the captcha to appear before solving (default true). Set false if you have already verified the widget is on screen.", + "type": "boolean" + } + }, + "type": "object" + } + }, + "required": [ + "method" + ], + "type": "object" + }, + { + "properties": { + "method": { + "const": "screenshot", + "type": "string" + }, + "params": { + "default": {}, + "properties": { + "clip": { + "description": "Region of the page to capture. Mutually exclusive with selector/fullPage.", + "properties": { + "height": { + "description": "Height of the clip, in CSS pixels (>0)", + "minimum": 1, + "type": "number" + }, + "scale": { + "description": "Scale factor of the clip (default 1, >0)", + "exclusiveMinimum": 0, + "type": "number" + }, + "width": { + "description": "Width of the clip, in CSS pixels (>0)", + "minimum": 1, + "type": "number" + }, + "x": { + "description": "X coordinate of the top-left corner, in CSS pixels", + "type": "number" + }, + "y": { + "description": "Y coordinate of the top-left corner, in CSS pixels", + "type": "number" + } + }, + "required": [ + "x", + "y", + "width", + "height" + ], + "type": "object" + }, + "fullPage": { + "description": "Capture the entire scrollable page (default false)", + "type": "boolean" + }, + "omitBackground": { + "description": "Hide default white background for transparent screenshots", + "type": "boolean" + }, + "quality": { + "description": "Image quality 0-100. Applies to jpeg/webp only.", + "maximum": 100, + "minimum": 0, + "type": "number" + }, + "selector": { + "description": "CSS selector of an element to screenshot. Mutually exclusive with fullPage/clip.", + "type": "string" + }, + "timeout": { + "description": "Timeout in milliseconds (default 30000)", + "type": "number" + }, + "toDisk": { + "description": "Save the screenshot to disk instead of returning it inline. You will NOT see the image; the response gives a reusable handle (local path in stdio, single-use GET URL over HTTP) exactly like a download — reuse it with uploadFile or hand it to the user. Use when you only need the file later, not to look at now (see file-transfers).", + "type": "boolean" + }, + "type": { + "description": "Image format. Default \"png\". Use \"jpeg\" for smaller payloads on large pages.", + "enum": [ + "jpeg", + "png", + "webp" + ], + "type": "string" + }, + "waitForImages": { + "description": "Wait for all images on the page to load before capturing", + "type": "boolean" + } + }, + "type": "object" + } + }, + "required": [ + "method" + ], + "type": "object" + }, + { + "properties": { + "method": { + "const": "uploadFile", + "type": "string" + }, + "params": { + "properties": { + "files": { + "description": "Files to attach. Combined decoded size is capped (server default 10MB, hard max 50MB).", + "items": { + "properties": { + "content": { + "description": "Base64-encoded file content. LAST RESORT — only for tiny data you already hold inline. Do NOT read a file into the conversation, and never split/reassemble base64 by hand: use `path` (stdio) or `handle` so the server moves the bytes.", + "type": "string" + }, + "handle": { + "description": "A download handle from a prior getDownloads (a path in stdio mode, a `browserless-download://` URI in HTTP mode). The MCP server reads the stored file — works in both transports and keeps the bytes out of the conversation. Use this to re-upload a file you just downloaded.", + "type": "string" + }, + "mimeType": { + "description": "MIME type; inferred from the extension when omitted.", + "type": "string" + }, + "name": { + "description": "Filename reported to the page. Defaults to the basename of `path`, else \"file\".", + "type": "string" + }, + "path": { + "description": "Local filesystem path to read and upload. stdio (local) mode only — the MCP server reads and base64-encodes it. In HTTP mode use `handle` or `content` instead.", + "type": "string" + } + }, + "type": "object" + }, + "minItems": 1, + "type": "array" + }, + "selector": { + "description": "CSS selector of the <input type=\"file\"> element", + "type": "string" + } + }, + "required": [ + "selector", + "files" + ], + "type": "object" + } + }, + "required": [ + "method", + "params" + ], + "type": "object" + }, + { + "properties": { + "method": { + "const": "getDownloads", + "type": "string" + }, + "params": { + "default": {}, + "properties": {}, + "type": "object" + } + }, + "required": [ + "method" + ], + "type": "object" + }, + { + "properties": { + "method": { + "const": "close", + "type": "string" + }, + "params": { + "default": {}, + "properties": {}, + "type": "object" + } + }, + "required": [ + "method" + ], + "type": "object" + } + ] + }, + { + "properties": { + "method": { + "description": "The BQL method name", + "type": "string" + }, + "params": { + "additionalProperties": {}, + "default": {}, + "description": "Parameters for the method", + "propertyNames": { + "type": "string" + }, + "type": "object" + } + }, + "required": [ + "method" + ], + "type": "object" + } +] - added
Input schema / properties / integrationIdAdded value: +{ + "description": "Optional 1Password integration id (e.g. \"op_int_…\") to bind to the agent session so `loadSecret` can resolve `op://vault/item/field` references. Find it via GET /integrations/onepassword. Bind it on EVERY call in a multi-call flow (like `profile`); a call that omits it runs with no vault bound and `loadSecret` returns CredentialNotResolved. Pair with `allowedDomains` to permit filling on the target sites.", + "minLength": 1, + "type": "string" +}
4 tool updates
v1.22.1- Added
browserless_account - Added
browserless_logs - Added
browserless_sessions - Added
browserless_usage
1 tool update
v1.20.0- Changed
browserless_agent2 fields changed- changed
Input schema / properties / commands / items / anyOfPrevious value: -[ - { - "oneOf": [ - { - "properties": { - "method": { - "const": "goto", - "type": "string" - }, - "params": { - "properties": { - "timeout": { - "description": "Navigation timeout in milliseconds", - "type": "number" - }, - "url": { - "description": "The URL to navigate to", - "type": "string" - }, - "waitUntil": { - "description": "When to consider navigation complete. Defaults to \"domcontentloaded\". Avoid networkidle0/networkidle2 unless explicitly needed — they hang on SPAs and dynamic sites.", - "enum": [ - "load", - "domcontentloaded", - "networkidle0", - "networkidle2" - ], - "type": "string" - } - }, - "required": [ - "url" - ], - "type": "object" - } - }, - "required": [ - "method", - "params" - ], - "type": "object" - }, - { - "properties": { - "method": { - "const": "back", - "type": "string" - }, - "params": { - "default": {}, - "properties": { - "waitUntil": { - "description": "When to consider navigation complete. Defaults to \"load\".", - "enum": [ - "load", - "domcontentloaded", - "networkidle0", - "networkidle2" - ], - "type": "string" - } - }, - "type": "object" - } - }, - "required": [ - "method" - ], - "type": "object" - }, - { - "properties": { - "method": { - "const": "forward", - "type": "string" - }, - "params": { - "default": {}, - "properties": { - "waitUntil": { - "description": "When to consider navigation complete. Defaults to \"load\".", - "enum": [ - "load", - "domcontentloaded", - "networkidle0", - "networkidle2" - ], - "type": "string" - } - }, - "type": "object" - } - }, - "required": [ - "method" - ], - "type": "object" - }, - { - "properties": { - "method": { - "const": "reload", - "type": "string" - }, - "params": { - "default": {}, - "properties": { - "waitUntil": { - "description": "When to consider navigation complete. Defaults to \"load\".", - "enum": [ - "load", - "domcontentloaded", - "networkidle0", - "networkidle2" - ], - "type": "string" - } - }, - "type": "object" - } - }, - "required": [ - "method" - ], - "type": "object" - }, - { - "properties": { - "method": { - "const": "snapshot", - "type": "string" - }, - "params": { - "default": {}, - "properties": { - "full": { - "description": "Force a complete snapshot instead of a diff. Snapshots normally return only what changed since your previous one; set full:true when you no longer have that previous snapshot in context (e.g. it was summarized away) and need the entire element list again.", - "type": "boolean" - }, - "maxElements": { - "description": "Maximum number of elements to return (default 500)", - "exclusiveMinimum": 0, - "maximum": 9007199254740991, - "type": "integer" - }, - "targetId": { - "description": "Optional tab targetId to peek at without switching the active tab. Obtain via getTabs or a prior snapshot response. Omit to snapshot the active tab.", - "type": "string" - } - }, - "type": "object" - } - }, - "required": [ - "method" - ], - "type": "object" - }, - { - "properties": { - "method": { - "const": "getTabs", - "type": "string" - }, - "params": { - "default": {}, - "properties": {}, - "type": "object" - } - }, - "required": [ - "method" - ], - "type": "object" - }, - { - "properties": { - "method": { - "const": "switchTab", - "type": "string" - }, - "params": { - "properties": { - "targetId": { - "description": "The targetId of the tab to make active (from getTabs).", - "type": "string" - } - }, - "required": [ - "targetId" - ], - "type": "object" - } - }, - "required": [ - "method", - "params" - ], - "type": "object" - }, - { - "properties": { - "method": { - "const": "createTab", - "type": "string" - }, - "params": { - "default": {}, - "properties": { - "activate": { - "description": "If true (default), switch to the new tab. If false, open it in the background and leave the current tab active.", - "type": "boolean" - }, - "url": { - "description": "URL to open in the new tab. Defaults to about:blank if omitted.", - "type": "string" - }, - "waitUntil": { - "description": "When to consider navigation complete. Only applies when activate is true. Defaults to \"domcontentloaded\".", - "enum": [ - "load", - "domcontentloaded", - "networkidle0", - "networkidle2" - ], - "type": "string" - } - }, - "type": "object" - } - }, - "required": [ - "method" - ], - "type": "object" - }, - { - "properties": { - "method": { - "const": "closeTab", - "type": "string" - }, - "params": { - "properties": { - "targetId": { - "description": "The targetId of the tab to close.", - "type": "string" - } - }, - "required": [ - "targetId" - ], - "type": "object" - } - }, - "required": [ - "method", - "params" - ], - "type": "object" - }, - { - "properties": { - "method": { - "const": "click", - "type": "string" - }, - "params": { - "properties": { - "selector": { - "description": "CSS selector of the element to click", - "type": "string" - } - }, - "required": [ - "selector" - ], - "type": "object" - } - }, - "required": [ - "method", - "params" - ], - "type": "object" - }, - { - "properties": { - "method": { - "const": "type", - "type": "string" - }, - "params": { - "properties": { - "selector": { - "description": "CSS selector of the input element", - "type": "string" - }, - "text": { - "description": "Text to type into the element", - "type": "string" - } - }, - "required": [ - "selector", - "text" - ], - "type": "object" - } - }, - "required": [ - "method", - "params" - ], - "type": "object" - }, - { - "properties": { - "method": { - "const": "loadSecret", - "type": "string" - }, - "params": { - "properties": { - "ref": { - "description": "The credential reference/alias to inject (e.g. an op:// reference). The secret value is resolved server-side and typed into the field — you never see it. Use this for ALL passwords and usernames from a secrets vault; never put a secret value in `type`.", - "type": "string" - }, - "selector": { - "description": "CSS selector of the input to fill. If omitted, the secret is injected into the currently focused element (click/focus the field first).", - "type": "string" - } - }, - "required": [ - "ref" - ], - "type": "object" - } - }, - "required": [ - "method", - "params" - ], - "type": "object" - }, - { - "properties": { - "method": { - "const": "select", - "type": "string" - }, - "params": { - "properties": { - "selector": { - "description": "CSS selector of the select element", - "type": "string" - }, - "value": { - "description": "Option value to select", - "type": "string" - } - }, - "required": [ - "selector", - "value" - ], - "type": "object" - } - }, - "required": [ - "method", - "params" - ], - "type": "object" - }, - { - "properties": { - "method": { - "const": "checkbox", - "type": "string" - }, - "params": { - "properties": { - "checked": { - "description": "Desired checked state (default: toggle)", - "type": "boolean" - }, - "selector": { - "description": "CSS selector of the checkbox element", - "type": "string" - } - }, - "required": [ - "selector" - ], - "type": "object" - } - }, - "required": [ - "method", - "params" - ], - "type": "object" - }, - { - "properties": { - "method": { - "const": "hover", - "type": "string" - }, - "params": { - "properties": { - "selector": { - "description": "CSS selector of the element to hover over", - "type": "string" - } - }, - "required": [ - "selector" - ], - "type": "object" - } - }, - "required": [ - "method", - "params" - ], - "type": "object" - }, - { - "properties": { - "method": { - "const": "scroll", - "type": "string" - }, - "params": { - "default": {}, - "properties": { - "direction": { - "description": "Scroll direction. Defaults to \"down\".", - "enum": [ - "up", - "down", - "left", - "right" - ], - "type": "string" - }, - "selector": { - "description": "CSS selector of element to scroll (omit for page scroll)", - "type": "string" - } - }, - "type": "object" - } - }, - "required": [ - "method" - ], - "type": "object" - }, - { - "properties": { - "method": { - "const": "evaluate", - "type": "string" - }, - "params": { - "properties": { - "content": { - "description": "JavaScript code to execute (use IIFE syntax)", - "type": "string" - } - }, - "required": [ - "content" - ], - "type": "object" - } - }, - "required": [ - "method", - "params" - ], - "type": "object" - }, - { - "properties": { - "method": { - "const": "text", - "type": "string" - }, - "params": { - "default": {}, - "properties": { - "selector": { - "description": "CSS selector to extract text from", - "type": "string" - } - }, - "type": "object" - } - }, - "required": [ - "method" - ], - "type": "object" - }, - { - "properties": { - "method": { - "const": "html", - "type": "string" - }, - "params": { - "default": {}, - "properties": { - "selector": { - "description": "CSS selector to get HTML from", - "type": "string" - } - }, - "type": "object" - } - }, - "required": [ - "method" - ], - "type": "object" - }, - { - "properties": { - "method": { - "const": "waitForSelector", - "type": "string" - }, - "params": { - "properties": { - "selector": { - "description": "CSS selector to wait for", - "type": "string" - }, - "timeout": { - "description": "Timeout in milliseconds (recommend 5000-10000)", - "type": "number" - } - }, - "required": [ - "selector" - ], - "type": "object" - } - }, - "required": [ - "method", - "params" - ], - "type": "object" - }, - { - "properties": { - "method": { - "const": "waitForNavigation", - "type": "string" - }, - "params": { - "default": {}, - "properties": { - "timeout": { - "description": "Timeout in milliseconds (default 30000)", - "type": "number" - } - }, - "type": "object" - } - }, - "required": [ - "method" - ], - "type": "object" - }, - { - "properties": { - "method": { - "const": "waitForTimeout", - "type": "string" - }, - "params": { - "properties": { - "time": { - "description": "Time to wait in milliseconds (e.g., 3000 for 3 seconds)", - "type": "number" - } - }, - "required": [ - "time" - ], - "type": "object" - } - }, - "required": [ - "method", - "params" - ], - "type": "object" - }, - { - "properties": { - "method": { - "const": "waitForRequest", - "type": "string" - }, - "params": { - "properties": { - "method": { - "description": "HTTP method to match (e.g., \"GET\", \"POST\")", - "type": "string" - }, - "timeout": { - "description": "Timeout in milliseconds (default 30000)", - "type": "number" - }, - "url": { - "description": "URL pattern to match (glob-style, e.g., \"*api/results*\")", - "type": "string" - } - }, - "type": "object" - } - }, - "required": [ - "method", - "params" - ], - "type": "object" - }, - { - "properties": { - "method": { - "const": "waitForResponse", - "type": "string" - }, - "params": { - "properties": { - "statuses": { - "description": "HTTP status codes to match (e.g., [200, 201])", - "items": { - "type": "number" - }, - "type": "array" - }, - "timeout": { - "description": "Timeout in milliseconds (default 30000)", - "type": "number" - }, - "url": { - "description": "URL pattern to match (glob-style, e.g., \"*api/results*\")", - "type": "string" - } - }, - "type": "object" - } - }, - "required": [ - "method", - "params" - ], - "type": "object" - }, - { - "properties": { - "method": { - "const": "liveURL", - "type": "string" - }, - "params": { - "default": {}, - "properties": { - "interactable": { - "description": "Allow interaction via the live URL", - "type": "boolean" - }, - "quality": { - "description": "Image quality (1-100)", - "maximum": 100, - "minimum": 1, - "type": "integer" - }, - "resizable": { - "description": "Allow resizing the browser viewport", - "type": "boolean" - }, - "timeout": { - "description": "How long the live URL stays active (ms)", - "type": "number" - }, - "type": { - "description": "Image format for the stream", - "enum": [ - "jpeg", - "png" - ], - "type": "string" - } - }, - "type": "object" - } - }, - "required": [ - "method" - ], - "type": "object" - }, - { - "properties": { - "method": { - "const": "solve", - "type": "string" - }, - "params": { - "default": {}, - "properties": { - "timeout": { - "description": "How long to wait for the captcha to appear (ms). Default 30000. Does not bound the solver itself once a captcha is found.", - "exclusiveMinimum": 0, - "maximum": 9007199254740991, - "type": "integer" - }, - "type": { - "description": "Captcha type to solve. Omit to auto-detect.", - "enum": [ - "cloudflare", - "hcaptcha", - "recaptcha", - "recaptchaV3", - "geetest", - "normal", - "friendlyCaptcha", - "capy", - "textCaptcha", - "amazonWaf", - "dataDome", - "akamai", - "lemin", - "mtcaptcha", - "slider" - ], - "type": "string" - }, - "wait": { - "description": "Wait for the captcha to appear before solving (default true). Set false if you have already verified the widget is on screen.", - "type": "boolean" - } - }, - "type": "object" - } - }, - "required": [ - "method" - ], - "type": "object" - }, - { - "properties": { - "method": { - "const": "screenshot", - "type": "string" - }, - "params": { - "default": {}, - "properties": { - "clip": { - "description": "Region of the page to capture. Mutually exclusive with selector/fullPage.", - "properties": { - "height": { - "description": "Height of the clip, in CSS pixels (>0)", - "minimum": 1, - "type": "number" - }, - "scale": { - "description": "Scale factor of the clip (default 1, >0)", - "exclusiveMinimum": 0, - "type": "number" - }, - "width": { - "description": "Width of the clip, in CSS pixels (>0)", - "minimum": 1, - "type": "number" - }, - "x": { - "description": "X coordinate of the top-left corner, in CSS pixels", - "type": "number" - }, - "y": { - "description": "Y coordinate of the top-left corner, in CSS pixels", - "type": "number" - } - }, - "required": [ - "x", - "y", - "width", - "height" - ], - "type": "object" - }, - "fullPage": { - "description": "Capture the entire scrollable page (default false)", - "type": "boolean" - }, - "omitBackground": { - "description": "Hide default white background for transparent screenshots", - "type": "boolean" - }, - "quality": { - "description": "Image quality 0-100. Applies to jpeg/webp only.", - "maximum": 100, - "minimum": 0, - "type": "number" - }, - "selector": { - "description": "CSS selector of an element to screenshot. Mutually exclusive with fullPage/clip.", - "type": "string" - }, - "timeout": { - "description": "Timeout in milliseconds (default 30000)", - "type": "number" - }, - "toDisk": { - "description": "Save the screenshot to disk instead of returning it inline. You will NOT see the image; the response gives a reusable handle (local path in stdio, single-use GET URL over HTTP) exactly like a download — reuse it with uploadFile or hand it to the user. Use when you only need the file later, not to look at now (see file-transfers).", - "type": "boolean" - }, - "type": { - "description": "Image format. Default \"png\". Use \"jpeg\" for smaller payloads on large pages.", - "enum": [ - "jpeg", - "png", - "webp" - ], - "type": "string" - }, - "waitForImages": { - "description": "Wait for all images on the page to load before capturing", - "type": "boolean" - } - }, - "type": "object" - } - }, - "required": [ - "method" - ], - "type": "object" - }, - { - "properties": { - "method": { - "const": "uploadFile", - "type": "string" - }, - "params": { - "properties": { - "files": { - "description": "Files to attach. Combined decoded size is capped (server default 10MB, hard max 50MB).", - "items": { - "properties": { - "content": { - "description": "Base64-encoded file content. LAST RESORT — only for tiny data you already hold inline. Do NOT read a file into the conversation, and never split/reassemble base64 by hand: use `path` (stdio) or `handle` so the server moves the bytes.", - "type": "string" - }, - "handle": { - "description": "A download handle from a prior getDownloads (a path in stdio mode, a `browserless-download://` URI in HTTP mode). The MCP server reads the stored file — works in both transports and keeps the bytes out of the conversation. Use this to re-upload a file you just downloaded.", - "type": "string" - }, - "mimeType": { - "description": "MIME type; inferred from the extension when omitted.", - "type": "string" - }, - "name": { - "description": "Filename reported to the page. Defaults to the basename of `path`, else \"file\".", - "type": "string" - }, - "path": { - "description": "Local filesystem path to read and upload. stdio (local) mode only — the MCP server reads and base64-encodes it. In HTTP mode use `handle` or `content` instead.", - "type": "string" - } - }, - "type": "object" - }, - "minItems": 1, - "type": "array" - }, - "selector": { - "description": "CSS selector of the <input type=\"file\"> element", - "type": "string" - } - }, - "required": [ - "selector", - "files" - ], - "type": "object" - } - }, - "required": [ - "method", - "params" - ], - "type": "object" - }, - { - "properties": { - "method": { - "const": "getDownloads", - "type": "string" - }, - "params": { - "default": {}, - "properties": {}, - "type": "object" - } - }, - "required": [ - "method" - ], - "type": "object" - }, - { - "properties": { - "method": { - "const": "close", - "type": "string" - }, - "params": { - "default": {}, - "properties": {}, - "type": "object" - } - }, - "required": [ - "method" - ], - "type": "object" - } - ] - }, - { - "properties": { - "method": { - "description": "The BQL method name", - "type": "string" - }, - "params": { - "additionalProperties": {}, - "default": {}, - "description": "Parameters for the method", - "propertyNames": { - "type": "string" - }, - "type": "object" - } - }, - "required": [ - "method" - ], - "type": "object" - } -]New value: +[ + { + "oneOf": [ + { + "properties": { + "method": { + "const": "goto", + "type": "string" + }, + "params": { + "properties": { + "timeout": { + "description": "Navigation timeout in milliseconds", + "type": "number" + }, + "url": { + "description": "The URL to navigate to", + "type": "string" + }, + "waitUntil": { + "description": "When to consider navigation complete. Defaults to \"domcontentloaded\". Avoid networkidle0/networkidle2 unless explicitly needed — they hang on SPAs and dynamic sites.", + "enum": [ + "load", + "domcontentloaded", + "networkidle0", + "networkidle2" + ], + "type": "string" + } + }, + "required": [ + "url" + ], + "type": "object" + } + }, + "required": [ + "method", + "params" + ], + "type": "object" + }, + { + "properties": { + "method": { + "const": "back", + "type": "string" + }, + "params": { + "default": {}, + "properties": { + "waitUntil": { + "description": "When to consider navigation complete. Defaults to \"load\".", + "enum": [ + "load", + "domcontentloaded", + "networkidle0", + "networkidle2" + ], + "type": "string" + } + }, + "type": "object" + } + }, + "required": [ + "method" + ], + "type": "object" + }, + { + "properties": { + "method": { + "const": "forward", + "type": "string" + }, + "params": { + "default": {}, + "properties": { + "waitUntil": { + "description": "When to consider navigation complete. Defaults to \"load\".", + "enum": [ + "load", + "domcontentloaded", + "networkidle0", + "networkidle2" + ], + "type": "string" + } + }, + "type": "object" + } + }, + "required": [ + "method" + ], + "type": "object" + }, + { + "properties": { + "method": { + "const": "reload", + "type": "string" + }, + "params": { + "default": {}, + "properties": { + "waitUntil": { + "description": "When to consider navigation complete. Defaults to \"load\".", + "enum": [ + "load", + "domcontentloaded", + "networkidle0", + "networkidle2" + ], + "type": "string" + } + }, + "type": "object" + } + }, + "required": [ + "method" + ], + "type": "object" + }, + { + "properties": { + "method": { + "const": "snapshot", + "type": "string" + }, + "params": { + "default": {}, + "properties": { + "full": { + "description": "Force a complete snapshot instead of a diff. Snapshots normally return only what changed since your previous one; set full:true when you no longer have that previous snapshot in context (e.g. it was summarized away) and need the entire element list again.", + "type": "boolean" + }, + "maxElements": { + "description": "Maximum number of elements to return (default 500)", + "exclusiveMinimum": 0, + "maximum": 9007199254740991, + "type": "integer" + }, + "targetId": { + "description": "Optional tab targetId to peek at without switching the active tab. Obtain via getTabs or a prior snapshot response. Omit to snapshot the active tab.", + "type": "string" + } + }, + "type": "object" + } + }, + "required": [ + "method" + ], + "type": "object" + }, + { + "properties": { + "method": { + "const": "getTabs", + "type": "string" + }, + "params": { + "default": {}, + "properties": {}, + "type": "object" + } + }, + "required": [ + "method" + ], + "type": "object" + }, + { + "properties": { + "method": { + "const": "switchTab", + "type": "string" + }, + "params": { + "properties": { + "targetId": { + "description": "The targetId of the tab to make active (from getTabs).", + "type": "string" + } + }, + "required": [ + "targetId" + ], + "type": "object" + } + }, + "required": [ + "method", + "params" + ], + "type": "object" + }, + { + "properties": { + "method": { + "const": "createTab", + "type": "string" + }, + "params": { + "default": {}, + "properties": { + "activate": { + "description": "If true (default), switch to the new tab. If false, open it in the background and leave the current tab active.", + "type": "boolean" + }, + "url": { + "description": "URL to open in the new tab. Defaults to about:blank if omitted.", + "type": "string" + }, + "waitUntil": { + "description": "When to consider navigation complete. Only applies when activate is true. Defaults to \"domcontentloaded\".", + "enum": [ + "load", + "domcontentloaded", + "networkidle0", + "networkidle2" + ], + "type": "string" + } + }, + "type": "object" + } + }, + "required": [ + "method" + ], + "type": "object" + }, + { + "properties": { + "method": { + "const": "closeTab", + "type": "string" + }, + "params": { + "properties": { + "targetId": { + "description": "The targetId of the tab to close.", + "type": "string" + } + }, + "required": [ + "targetId" + ], + "type": "object" + } + }, + "required": [ + "method", + "params" + ], + "type": "object" + }, + { + "properties": { + "method": { + "const": "click", + "type": "string" + }, + "params": { + "properties": { + "selector": { + "description": "CSS selector of the element to click", + "type": "string" + } + }, + "required": [ + "selector" + ], + "type": "object" + } + }, + "required": [ + "method", + "params" + ], + "type": "object" + }, + { + "properties": { + "method": { + "const": "type", + "type": "string" + }, + "params": { + "properties": { + "selector": { + "description": "CSS selector of the input element", + "type": "string" + }, + "text": { + "description": "Text to type into the element", + "type": "string" + } + }, + "required": [ + "selector", + "text" + ], + "type": "object" + } + }, + "required": [ + "method", + "params" + ], + "type": "object" + }, + { + "properties": { + "method": { + "const": "loadSecret", + "type": "string" + }, + "params": { + "properties": { + "ref": { + "description": "The credential reference/alias to inject (e.g. an op:// reference). The secret value is resolved server-side and typed into the field — you never see it. Use this for ALL passwords and usernames from a secrets vault; never put a secret value in `type`.", + "type": "string" + }, + "selector": { + "description": "CSS selector of the input to fill. If omitted, the secret is injected into the currently focused element (click/focus the field first).", + "type": "string" + } + }, + "required": [ + "ref" + ], + "type": "object" + } + }, + "required": [ + "method", + "params" + ], + "type": "object" + }, + { + "properties": { + "method": { + "const": "select", + "type": "string" + }, + "params": { + "properties": { + "selector": { + "description": "CSS selector of the select element", + "type": "string" + }, + "value": { + "description": "Option value to select. The snapshot lists a select's accepted values as options=[label=value, …] (bare entries have label === value) — take the value from there instead of guessing or reading the DOM with evaluate.", + "type": "string" + } + }, + "required": [ + "selector", + "value" + ], + "type": "object" + } + }, + "required": [ + "method", + "params" + ], + "type": "object" + }, + { + "properties": { + "method": { + "const": "checkbox", + "type": "string" + }, + "params": { + "properties": { + "checked": { + "description": "Desired checked state (default: toggle)", + "type": "boolean" + }, + "selector": { + "description": "CSS selector of the checkbox element", + "type": "string" + } + }, + "required": [ + "selector" + ], + "type": "object" + } + }, + "required": [ + "method", + "params" + ], + "type": "object" + }, + { + "properties": { + "method": { + "const": "hover", + "type": "string" + }, + "params": { + "properties": { + "selector": { + "description": "CSS selector of the element to hover over", + "type": "string" + } + }, + "required": [ + "selector" + ], + "type": "object" + } + }, + "required": [ + "method", + "params" + ], + "type": "object" + }, + { + "properties": { + "method": { + "const": "scroll", + "type": "string" + }, + "params": { + "default": {}, + "properties": { + "direction": { + "description": "Scroll direction. Defaults to \"down\".", + "enum": [ + "up", + "down", + "left", + "right" + ], + "type": "string" + }, + "selector": { + "description": "CSS selector of element to scroll (omit for page scroll)", + "type": "string" + } + }, + "type": "object" + } + }, + "required": [ + "method" + ], + "type": "object" + }, + { + "properties": { + "method": { + "const": "evaluate", + "type": "string" + }, + "params": { + "properties": { + "content": { + "description": "JavaScript code to execute (use IIFE syntax)", + "type": "string" + } + }, + "required": [ + "content" + ], + "type": "object" + } + }, + "required": [ + "method", + "params" + ], + "type": "object" + }, + { + "properties": { + "method": { + "const": "text", + "type": "string" + }, + "params": { + "default": {}, + "properties": { + "selector": { + "description": "CSS selector to extract text from", + "type": "string" + } + }, + "type": "object" + } + }, + "required": [ + "method" + ], + "type": "object" + }, + { + "properties": { + "method": { + "const": "html", + "type": "string" + }, + "params": { + "default": {}, + "properties": { + "selector": { + "description": "CSS selector to get HTML from", + "type": "string" + } + }, + "type": "object" + } + }, + "required": [ + "method" + ], + "type": "object" + }, + { + "properties": { + "method": { + "const": "waitForSelector", + "type": "string" + }, + "params": { + "properties": { + "selector": { + "description": "CSS selector to wait for", + "type": "string" + }, + "timeout": { + "description": "Timeout in milliseconds (recommend 5000-10000)", + "type": "number" + } + }, + "required": [ + "selector" + ], + "type": "object" + } + }, + "required": [ + "method", + "params" + ], + "type": "object" + }, + { + "properties": { + "method": { + "const": "waitForNavigation", + "type": "string" + }, + "params": { + "default": {}, + "properties": { + "timeout": { + "description": "Timeout in milliseconds (default 30000)", + "type": "number" + } + }, + "type": "object" + } + }, + "required": [ + "method" + ], + "type": "object" + }, + { + "properties": { + "method": { + "const": "waitForTimeout", + "type": "string" + }, + "params": { + "properties": { + "time": { + "description": "Time to wait in milliseconds (e.g., 3000 for 3 seconds)", + "type": "number" + } + }, + "required": [ + "time" + ], + "type": "object" + } + }, + "required": [ + "method", + "params" + ], + "type": "object" + }, + { + "properties": { + "method": { + "const": "waitForRequest", + "type": "string" + }, + "params": { + "properties": { + "method": { + "description": "HTTP method to match (e.g., \"GET\", \"POST\")", + "type": "string" + }, + "timeout": { + "description": "Timeout in milliseconds (default 30000)", + "type": "number" + }, + "url": { + "description": "URL pattern to match (glob-style, e.g., \"*api/results*\")", + "type": "string" + } + }, + "type": "object" + } + }, + "required": [ + "method", + "params" + ], + "type": "object" + }, + { + "properties": { + "method": { + "const": "waitForResponse", + "type": "string" + }, + "params": { + "properties": { + "statuses": { + "description": "HTTP status codes to match (e.g., [200, 201])", + "items": { + "type": "number" + }, + "type": "array" + }, + "timeout": { + "description": "Timeout in milliseconds (default 30000)", + "type": "number" + }, + "url": { + "description": "URL pattern to match (glob-style, e.g., \"*api/results*\")", + "type": "string" + } + }, + "type": "object" + } + }, + "required": [ + "method", + "params" + ], + "type": "object" + }, + { + "properties": { + "method": { + "const": "liveURL", + "type": "string" + }, + "params": { + "default": {}, + "properties": { + "interactable": { + "description": "Allow interaction via the live URL", + "type": "boolean" + }, + "quality": { + "description": "Image quality (1-100)", + "maximum": 100, + "minimum": 1, + "type": "integer" + }, + "resizable": { + "description": "Allow resizing the browser viewport", + "type": "boolean" + }, + "timeout": { + "description": "How long the live URL stays active (ms)", + "type": "number" + }, + "type": { + "description": "Image format for the stream", + "enum": [ + "jpeg", + "png" + ], + "type": "string" + } + }, + "type": "object" + } + }, + "required": [ + "method" + ], + "type": "object" + }, + { + "properties": { + "method": { + "const": "solve", + "type": "string" + }, + "params": { + "default": {}, + "properties": { + "timeout": { + "description": "How long to wait for the captcha to appear (ms). Default 30000. Does not bound the solver itself once a captcha is found.", + "exclusiveMinimum": 0, + "maximum": 9007199254740991, + "type": "integer" + }, + "type": { + "description": "Captcha type to solve. Omit to auto-detect.", + "enum": [ + "cloudflare", + "hcaptcha", + "recaptcha", + "recaptchaV3", + "geetest", + "normal", + "friendlyCaptcha", + "capy", + "textCaptcha", + "amazonWaf", + "dataDome", + "akamai", + "lemin", + "mtcaptcha", + "slider" + ], + "type": "string" + }, + "wait": { + "description": "Wait for the captcha to appear before solving (default true). Set false if you have already verified the widget is on screen.", + "type": "boolean" + } + }, + "type": "object" + } + }, + "required": [ + "method" + ], + "type": "object" + }, + { + "properties": { + "method": { + "const": "screenshot", + "type": "string" + }, + "params": { + "default": {}, + "properties": { + "clip": { + "description": "Region of the page to capture. Mutually exclusive with selector/fullPage.", + "properties": { + "height": { + "description": "Height of the clip, in CSS pixels (>0)", + "minimum": 1, + "type": "number" + }, + "scale": { + "description": "Scale factor of the clip (default 1, >0)", + "exclusiveMinimum": 0, + "type": "number" + }, + "width": { + "description": "Width of the clip, in CSS pixels (>0)", + "minimum": 1, + "type": "number" + }, + "x": { + "description": "X coordinate of the top-left corner, in CSS pixels", + "type": "number" + }, + "y": { + "description": "Y coordinate of the top-left corner, in CSS pixels", + "type": "number" + } + }, + "required": [ + "x", + "y", + "width", + "height" + ], + "type": "object" + }, + "fullPage": { + "description": "Capture the entire scrollable page (default false)", + "type": "boolean" + }, + "omitBackground": { + "description": "Hide default white background for transparent screenshots", + "type": "boolean" + }, + "quality": { + "description": "Image quality 0-100. Applies to jpeg/webp only.", + "maximum": 100, + "minimum": 0, + "type": "number" + }, + "selector": { + "description": "CSS selector of an element to screenshot. Mutually exclusive with fullPage/clip.", + "type": "string" + }, + "timeout": { + "description": "Timeout in milliseconds (default 30000)", + "type": "number" + }, + "toDisk": { + "description": "Save the screenshot to disk instead of returning it inline. You will NOT see the image; the response gives a reusable handle (local path in stdio, single-use GET URL over HTTP) exactly like a download — reuse it with uploadFile or hand it to the user. Use when you only need the file later, not to look at now (see file-transfers).", + "type": "boolean" + }, + "type": { + "description": "Image format. Default \"png\". Use \"jpeg\" for smaller payloads on large pages.", + "enum": [ + "jpeg", + "png", + "webp" + ], + "type": "string" + }, + "waitForImages": { + "description": "Wait for all images on the page to load before capturing", + "type": "boolean" + } + }, + "type": "object" + } + }, + "required": [ + "method" + ], + "type": "object" + }, + { + "properties": { + "method": { + "const": "uploadFile", + "type": "string" + }, + "params": { + "properties": { + "files": { + "description": "Files to attach. Combined decoded size is capped (server default 10MB, hard max 50MB).", + "items": { + "properties": { + "content": { + "description": "Base64-encoded file content. LAST RESORT — only for tiny data you already hold inline. Do NOT read a file into the conversation, and never split/reassemble base64 by hand: use `path` (stdio) or `handle` so the server moves the bytes.", + "type": "string" + }, + "handle": { + "description": "A download handle from a prior getDownloads (a path in stdio mode, a `browserless-download://` URI in HTTP mode). The MCP server reads the stored file — works in both transports and keeps the bytes out of the conversation. Use this to re-upload a file you just downloaded.", + "type": "string" + }, + "mimeType": { + "description": "MIME type; inferred from the extension when omitted.", + "type": "string" + }, + "name": { + "description": "Filename reported to the page. Defaults to the basename of `path`, else \"file\".", + "type": "string" + }, + "path": { + "description": "Local filesystem path to read and upload. stdio (local) mode only — the MCP server reads and base64-encodes it. In HTTP mode use `handle` or `content` instead.", + "type": "string" + } + }, + "type": "object" + }, + "minItems": 1, + "type": "array" + }, + "selector": { + "description": "CSS selector of the <input type=\"file\"> element", + "type": "string" + } + }, + "required": [ + "selector", + "files" + ], + "type": "object" + } + }, + "required": [ + "method", + "params" + ], + "type": "object" + }, + { + "properties": { + "method": { + "const": "getDownloads", + "type": "string" + }, + "params": { + "default": {}, + "properties": {}, + "type": "object" + } + }, + "required": [ + "method" + ], + "type": "object" + }, + { + "properties": { + "method": { + "const": "close", + "type": "string" + }, + "params": { + "default": {}, + "properties": {}, + "type": "object" + } + }, + "required": [ + "method" + ], + "type": "object" + } + ] + }, + { + "properties": { + "method": { + "description": "The BQL method name", + "type": "string" + }, + "params": { + "additionalProperties": {}, + "default": {}, + "description": "Parameters for the method", + "propertyNames": { + "type": "string" + }, + "type": "object" + } + }, + "required": [ + "method" + ], + "type": "object" + } +] - added
Input schema / properties / sessionIdAdded value: +{ + "description": "The `sessionId` returned by your previous browserless_agent call in this conversation. Echo it back on EVERY subsequent call — it binds this conversation to its live browser and its page state (current URL, cookies, filled forms, open tabs). Omit it only on the first call; omitting it later abandons the current browser and starts a blank one, losing everything the session had done. Only ever pass a value the server returned — never invent one.", + "minLength": 1, + "type": "string" +}
10 tool updates
v1.16.0- First observed
browserless_agent - First observed
browserless_crawl - First observed
browserless_export - First observed
browserless_function - First observed
browserless_map - First observed
browserless_performance - First observed
browserless_profiles - First observed
browserless_search - First observed
browserless_skill - First observed
browserless_smartscraper
TDQS
Scored across 16 tools
Most tools are clearly distinct (account, usage, logs, sessions, performance, search, map, crawl, export, function, smartscraper, profiles, link_connect, link_checkout). The main ambiguity is between browserless_smartscraper, browserless_crawl, and browserless_map, which all involve scraping/discovery, though their descriptions do differentiate single-page vs multi-page vs URL discovery. browserless_agent and browserless_function also overlap in executing browser automation, but one is a persistent session and the other is custom Puppeteer code.
All tools use the browserless_ prefix with a descriptive noun (agent, skill, sessions, link_checkout, link_connect, performance, account, usage, logs, smartscraper, function, export, map, crawl, profiles, search). The pattern is consistent, though 'link_checkout' and 'link_connect' use a verb-ish noun while others are plain nouns, and 'smartscraper' is a compound word rather than a simple noun. Minor deviations but overall predictable.
16 tools is on the higher end but appropriate for a browser automation platform that covers session control, scraping, crawling, search, account management, and Stripe Link integration. Each tool serves a distinct capability area, though a few could be consolidated (e.g., smartscraper/crawl/map could arguably be one tool with modes).
The tool surface covers the core browser automation lifecycle well: driving a session (browserless_agent), custom code (browserless_function), scraping (smartscraper), crawling (crawl), mapping (map), search, export, performance audits, account/usage/logs, sessions, profiles, and Stripe Link. Minor gaps: there's no dedicated tool for managing browser profiles beyond listing them (no create/delete), and no explicit tool for managing API keys, but these are peripheral to the main purpose.
Maintenance
Related MCP Connectors
Browserless MCP — wraps the Browserless headless-Chromium REST API (browserless.io)
Zenrows MCP server — Fetch, Extract, Batch, and Browser Sessions for AI coding assistants
Official SerpApi MCP server for Google, Bing, and other search engines.
ScrapingBee MCP — wraps the ScrapingBee headless-scraping API (scrapingbee.com)
Related MCP Servers
- AlicenseAqualityDmaintenanceA server that provides browser automation capabilities using Playwright, enabling LLMs to interact with web pages through structured accessibility snapshots without requiring screenshots or vision models.225,425,474 npm1Apache 2.0
- FlicenseNot gradedqualityCmaintenanceMCP server that exposes the Pinchtab browser API for token-efficient web scraping, change detection, and automated testing workflows.15-
- AlicenseAqualityBmaintenanceMCP server for browser automation with anti-detection. Scout pages, find elements, interact with websites, and monitor network traffic from any AI client that supports the Model Context Protocol.211MIT
- AlicenseNot gradedqualityAmaintenanceRemote MCP server for web scraping with anti-bot evasion. Provides stealth HTTP fetching, headless browser with Cloudflare bypass, CSS selectors, YouTube transcripts, and Markdown conversion.1MIT