Developers

Headless runs

Run Arx without windows from scripts, agents and CI, with a local MCP endpoint and only the grants you pass.

This page is a first draft. It may change before the public beta.

Arx --headless starts the browser without windows, a Dock icon or a menu bar. It runs the same browser and the same local MCP server as the app, and gives its own MCP client only the grants you pass. Every run ends with a clean stopped event.

Start a run

/Applications/Arx.app/Contents/MacOS/Arx --headless --allow read,screenshots,content --viewport 1280x800
OptionMeaning
--allowComma-separated grants for the run's MCP client: read (required), screenshots, content, interaction, manage. Default read. The client sees every space of the run's profile. manage approves changes in advance for the whole run, or until --timeout.
--profile <absolute path>Use and keep a persistent profile. By default each run gets a fresh private profile that is deleted at exit.
--viewport <W>x<H>Page size in points, 320×240 to 3840×2160. Default 1280×800.
--timeout <seconds>Optional deadline, 10 to 86,400. The run exits with 75 when it's reached. Default: none.
--mcp-configuration <absolute path>Where to write the MCP client configuration. The file must not exist yet; it's created readable only by you. Default: mcp.json in the run's directory.
--url <url>First page for a new profile: http(s) or about:blank (default).

Start your MCP client with the mcpServers.arx entry of that configuration file. It launches Arx's bundled helper with the run's one-time credential.

Events

Stdout carries only events, one JSON object per line:

EventFields
readyconfiguration, profile, viewport, grants, grantsExpireAt, timeoutSeconds, pid
stoppingreason: signal, timeout, failure, parentExited, launcherExited or inputClosed
errorcode, message, exitCode
diagnosticcode, for example shutdownSlow (with seconds) or authenticationProtected
rendererFailedtabID of a page whose renderer died. A reload recovers it.
authenticationRequiredtabID of a page that hit an HTTP authentication challenge
userActionRequiredtabID and prompts (webAuthentication, federatedSignIn)
stoppedexitCode, suppressedDialogs
{"event":"ready","grants":["read","screenshots","content"],"viewport":"1280x800", …}
{"event":"stopping","reason":"signal"}
{"event":"stopped","exitCode":0,"suppressedDialogs":0}

Wait for ready before connecting. Diagnostics from Arx and Chromium go to stderr.

Stopping

Send SIGTERM or SIGINT. A run without --timeout also stops when:

  • the process that started it exits;
  • an outer launcher in a chain such as npx → node → Arx exits;
  • stdin was a pipe at startup and reaches end of file. Keep the pipe open for as long as the run should last.

Exit codes

CodeMeaning
0Stopped on request
64Invalid options
69Engine or MCP endpoint unavailable
70Startup failure
73Profile in use or can't be created
75--timeout reached
2Shutdown hung and was forced to exit

Nobody is watching

A headless run never waits for a person:

  • A change outside the run's grant fails with userActionRequired.
  • Page alert, confirm and prompt dialogs get Chromium's default answer, and permission requests are denied.
  • HTTP authentication, passkey and federated sign-in prompts are cancelled and reported as events. Sign-in handoffs aren't available.
  • Arx writes no screenshot files. browser_capture returns the image to the client.

A persistent profile is locked for the run: a second run on it, or Arx already open on it, is rejected with profileInUse.

macOS draws pages through the window server, so a headless run needs a logged-in graphical session for the same user. It doesn't work from a plain SSH session or a launchd daemon, so CI needs a runner that is logged in.
Planned
Headless runs without a logged-in graphical session, such as from SSH or launchd.