BrowserGateway

MCP Server

Give an AI agent a real browser through the Model Context Protocol. Runs on your machine with no setup, or against any provider in your gateway.

browser-gateway includes an MCP (Model Context Protocol) server. MCP is the standard way AI agents such as Claude Code, Cursor and Codex call external tools. With it, an agent can open a browser, read pages, click and type, and close the browser when it is done. No Playwright or Puppeteer install is needed.

Using the hosted cloud instead? See Cloud MCP. Calling the Claude or Gemini API from your own code and using the model's built-in browser tool? See Computer Use.

Quick start

Add this to your agent's MCP configuration:

{
  "mcpServers": {
    "browser-gateway": {
      "command": "npx",
      "args": ["browser-gateway", "mcp"]
    }
  }
}

The agent starts the server itself. On the first browser call it finds Chrome on your machine (macOS, Linux or Windows), launches it and connects. There is nothing else to configure.

How an agent uses it

A typical task looks like this:

  1. browser_navigate to a url. If no browser is open yet, one opens.
  2. Read the page with browser_extract, or list what can be clicked with browser_snapshot.
  3. Click and type with browser_act, using the labels from the snapshot (e1, e2, ...).
  4. browser_session with action: "close" when the task is done.

The server teaches the agent these rules when it connects, so you do not need to put them in a prompt.

Results are kept small on purpose. A snapshot lists only elements you can interact with that are on screen, and an action returns only what changed on the page. Reading a page as text is much cheaper for the agent than a screenshot, so screenshots are for cases where layout matters.

Tools

ToolWhat it does
browser_sessionOpen, close, list or inspect browser sessions.
browser_navigateGo to a url and return the page's interactive elements.
browser_snapshotList what can be clicked or typed on the page, labelled e1, e2. Labels change after every navigation.
browser_actRun one or more steps (click, fill, type, press, hover, check, uncheck, select, scroll, wait) and return only what changed.
browser_extractRead the page as markdown, plain text or a list of links.
browser_screenshotTake a JPEG screenshot of the page or of one element. Returned as an image the model can see.
browser_waitWait for text or an element to appear or disappear, or for the url to change.
browser_tabsList, open, switch or close tabs.
browser_evaluateRun a JavaScript expression in the page and return its value.
browser_observeConsole output, failed requests, downloads, dialogs and popup tabs seen so far.
browser_statusGateway providers, sessions and queue state.

Every tool except browser_status takes an optional sessionId. It can be left out when only one browser is open.

Several steps in one call

browser_act accepts a list of steps, so filling a form is one call instead of three:

{
  "steps": [
    { "type": "fill", "ref": "e3", "text": "ada@example.com" },
    { "type": "fill", "ref": "e4", "text": "correct horse" },
    { "type": "click", "ref": "e5" }
  ]
}

It stops at the first failed step unless stopOnError is false, and it reports why an element could not be used (disabled, hidden or still moving).

Session rules

  • A browser closes after 5 minutes without a tool call. An agent can ask for 1 to 30 minutes with idleMinutes when it opens the session.
  • Every browser closes after 4 hours regardless of activity.
  • Tool results include the session id and the seconds left before the idle close.
  • Dialogs (alert, confirm, prompt) are answered automatically and reported in the next result.
  • pageConsole: true on open also records the page's own console output, readable through browser_observe.

Options

browser-gateway mcp                                     # Find and launch local Chrome (default)
browser-gateway mcp --headless                          # Launch Chrome without a window
browser-gateway mcp --cdp-endpoint ws://localhost:9222  # Use a browser that is already running
browser-gateway mcp --config gateway.yml                # Route to the providers in a config file

Headless or with a window

On macOS and Windows the browser opens with a visible window, so you can watch the agent work. On Linux without a display (Docker, CI) it runs headless. --headless forces headless everywhere:

{
  "mcpServers": {
    "browser-gateway": {
      "command": "npx",
      "args": ["browser-gateway", "mcp", "--headless"]
    }
  }
}

Multiple agents

Each agent process starts its own server and its own Chrome, on a free port it picks itself. Two editors side by side never share a browser.

Using remote providers

To give the agent browsers from your providers instead of local Chrome, point it at a config file:

# gateway.yml
providers:
  primary:
    url: wss://provider-one.example.com?token=${PROVIDER_ONE_TOKEN}
    limits:
      maxConcurrent: 10
    priority: 1
  backup:
    url: wss://provider-two.example.com?apiKey=${PROVIDER_TWO_KEY}
    limits:
      maxConcurrent: 5
    priority: 2
{
  "mcpServers": {
    "browser-gateway": {
      "command": "npx",
      "args": ["browser-gateway", "mcp", "--config", "gateway.yml"]
    }
  }
}

The gateway picks a healthy provider for each browser and fails over to the next one when a provider is down or full. See Providers.

Over HTTP

browser-gateway mcp talks to the agent over stdio, which means the agent starts the server as a child process. A gateway started with browser-gateway serve exposes the same tools over HTTP, so several agents on different machines can share one long-running gateway:

http://localhost:9500/mcp

If BG_TOKEN is set, send it as Authorization: Bearer <BG_TOKEN>.

GET /mcp/setup.md on the same gateway returns plain-text setup instructions an agent can follow on its own. Set BG_PUBLIC_URL when the gateway sits behind a proxy, so the instructions name the public address instead of the local one.

Reaching /mcp from another machine

When BG_TOKEN is not set, /mcp answers only requests whose Host header is a loopback name: localhost, 127.0.0.1, ::1 or 0.0.0.0, with or without a port. A request under any other hostname gets HTTP 403:

{
  "error": "Forbidden host",
  "message": "Set BG_TOKEN to reach /mcp on a non-loopback host, or add the host to BG_ALLOWED_HOSTS."
}

Most agents show this as a failed connection rather than the message itself, so a 403 on first connect from a remote machine or through a reverse proxy is usually this check.

Two ways to fix it:

  • Set BG_TOKEN. The hostname check does not run once a token is set, and the endpoint is authenticated instead. Pick this for anything reachable beyond your own machine.
  • Or list the hostname in BG_ALLOWED_HOSTS, comma-separated, to keep the gateway tokenless. The value is matched against the Host header with the port removed, so BG_ALLOWED_HOSTS=gateway.internal covers gateway.internal:9500.
BG_ALLOWED_HOSTS=gateway.internal browser-gateway serve

A tokenless gateway also binds to 127.0.0.1 by default, so reaching it from another machine takes HOST=0.0.0.0 as well. See Configuration.

Separately, if a request carries a browser Origin header that is neither the gateway's own address nor listed in BG_ALLOWED_ORIGINS, /mcp answers HTTP 403 with {"error": "Forbidden origin"}. This check runs whether or not BG_TOKEN is set. Agents running in a terminal do not send Origin, so it only affects browser-based clients.

Using with Playwright MCP

The gateway also works as the browser behind Playwright MCP, which has a larger tool set. Start a gateway, then point Playwright MCP at it to get routing and failover:

browser-gateway serve --config gateway.yml
{
  "mcpServers": {
    "playwright": {
      "command": "npx",
      "args": ["@playwright/mcp@latest", "--cdp-endpoint", "ws://localhost:9500/v1/connect"]
    }
  }
}

Use the built-in server when you want compact results and low token use. Use Playwright MCP when you need its wider set of actions. Both can run side by side.

On this page