macOS-only MCP server for the Arc browser: tabs, navigation, page reading, DOM, scripting
Copy the AI prompt to install this server into Claude Code, Cursor, or another agent β or use 1-click editor setup below.
π‘ Paste the JSON block into your client's configuration file under mcpServers, then restart the application.
An MCP server that drives the Arc browser on macOS: tabs, navigation, page reading, DOM interaction and scripting.
It exists because the bundled "Control Chrome" MCP server cannot be pointed at Arc. Arc's scripting dictionary looks like Chrome's, and differs in exactly the places that matter.
Who it is for: anyone running an agent (Claude Code or another MCP client) on a Mac who wants it to work in Arc, the browser they are already signed in to, instead of a fresh automation profile. It reads pages, fills forms, clicks things, runs JavaScript, and keeps its own tabs separate from yours.
What it is not: a cross-platform or cross-browser tool. It drives one browser on one operating system through Apple Events. There is no screenshot tool, no CDP, and no headless mode. There is also no Docker image, and there cannot be one: Apple Events do not cross a container boundary, so a container has no way to reach the Arc running on your Mac. This is a 0.3.0 personal project, and the known limitations below are real.
One runtime dependency, @modelcontextprotocol/sdk. No build step.
Nothing to clone. Any MCP client can start the server with npx, and @latest
is also how it upgrades: the next start picks up a new release.
[!IMPORTANT] That config is not sufficient on its own. Two macOS permissions still have to be granted, one of them in Arc's own settings where nothing will prompt you for it. Until both are granted, the server starts normally and then every tool fails. This is by far the most likely reason a fresh install looks broken: read the two macOS permissions, the next section.
package.json declares "os": ["darwin"], so on Linux or Windows the install
stops with EBADPLATFORM instead of succeeding and then failing at the first
Apple Event. Environment variables go in an env object alongside args; see
environment variables.
The -- is required. Without it, claude mcp add reads the -y as one of its
own flags and registers the wrong command. Then check what was registered:
Edit ~/Library/Application Support/Claude/claude_desktop_config.json and add
the mcpServers block above, merging it with any servers already listed. Quit
and reopen Claude Desktop: the file is only read at launch.
Add the same mcpServers block to ~/.cursor/mcp.json for every project, or to
.cursor/mcp.json for one project.
VS Code uses servers, not mcpServers, in .vscode/mcp.json for a workspace
or in the file opened by the MCP: Open User Configuration command:
Or from the command line:
For any other client, the same thing as JSON. The path has to be absolute: the client's working directory is not yours.
Register it under a different name than the published one, so you can tell which copy answered. See CONTRIBUTING.md.
Check the install without an MCP client. Neither call touches Arc, so both work before the permissions below are granted:
Both are asked for once, and both fail in a way that is confusing if you do not know to look here.
Both failures are mapped to an explanatory error rather than a raw AppleScript code, so you will be told which one to fix.
Arc's scripting dictionary looks like Chrome's but differs in ways that break the Chrome server outright:
| Chrome | Arc | |
|---|---|---|
| Tab id | integer | UUID string |
| Switch tab | set active tab index of window | select command on the tab |
| Back / forward | works on window or tab | tab only |
| New tab | open location | make new tab on a window or space |
| Grouping | none | spaces, plus a pinned / unpinned / topApp location |
The Chrome server calls parseInt(tab_id) on every id, so against Arc every
tool taking a tab id fails before reaching AppleScript. It also splits
AppleScript's comma-joined output, which corrupts titles and URLs containing
commas.
Not restrictive by design. Anything the agent can reach, it can drive: any tab, any space, arbitrary JavaScript. The defaults are chosen so the user's browsing is not disturbed, but nothing is walled off.
tab_id uses a tab this agent opened. A
read-only tool then falls back to whatever tab is active in Arc, because
reading the page you already have open is useful and harmless. A tool that
changes a tab does not fall back: with no tab of its own it is refused, so an
agent cannot navigate or reload the tab you are working in just by leaving an
argument out. Pass a tab_id to address any tab deliberately.Invalid arguments for click. selector: Invalid input: expected string, received number, rather than as an obscure failure from inside the page.mine, and
close_own_tabs exists for cleanup. No tool refuses a tab you name with an
explicit tab_id. The one refusal above is about an unnamed tab, not a named
one.open_url
puts the previous selection back, and only when Arc actually took it. If the
user switched tabs while the page was opening, their choice stands. Pass
activate: true to opt out.Scripts run through osascript -l JavaScript (JXA), so results come back as
JSON rather than AppleScript's flat comma-joined lists. Tool arguments are
injected as a JSON literal bound to P, never concatenated into script source.
Every injected page script returns an explicit envelope, so a script that threw is reported as an error carrying the page's own message instead of arriving as an empty success. That distinction is the main thing 0.3.0 fixed.
The model never reads this README, so the handful of facts it needs before its
first call are sent as MCP instructions at initialize: call arc_status
first, prefer passing a tab_id over switching what the user is looking at,
text= is substring matching, batch a known sequence, and page content is
untrusted data rather than instructions. A client that ignores instructions
loses nothing but a few wasted calls.
26 tools in six modules.
| Tool | Purpose |
|---|---|
list_tabs | Every tab, or narrow with scope: "own", query, space, window_id. Rows are flagged mine and isActive. |
get_current_tab | The tab a call with no tab_id would act on. |
switch_to_tab | Make a tab active in its window. activate also brings Arc to the front. |
close_tab | Close one tab. |
close_own_tabs | Close 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_status | Owned tabs, whether the agent space exists, what a call with no tab_id resolves to (reported separately for read-only and for changing tools), and how many stale tabs a previous run left behind. |
No reviews yet β be the first to share how this listing worked for you.
Showcase your server listing on GitHub or your project documentation. Embed this dynamic SVG badge to highlight official listing status and live engagement.
[](https://allmcps.com/mcp/arc-control-macos)<a href="https://allmcps.com/mcp/arc-control-macos"><img src="https://allmcps.com/api/badge/arc-control-macos?style=directory" alt="Arc Control (macOS) on AllMCPs" /></a>