Documentation

Migrate from Browserstation

Map all 40 reviewed wrapper tools to local Swarmtab operations.

This table maps all 40 names in the local Browserstation MCP wrapper reviewed on 17 October 2026. Its prefix is mcp__browserstation__; omit that prefix in the table. It is separate from upstream ReinforceNow/browserstation, which exposes /browsers and CDP WebSockets. Swarmtab nineteenth checkpoint has neither route compatibility nor a public CDP/Playwright transport. This guide is a rewrite map, not an installed compatibility adapter.

Session and result rules

Use Node 22+ and the checkout’s sdk/node/index.mjs, or Python 3.11+ and sdk/python on PYTHONPATH. Privately set SWARMTAB_URL and SWARMTAB_AGENT_TOKEN from Swarmtab’s agent connect information. The SDK creates a REST session on its first call, keeps Swarmtab-Session-Id, and closes with DELETE /v1/session. A wrapper s1 ID or upstream browser UUID cannot be reused. Keep your own wrapper-ID-to-client map if you maintain a compatibility layer.

Results keep MCP content, optional structuredContent and isError. Schema values live at structuredContent.data.data; check complete and validate before using them. Raw REST callers must check HTTP status and isError, including on HTTP 200. The SDK throws on either error layer. Default task lifetime is 600-second disconnected grace and 21,600-second maximum life, configurable by the human. Close clients explicitly; a timeout is an unknown action outcome, not permission to replay input.

Name-by-name mapping

Arguments below are tool JSON, used with client.call(name,args). Each client owns its session. Session arguments are therefore omitted from individual calls.

Wrapper tool Swarmtab tool and arguments Difference / unsupported behavior
browse For each URL: site {action:"scrape",url,format:"markdown",max_chars} No six-worker guarantee or matching multi-document envelope; use separate clients for isolation/concurrency
browser_reset Close only the caller’s clients; create new ones No agent-authorized global browser reset
browser_status tabs {action:"list"} per owned client No cluster/worker status equivalent
check check {ref,on:true} Supply on explicitly; wrapper default was true
click click {ref} CSS must match exactly one element
close_tab tabs {action:"close",id:"t2"} Resolve index through fresh tab list; last tab cannot be closed
console_logs logs {kind:"console",limit:50} Use contains to filter bounded sanitized entries
double_click click {ref,action:"double_click"} Same reference rules
download click {ref}, then files {action:"downloads"} Agent-owned storage, not out_dir; hand off with approved Outbox
drag click {ref:from_ref,action:"drag",to:to_ref} Or dx/dy; fresh references required
eval_js read {format:"script",expression} Human/secret gates; not a raw CDP evaluator
fill type {ref,text,submit:press_enter} Replaces value; typed:true emits sequential keys with bounded delay_ms and optional append:true
get_cookies space {action:"export_cookies",path:"login.json"} Human approval, scoped file; no raw cookie JSON return
go_back nav {action:"back"} Resubmission gates remain
go_forward nav {action:"forward"} Resubmission gates remain
hover click {ref,action:"hover"} Same reference rules
list_frames read {format:"controls"} for combined frame controls No matching frame-index catalog or selected-frame mode
list_tabs tabs {action:"list"} IDs such as t1, not zero-based indexes
navigate open {url} Read new controls after navigation
network_requests logs {kind:"network",limit:50} Sanitized request/status/failure entries; supports contains and only_failures; approved HAR is separate
new_tab tabs {action:"new",url}, then tabs {action:"list"} New returns the page read; list tabs to look up its ID (such as t2) before switching or closing
page_head read {format:"seo"} Swarmtab SEO shape
page_html read {format:"html",selector,max_bytes} max_chars is not the HTML byte limit; live DOM and masked hidden/secret values
page_read read {format:"controls",max_chars} Fresh control numbers start at zero, not wrapper’s one
press press {key} Swarmtab key syntax; not a fill helper
reload nav {action:"reload"} Guarded reload; input outcome must be inspected
right_click click {ref,action:"right_click"} Same reference rules
screenshot New client: open {url}, optional settings {viewport:"1440x900"}, then screenshot {full:true} Decode inline image in caller; width, out_path, full_page are not aliases
scroll scroll {to:"bottom"} or {to:"down",n:600} / {to:"up",n:600} to required, not defaulted; control scroll is {to:"control",n:<number>}, no CSS ref
select select {ref,value} Native matching tries value before visible text; wrapper precedence was opposite; arrays support multiple selection
session_close client.close() / DELETE /v1/session Ends only this client; private state and unfinished jobs are removed
session_list Caller maintains client/session registry No agent-wide list of other clients’ sessions
session_open New SDK client, optional viewport setting, then open {url} No worker allocation; use the actual ephemeral local port
session_screenshot screenshot {full:full_page} Inline JPEG by default or format:"png", rather than writing out_path; supports scale and freeze_animations; cross-origin frame consent may be required
set_cookies space {action:"import_cookies",path:"login.json"} Approved file import; raw cookies string is unsupported
storage read {format:"storage"} Both local/session stores, sensitive names masked; no selected-frame inheritance
switch_tab tabs {action:"switch",id:"t1"} Resolve current index to ID rather than adding one
upload_file files {action:"upload",ref:<number>,path} or {action:"upload",ref,from:"inbox",name} paths or inbox names selects up to 20 admitted files atomically, up to 200 MiB; multiple files require a multiple input. Repeated calls replace selection
use_frame Use fresh numbered frame controls from read No sticky frame scope for CSS, HTML, storage, scripts or extraction
wait_for wait {text,seconds:10} timeout_s maps to seconds; selector-visible wait has no exact equivalent; count is not visibility

