A side-by-side look at the Arc Control (macOS) and Public Browser MCP servers: transport, auth, tools, quality signals, and the config to install either one in Claude, Cursor, Windsurf or VS Code.
Verdict: choose Arc Control (macOS) for browser automation work as a local process; choose Public Browser for browser automation as a local process. You can also run both side by side in one mcpServers block.
list_tabs, get_current_tab, switch_to_tab.virtual_desk, view_page, click.Rows where the two servers differ are marked.
| Spec | ||
|---|---|---|
| Category | Browser Automation | Browser Automation |
| Quality signal | 56/100 · Good | 64/100 · Good |
| Install command | Confirmed | Confirmed |
| Transport | Local subprocess (stdio) | Local subprocess (stdio) |
| Auth | Not declared | Not declared |
| Pricing | Not declared | Not declared |
| Required env vars | None listed | None listed |
| Verified owner | Community listing | Community listing |
| GitHub stars | 1 | 19 |
| Directory activity | 1 views 0 installs 0 upvotes | 1 views 0 installs 0 upvotes |
| Tools | 38 | 25 |
| Full listing | View Arc Control (macOS) | View Public Browser |
list_tabsEvery tab, or narrow with `scope: "own"`, `query`, `space`, `window_id`. Rows are flagged `mine` and `isActive`.get_current_tabThe tab a call with no `tab_id` would act on.switch_to_tabMake a tab active in its window. `activate` also brings Arc to the front. Waits for you to stop typing.close_tabClose one tab.close_own_tabsClose every tab this agent opened, leaving the user's alone. `include_stale` also closes tabs leaked by a dead previous run of the same label.arc_statusArc's version and whether it is the frontmost app, owned tabs, the agent window (id, placement, display, minimized), whether Accessibility is available, your current idle time, whether the agent space exists, what a call with no `tab_id` resolves to (reported separately for read-only and for changi…open_urlOpen a URL in the agent window, launching Arc if needed. Waits for you to stop typing, and reports `focusRestored` and `waitedForUserMs`. Options for `new_tab`, target `space`, `little_arc`, `activate`, `wait_until_loaded`.go_backMove a tab through its history, verified by checking the URL actually changed.reload_tabReload a tab.wait_for_loadPoll until the document is ready, optionally until the URL contains a substring. Also reads Arc's `loading` flag (see [Loading tabs](#loading-tabs-and-stop_loading)), so a hung page shows up as `ready: "loading"` instead of a stalled call.stop_loadingPress the stop button on a tab that will not finish loading, which also unblocks every page tool on it.get_page_contentVisible text, whole page or every element matching a selector, joined. Always reports `matched`, so a partial answer is never silent, and flags truncation.virtual_deskList all open tabs with IDs, URLs and state. Call it first in every session, after a reconnect, or when unsure; reuse a listed tab ID instead of opening duplicates. Cheap, call liberally.view_pageSee what is on the page: text plus stable element refs for click/type/fill_form. Default filter 'interactive' lists actionable elements; for paragraphs or table cells call view_page(ref, filter: 'all'). Collapsed containers show as [eXX role, N items] — expand with view_page(ref: 'eXX', filter: 'all').clickClick an element by ref, CSS selector, visible text, or viewport x/y (canvas, pixel-precise targets). Dispatches real CDP mouse events. A click that opens a new tab is reported in the response. The DOM diff (NEW/REMOVED/CHANGED) arrives with the next page action, or here with wait_for_diff: true.typeType text into an input identified by ref or CSS selector. For 2+ fields use fill_form; for special keys (Enter, Escape, Tab, arrows) or shortcuts (Ctrl+K) use press_key.fill_formFill a whole form in one call: text inputs, <select> (by value or visible label), checkboxes (boolean) and radio buttons. One round-trip; partial errors do not abort and each field reports its own status. On per-field errors call view_page and retry the failing fields.press_keyPress a key or shortcut (Enter, Escape, Tab, arrows, Ctrl+K). Optionally focus an element first via ref or selector.scrollScroll the page, a container, or an element into view. Reports the position and content growth (scrollHeight delta — detects lazy-loaded content) and handles settle timing. Use container_ref/container_selector plus direction to scroll inside a container.dragDrag a source onto a target: HTML5 drag-and-drop (draggable lists, SortableJS, React DnD) and mouse drags (sliders, canvas, resize handles). Scrolls the source into view; says when no page reaction was detected. Source: from_ref/from_selector or from_x+from_y; target: to_ref/to_selector or to_x+to_y.navigateNavigate the active tab to a URL, or action 'back' / 'reload' (all refs become stale after reload). Waits for settle. Overwrites the user's active tab — check virtual_desk first; the first call per session is redirected to virtual_desk.switch_tabOpen a new tab, switch to a tab by ID (from virtual_desk), or close one. Prefer 'open' over navigate when the user's active tab must stay untouched. Refs from the previous tab are invalid afterwards — call view_page before acting.tab_statusActive tab's cached URL, title, ready state and errors — a cheap mid-workflow sanity check ('did my click navigate?'). Not for tab discovery or page content.wait_forWait until a condition holds: element visible, page text present, URL match, network idle, or a JS expression true. Prefer condition 'text' over a JS expression for 'has the page said X yet'. assert: true checks once and fails instead of waiting.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.
{
"mcpServers": {
"arc-control-macos": {
"command": "npx",
"args": [
"-y",
"arc-control-mcp@latest"
]
}
}
}
{
"mcpServers": {
"public-browser": {
"command": "npx",
"args": [
"-y",
"public-browser@latest"
]
}
}
}