FSB vs Browser MCP

A side-by-side look at the FSB and Browser MCP MCP servers: transport, auth, tools, quality signals, and the config to install either one in Claude, Cursor, Windsurf or VS Code.

FSB logoFSBBrowser Automation · Local subprocess (stdio)Chrome extension plus MCP server that lets any MCP client drive your own signed in Chrome by reading the live DOM instead of screenshots, with no API key. 69 tools, session replay, and an encrypted credential vault.64GoodAuth: no auth requiredBrowser MCP logoBrowser MCPBrowser Automation · Local subprocess (stdio)Control your real Chrome from Claude Code. 29 tools with CAPTCHA solving and multi-session.69GreatAuth: no auth required

Verdict: choose FSB for browser automation work as a local process; choose Browser MCP for browser automation as a local process. You can also run both side by side in one mcpServers block.

Which MCP server should you choose?

FSB logoChoose FSB when

  • You need dedicated capabilities in the Browser Automation domain.
  • You prefer a local stdio subprocess transport.
  • Its auth model fits you: No auth required.
  • You want tools like start_visual_session, end_visual_session, trigger.
Explore FSB

Browser MCP logoChoose Browser MCP when

  • You need dedicated capabilities in the Browser Automation domain.
  • You prefer a local stdio subprocess transport.
  • Its auth model fits you: No auth required.
  • You want tools like browser_navigate, browser_get_page_content, browser_extract_list.
Explore Browser MCP

Feature & specification comparison

Rows where the two servers differ are marked.

SpecFSB logoFSBBrowser MCP logoBrowser MCP
CategoryBrowser AutomationBrowser Automation
Quality signal64/100 · Good69/100 · Great
Install commandConfirmedConfirmed
TransportLocal subprocess (stdio)Local subprocess (stdio)
AuthNo auth requiredNo auth required
PricingFreeFree
Required env varsNone listedNone listed
Verified ownerCommunity listingCommunity listing
GitHub stars20258
Directory activity 1 views 0 installs 0 upvotes 8 views 0 installs 0 upvotes
Tools7240

Tools & capabilities

