Skip to main content
Glama

Server Configuration

Describes the environment variables required to run the server.

NameRequiredDescriptionDefault
PORTNoHTTP server port (only for httpStream transport)8080
TRANSPORTNoTransport type: stdio or httpStreamstdio
BROWSERLESS_TOKENYesYour Browserless API token
BROWSERLESS_API_URLNoAPI endpoint (for self-hosted Browserless)https://production-sfo.browserless.io
BROWSERLESS_TIMEOUTNoRequest timeout in milliseconds30000
BROWSERLESS_CACHE_TTLNoCache TTL in milliseconds (0 to disable)60000
BROWSERLESS_MAX_RETRIESNoMax retry attempts for failed requests3

Capabilities

Features and capabilities supported by this server

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

Tools

Functions exposed to the LLM to take actions

NameDescription
browserless_exportA

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.

browserless_skillA

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/slug from a site lookup.

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 triggers

  • screenshots — when to screenshot vs. snapshot, scope and format choices

  • 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 solve command, response semantics, escalation path (Cloud-only)

  • file-transfersuploadFile / getDownloads, stdio-path vs. base64 content, size caps

browserless_agentA

READ CAREFULLY: Execute browser commands in persistent agent session.

Core Loop (ReAct: Reason → Act → Observe)

  1. 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 to goto.

  2. goto — waits "domcontentloaded"

  3. snapshot — returns interactive + informational elements (button, link, textbox, combobox, checkbox, heading, img+alt) with ref= selectors

  4. Plan all actions from snapshot

  5. Batch execute

  6. Re-snapshot only if page changed

  7. 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).

Proxy (optional)

Proxy config is a top-level tool argument (proxy, proxyCountry, etc. on the tool call itself) — 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.

  • proxy: "residential" — enable routing; proxyCountry: "us" — geo (ISO-2); proxyState / proxyCity (paid plans, 401 otherwise); proxySticky: true — stable IP; proxyLocaleMatch: true — match locale; proxyPreset — named config; externalProxyServer: "http://u:p@host:port" — bring your own (http(s) only)

  • Geo/preset/sticky require proxy: "residential" or externalProxyServer set

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.

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 ## Auth above for when to load)

  • shadow-dom — deep selectors, iframe targeting

  • cookie-consent — vendor-specific dismiss recipes

  • modals — closing dialogs and alertdialogs

  • captchas — the solve command (Cloud only)

  • snapshot-misses — truncated/empty snapshots, image-rendered content

  • dynamic-content — choosing the right wait* method

  • screenshots — when to screenshot vs. snapshot, scope and format choices

  • tabs — 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

  • Snapshots 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), request snapshot { full: true } to get the complete element list again.

Selectors

  • Use ref= (CSS) or deep-ref= (starts < ) exactly as shown in snapshot

  • Example: [3] button "Sign In" ref=button#submit"button#submit"

  • deep-ref for shadow DOM / iframes — see shadow-dom skill

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).

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

  1. Check in-memory snapshot (text/values already there)

  2. text { selector } — from specific element

  3. evaluate { content } — JS (IIFE): (() => { return ... })()

  4. 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.

  • 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 its handle (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 in uploadFile. (No separate download tool — use the agent.)

  • base64 content is a LAST RESORT — tiny inline data only.

  • Full recipe: file-transfers skill.

Batching — Maximize Per Call

Plan ALL actions from snapshot before next snapshot.

Process:

  1. Classify actions: safe (type, hover, scroll, evaluate, select, checkbox) vs. page-changing (click, goto)

  2. Batch: safe FIRST → page-changing LAST

  3. For forms: if submit button is in snapshot, batch type + click in one call

  4. 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 < selector if not already deep-ref

  • SESSION_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 — 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 straight to uploadFile (files: [{ path: "/abs/file" }]) — the server reads it. 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.

browserless_searchA

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.

browserless_performanceA

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.

browserless_smartscraperA

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 of a site, use browserless_crawl; to list a site's URLs, use browserless_map.

browserless_functionA

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 block

  • application/pdf and 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.

browserless_mapA

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.

browserless_crawlA

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.

browserless_profilesA

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.

Prompts

Interactive templates invoked by user choice

NameDescription
scrape-urlScrape a webpage and return its content as markdown with metadata
extract-contentExtract specific information from a webpage using the smart scraper

Resources

Contextual data attached and managed by the client

NameDescription
Browserless Service Status
Browserless API Documentation

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/browserless/browserless-mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server