Both systems require fresh numbered references, but numbers cannot transfer. A string containing only digits is also a Swarmtab control number. CSS targets traverse open shadow roots in the main document, must be visible/enabled, and must match exactly once. Closed roots and general selected-frame scope remain unsupported. Use numbered controls for represented frame/shadow controls. Ordinary logs retain 200 entries; the default read is 50.

Jobs, pagination and timeouts

Swarmtab’s ordinary site(crawl) and site(batch) return a textual job ID. Poll site {action:"status",job} in the same session; Node waitJob returns the MCP status result, not all original documents. Original results are retained separately from the 200-page scrape cache, up to 16 MiB per job and 50 MiB per agent. Page site(status) with offset/limit or SDK jobResults / job_results. Saved-space batches publish batch.jsonl; private batches keep results in memory without an artifact. Deterministic schema crawls run as background jobs up to 5,000 pages, and schema batches accept 1–100 URLs without chunking. Saved-space schema crawls keep resumable checkpoints. Actual client-model sampling keeps its 16-page, 180-second collection limit.

Map continuation uses site {action:"map",cursor}. A cursor is agent/context/query-bound, expires after five minutes and is replaced by a new map. Continue until the cursor is null, regardless of limited. Rows and monitors use offsets instead.

Operation Timeout / retry contract
Wrapper navigation 45-second Playwright calls in the reviewed wrapper
Swarmtab page operation Typical 30-second page limit; eligible engine GET recovery can retry once
Input Not automatically replayed; inspect after a timeout
Node SDK HTTP request / polling Default request timeout 240,000 ms; polling timeout/interval are milliseconds
Python SDK HTTP request / polling Default request timeout 240 seconds; polling timeout/interval are seconds
Text wait seconds 1–60; not a selector-visible wait
Assertion wait timeout_ms 0–60,000; default 10,000; count does not imply visibility

Runnable form migration

Save this as a root-level .mjs file in the checkout, set FORM_URL to a page with #name and #agree, then run with node. It fills a local form without submitting it. Runnable equivalent: node examples/integrations/migrate.mjs browserstation "$FORM_URL".

import { Swarmtab } from './sdk/node/index.mjs';
const client = new Swarmtab({ baseUrl: process.env.SWARMTAB_URL, token: process.env.SWARMTAB_AGENT_TOKEN });
try {
  await client.open(process.env.FORM_URL);
  await client.call('type', { ref: '#name', text: 'Migration visitor' });
  await client.call('check', { ref: '#agree', on: true });
  const result = await client.read({ format: 'markdown', max_chars: 2000 });
  console.log(result.content.filter(c => c.type === 'text').map(c => c.text).join('\n'));
} finally {
  await client.close();
}

Swarmtab runs on the Mac, so use http://127.0.0.1:<fixture-port> for local development, with the human’s allow_local grant. Do not preserve the wrapper’s Docker host.docker.internal workaround. Saved logins use named spaces, exclusive ownership and human consent. Ask the human for login, 2FA, CAPTCHA and approvals; no solver is provided. Real browser mode and identity remain the owner’s choice. Browser-use and CrewAI custom-tool recipes are covered in the companion integration guide; they do not restore raw CDP compatibility.

All documentation