FSB tools 72

  • start_visual_session[REMOVED in v0.9.0] This tool was removed in fsb-mcp-server v0.9.0 (FSB v0.9.62 implicit visual-session contract). Calling it returns the typed TOOL_REMOVED error with a migration recipe pointer. The visual session is now implicit on every action tool call via the required visual_reason + client field bundle -- there is no longer a separate start call. See CHANGELOG.md#v0.9.0 and the Visual Session Lifecycle section of mcp/README.md for the migration recipe.
  • end_visual_session[REMOVED in v0.9.0] This tool was removed in fsb-mcp-server v0.9.0 (FSB v0.9.62 implicit visual-session contract). Calling it returns the typed TOOL_REMOVED error with a migration recipe pointer. The visual session now clears either automatically after 60 seconds of silence (sliding window) or immediately when an action tool is called with is_final: true -- there is no longer a separate end call. See CHANGELOG.md#v0.9.0 and the Visual Session Lifecycle section of mcp/README.md for the migration recipe.
  • triggerArm a reactive DOM trigger on one selector. Use this when the user wants to watch an element until it changes, crosses a threshold, equals or matches a value, or contains text. The watch is owned by the background trigger runtime and persisted across service-worker eviction. Pass one selector and one condition object; runtime validation checks the nested condition details before arming. Use stop_trigger to cancel, get_trigger_status to inspect one watcher, and list_triggers to enumerate watchers.
  • stop_triggerCancel a trigger by id. This is cancellation-critical and bypass-class: it must be callable promptly even if a trigger or another mutation is pending. Missing or already terminal triggers are handled idempotently by the background trigger runtime.
  • get_trigger_statusRead the persisted status for one trigger by id. Returns storage-of-truth trigger state including status, watch mode, condition, target tab, owner, current value, timing, and attention details when available.
  • list_triggersList persisted trigger snapshots. By default, returns active and attention-state watchers; include_terminal can include fired and stopped triggers for audit or cleanup workflows.
  • execute_jsRun JavaScript directly in the active page. PRIMARY INTERACTION TOOL: try execute_js FIRST for clicks, scrolls, reads, attribute lookups, and most other DOM work -- it bypasses overlay/obscured-element issues, viewport constraints, and CDP timeouts that block native click/scroll. Typical patterns: `return document.querySelector('#add-to-cart-button').click(), true;` for clicks; `return Array.from(document.querySelectorAll('a')).map(a=>a.href);` for extraction; `window.scrollTo(0, document.body.scrollHeight); return true;` for scroll. After a JS click, verify with read_page or get_page_snapshot (a true click should produce observable DOM change). FALLBACK TO NATIVE TOOLS WHEN: (1) JS click reports success but the page state did not change (framework swallowed the synthetic event -- use native click which fires real CDP events that React/Angular/Vue listen to); (2) typing into controlled text inputs (use native `type` so React onChange fires correctly -- `element.value = ...` will NOT update component state); (3) real drag operations on react-beautiful-dnd / Sortable.js / Trello-style widgets (use drag_drop / drag for real pointer events); (4) form submission that depends on validated input state (use native click on submit). Code runs as a function body -- use `return` to send values back (results are stringified). Async work: Promises are not awaited, so fire-and-forget then poll on `window.__yourKey`. Related: read_page (verify result), get_dom_snapshot (debug after JS click), click / type / drag_drop (fallbacks when JS doesn't take effect). Multi-agent: agent-scoped tabs; cross-agent reject with TAB_NOT_OWNED; cap configurable (default 8, 1-64). Pass tab_id only when this agent owns multiple tabs; auto-resolves otherwise. RETURNS change_report: when this tool runs, the response includes a `change_report` field with a compact diff of what the action mutated (URL, dialogs_opened, nodes_added, nodes_removed, attrs_changed, inputs_changed, focus_shift). Use this to learn the consequence without calling read_page next. If the report exceeds the size cap, `change_report.truncated:true` and `change_report_hint` are set; call read_page for the full state.
  • navigateOpen a URL in the active browser tab. Returns the final URL after any redirects. When to use: as the first step to reach a target website. Related: read_page (read content after navigating), list_tabs (see what tabs are already open). Multi-agent: agent-scoped tabs; cross-agent reject with TAB_NOT_OWNED; cap configurable (default 8, 1-64). Pass tab_id only when this agent owns multiple tabs; auto-resolves otherwise. RETURNS change_report: when this tool runs, the response includes a `change_report` field with a compact diff of what the action mutated (URL, dialogs_opened, nodes_added, nodes_removed, attrs_changed, inputs_changed, focus_shift). Use this to learn the consequence without calling read_page next. If the report exceeds the size cap, `change_report.truncated:true` and `change_report_hint` are set; call read_page for the full state.
  • searchSearch for content on the current site or web. When to use: to find content on the current site or web. Automatically detects the site's search input (Amazon, YouTube, GitHub, etc.) via DOM heuristics -- only falls back to Google when no site search exists. Returns search results status. Related: read_page (read search results after searching), click (click a specific search result). Multi-agent: agent-scoped tabs; cross-agent reject with TAB_NOT_OWNED; cap configurable (default 8, 1-64). Pass tab_id only when this agent owns multiple tabs; auto-resolves otherwise.
  • go_backNavigate back one page in browser history. Returns the new URL. When to use: to return to the previous page after following a link or navigating away. Related: go_forward (undo a go_back), navigate (go to a specific URL instead). Multi-agent: agent-scoped tabs; cross-agent reject with TAB_NOT_OWNED; cap configurable (default 8, 1-64). Pass tab_id only when this agent owns multiple tabs; auto-resolves otherwise. RETURNS change_report: when this tool runs, the response includes a `change_report` field with a compact diff of what the action mutated (URL, dialogs_opened, nodes_added, nodes_removed, attrs_changed, inputs_changed, focus_shift). Use this to learn the consequence without calling read_page next. If the report exceeds the size cap, `change_report.truncated:true` and `change_report_hint` are set; call read_page for the full state.
  • go_forwardNavigate forward one page in browser history. Returns the new URL. When to use: after using go_back, to move forward again. Related: go_back (go back in history), navigate (go to a specific URL instead). Multi-agent: agent-scoped tabs; cross-agent reject with TAB_NOT_OWNED; cap configurable (default 8, 1-64). Pass tab_id only when this agent owns multiple tabs; auto-resolves otherwise. RETURNS change_report: when this tool runs, the response includes a `change_report` field with a compact diff of what the action mutated (URL, dialogs_opened, nodes_added, nodes_removed, attrs_changed, inputs_changed, focus_shift). Use this to learn the consequence without calling read_page next. If the report exceeds the size cap, `change_report.truncated:true` and `change_report_hint` are set; call read_page for the full state.
  • refreshReload the current page. Returns the refreshed URL. When to use: when page content may be stale, after errors, or to reset page state. Related: navigate (go to a different URL), wait_for_stable (wait for page to settle after refresh). Multi-agent: agent-scoped tabs; cross-agent reject with TAB_NOT_OWNED; cap configurable (default 8, 1-64). Pass tab_id only when this agent owns multiple tabs; auto-resolves otherwise. RETURNS change_report: when this tool runs, the response includes a `change_report` field with a compact diff of what the action mutated (URL, dialogs_opened, nodes_added, nodes_removed, attrs_changed, inputs_changed, focus_shift). Use this to learn the consequence without calling read_page next. If the report exceeds the size cap, `change_report.truncated:true` and `change_report_hint` are set; call read_page for the full state.
  • +60 more on the listing page

Browser MCP tools 40

  • browser_navigateNavigate the active browser tab to a URL. Reuses the current tab by default (no tab spam). Pass new_tab=true only when you need to keep the current page open. In the current tab the answer is ok:true only when Chrome took the address and the new page is proven loaded within about 25 s (the wait ends half a second before that limit). If the page asks "Leave site?", the default answer is Stay (an armed browser_handle_dialog answers instead) and the reply is error beforeunload-blocked with the tab's actual url: the page may hold unsaved work, so ask the user (browser_ask_user) before you call again with leave_page:true. Other errors, each with sent (was the address sent to Chrome?) and a note on what to do: navigation-in-progress (another navigate is working in this tab, or - with op_id null - an earlier answer to a dialog on the tab is not confirmed yet; wait a moment), dialog-open (a dialog is on screen - answer it with browser_handle_dialog), tab-not-responding (busy page, or a dialog Chrome does not show us), navigation-failed, dialog-unconfirmed, navigation-unconfirmed (no load proven in time; may_complete_later and pending_url say it can still happen - read the tab before sending it again; a download or a 204 answer never loads a page and ends here too), tab-closed (Chrome no longer has the tab), session-changed (another call changed the session's active tab while this one waited; nothing was sent).
  • browser_get_page_contentGet the content of the current page as text or HTML. Pass `selector` to read one part of a large page instead of all of it - on a big logged-in app the full HTML can run past a million characters. The answer is capped at 30000 characters by default and says so when it had to cut, with the real length, so a truncated page is never mistaken for a whole one.
  • browser_extract_listRead the rows of a long or virtualised list by scrolling its container until no new rows appear. Use this instead of browser_get_page_content whenever a page shows a repeating list longer than the viewport - mail lists (Outlook, Gmail), invoice/billing tables, search results, transaction histories. Those UIs keep only ~7 rows in the DOM at a time, so a single page read returns a sliver and looks complete. Pass the CSS selector of one repeating row (e.g. '[role="option"]', 'tr', '[role="listitem"]'); the scrollable ancestor is found automatically. When the tab is the active one in its window, it scrolls with real wheel events, also at the bottom, so a feed that loads more rows on wheel (Threads, X) loads them. A hidden page (a background tab) gets no wheel or scroll events, IntersectionObserver callbacks or animation frames, so a list that loads or renders rows while you scroll stops at what is already there; then reached_end is false and `note` says why. Returns deduplicated row text, scroll_method (wheel or script) and reached_end, which is true only when the list stood at its bottom in a visible page with no new rows, growth or movement for stable_rounds rounds in a row. If the selector matches no rows with text, the answer is error "no-rows".
  • browser_screenshotTake a screenshot of the visible area of the current tab. Returns base64 PNG, or saves to disk if path is provided. The image is in device pixels; when Chrome reports them in time, the answer also gives the CSS viewport size and devicePixelRatio, which browser_click_xy coordinates need. Chrome stays where it is: if its window is covered by other windows, there is no picture and the answer is error "window-covered".
  • browser_execute_scriptExecute JavaScript in the current page. IMPORTANT: the parameter is `code` (NOT `script` - though that alias is accepted), and it must be an EXPRESSION, not statements: use an IIFE `(() => { ...; return x; })()`. Top-level `return` is a syntax error (the handler wraps code in parentheses).
  • browser_double_clickTrue double-click on an element (two trusted press/release pairs with escalating clickCount). Use for open-item actions (calendar events, file lists) where two single clicks would trigger inline-rename instead (e.g. OWA month view). If the mouse would not reach the target at its center - another element lies in front of it (an overlay, a dialog's backdrop), the target does not take clicks there (pointer-events, visibility, clipping), or a different control inside it would get the click (a delete button on a card; for a container that is not clickable itself, its own plain link with a real address (a hash route with a path, such as #/orders, counts; a fragment with no target on the page does not) does not count, nor does a web component's own button that covers it, nor a box with a checkbox, switch or radio role right next to a hidden field and the only one in the nearest label around it, a label of that field (a box inside a closed shadow root cannot be seen and is not counted), which counts as that field) - the answer is ok:false with error "covered" and covered_by. Found before the mouse is sent, nothing is sent; if it comes in front afterwards, the rest of the presses and clicks are stopped (a release the page moves elsewhere is let through), and the answer also has maybe_landed: true, because the page may already have reacted.
  • browser_right_clickRight-click an element (trusted CDP mouse events) to open page-level context menus (web apps like OWA/Google Docs render their own). Note: Chrome's NATIVE context menu does not open via CDP - only in-page menus. If the mouse would not reach the target at its center - another element lies in front of it (an overlay, a dialog's backdrop), or the target does not take clicks there (pointer-events, visibility, clipping) - the answer is ok:false with error "covered" and covered_by. Found before the mouse is sent, nothing is sent; if it comes in front afterwards, the rest of the presses and clicks are stopped (a release the page moves elsewhere is let through), and the answer also has maybe_landed: true, because the page may already have reacted.
  • browser_click_xyESCAPE HATCH: Click at raw viewport coordinates (CSS pixels) with fully trusted mouse events. Use when a visible button resists every selector strategy (Azure portal dialogs, Knockout-bound divs, canvas UIs): take a screenshot, read the button's position, click its center. Coordinates are CSS pixels, NOT image pixels: convert with the viewport line browser_screenshot returns: x_css = x / W * css_width and y_css = y / W * css_width, where (x, y) is the point in the image and W is the width the image is shown to you at.
  • browser_reattach_debuggerRECOVERY: Force-detach and re-attach the Chrome debugger on the current tab. Use when interactive tools (click/fill/press_key) start timing out or reporting ghost-attach ("Debugger attach failed ... ghost") while list_tabs still works - faster than reloading the extension.
  • browser_clickClick an element on the page. Supports CSS selectors AND text-based selectors. Auto-scrolls element into view. Uses real mouse events (works on Angular/React SPAs and CSP-strict sites like Google, Stripe). A text selector looks inside an open modal dialog first (a <dialog> opened with showModal, or a visible element with aria-modal="true"), exact text before partial. If the mouse would not reach the target at its center - another element lies in front of it (an overlay, a dialog's backdrop), the target does not take clicks there (pointer-events, visibility, clipping), or a different control inside it would get the click (a delete button on a card; for a container that is not clickable itself, its own plain link with a real address (a hash route with a path, such as #/orders, counts; a fragment with no target on the page does not) does not count, nor does a web component's own button that covers it, nor a box with a checkbox, switch or radio role right next to a hidden field and the only one in the nearest label around it, a label of that field (a box inside a closed shadow root cannot be seen and is not counted), which counts as that field) - the answer is ok:false with error "covered" and covered_by. Found before the mouse is sent, nothing is sent; if it comes in front afterwards, the rest of the presses and clicks are stopped (a release the page moves elsewhere is let through), and the answer also has maybe_landed: true, because the page may already have reacted. Examples: "button:text(Get started)", "text=Submit", "#my-button", "a.btn-primary"
  • browser_fillFill a form input field with a value. Supports CSS selectors AND text-based selectors. Auto-scrolls and focuses the element. Works on CSP-strict sites via Chrome Debugger API. For date inputs use browser_set_date, for autocomplete/combobox use browser_set_combobox. With a text selector the field is clicked first, so the same check as browser_click applies: if the mouse would not reach it at its center, nothing is typed and the answer is ok:false with error "covered".
  • browser_press_keyPress a keyboard key (Enter, Tab, Escape, ArrowDown, etc.). Useful for submitting forms, navigating dropdowns, closing dialogs. Supports modifier keys (ctrl, alt, shift, meta). While ctrl, meta or alt is held, no character is typed. With ctrl or meta (and no shift), the letters a, c, x, v, z and y are sent with Chrome's own editing command - select all, copy, cut, paste, undo, redo; z with shift is redo - including on macOS, where Chrome runs no shortcut for a synthetic key without it. The answer names the command that was sent in `shortcut`; `landed` says the page received the key, not what the command did. Paste inserts the user's real clipboard into the page, and copy and cut replace what is on it.
  • +28 more on the listing page

