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:
browser_navigateto a url. If no browser is open yet, one opens.- Read the page with
browser_extract, or list what can be clicked withbrowser_snapshot. - Click and type with
browser_act, using the labels from the snapshot (e1,e2, ...). browser_sessionwithaction: "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
| Tool | What it does |
|---|---|
browser_session | Open, close, list or inspect browser sessions. |
browser_navigate | Go to a url and return the page's interactive elements. |
browser_snapshot | List what can be clicked or typed on the page, labelled e1, e2. Labels change after every navigation. |
browser_act | Run one or more steps (click, fill, type, press, hover, check, uncheck, select, scroll, wait) and return only what changed. |
browser_extract | Read the page as markdown, plain text or a list of links. |
browser_screenshot | Take a JPEG screenshot of the page or of one element. Returned as an image the model can see. |
browser_wait | Wait for text or an element to appear or disappear, or for the url to change. |
browser_tabs | List, open, switch or close tabs. |
browser_evaluate | Run a JavaScript expression in the page and return its value. |
browser_observe | Console output, failed requests, downloads, dialogs and popup tabs seen so far. |
browser_status | Gateway 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
idleMinuteswhen 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: trueon open also records the page's own console output, readable throughbrowser_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 fileHeadless 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/mcpIf 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 theHostheader with the port removed, soBG_ALLOWED_HOSTS=gateway.internalcoversgateway.internal:9500.
BG_ALLOWED_HOSTS=gateway.internal browser-gateway serveA 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.