Ready-to-paste client configs

Paste either block (or both) into your client config, e.g. claude_desktop_config.json or ~/.cursor/mcp.json. We only show a config when the install command is confirmed.

FSB

mcpServers (Claude Desktop / Cursor)
{
  "mcpServers": {
    "fullselfbrowsing-fsb": {
      "command": "npx",
      "args": [
        "-y",
        "fsb-mcp-server@latest"
      ]
    }
  }
}

Browser MCP

mcpServers (Claude Desktop / Cursor)
{
  "mcpServers": {
    "browser-mcp": {
      "command": "npx",
      "args": [
        "-y",
        "@agent360/browser-mcp"
      ]
    }
  }
}

Frequently asked questions

FSB is categorized under Browser Automation and uses a local stdio subprocess. Browser MCP belongs to Browser Automation and uses a local stdio subprocess. Pick FSB for browser automation work and Browser MCP for browser automation.

Copy the mcpServers block from the config section on this page into your client's config (for example claude_desktop_config.json or ~/.cursor/mcp.json), then fully restart the client.

FSB is listed as free. Auth: no auth required. Browser MCP is listed as free. Auth: no auth required. If a server needs environment variables such as API keys, define them under the env key in your client's MCP config.

Yes. MCP clients support multiple servers: add fullselfbrowsing-fsb and browser-mcp as separate keys in the same mcpServers object, and your assistant will call tools from either one.

On AllMCPs, FSB has a quality signal of 64/100 (Good) with 1 views, 0 installs and 202 GitHub stars. Browser MCP has 69/100 (Great) with 8 views, 0 installs and 58 GitHub stars.