Skip to main content
Glama
README.md
# neu-browser-mcp

An MCP server that gives an agent a remote browser: create one, get a CDP URL to drive it, hand the user a live view, pick up the files it downloaded, and shut it down.

<p>
  <a href="LICENSE"><img alt="License: Apache 2.0" src="https://img.shields.io/badge/License-Apache_2.0-blue.svg"></a>
  <a href="CONTRIBUTING.md"><img alt="Contributions welcome" src="https://img.shields.io/badge/contributions-welcome-brightgreen.svg"></a>
</p>

<p align="center">
  <img alt="The agent calls neu-browser-mcp, which exposes the same tools across browser backends" src="docs/concept.gif" width="800">
</p>

## Why

- **Lifecycle only.** It does not click, type or navigate. The agent drives the browser over CDP with [agent-browser](https://github.com/vercel-labs/agent-browser), Playwright, or any CDP client.
- **Same tools on any backend.** Prompts and skills stay the same when the browser moves between `nap`, `kernel`, `browser-use` and `hyperbrowser`. Each backend is a small adapter behind one interface.
- **Credentials stay on the server.** The backend token never enters the agent's context.
- **Human handoff and downloads.** Backends with a live view give you a URL to watch or take over the browser. Files the browser downloads can be listed, linked, or read.

## Tools

| Tool | What it does |
| --- | --- |
| `create_browser` | Starts a browser and returns `browser_id`, `connect_url` (CDP WebSocket), `connect_command` (a ready-to-run `agent-browser connect …`), `cdp_url` (CDP over HTTP, for Playwright's `connectOverCDP`) and `live_view_url`. Takes an optional `timeout_seconds`. |
| `list_browsers` | Lists active browsers with the same connection info, so an agent can reconnect to one it lost track of. |
| `delete_browser` | Stops a browser. |
| `list_browser_files` | Lists files in the browser's sandbox, the backend's download directory by default. |
| `get_browser_file_url` | Returns a download URL for one file, or `ready: false` if it is not there yet. |
| `read_browser_file` | Returns the contents of one file: text as text, anything else as a base64 resource. The server reads with the backend's credentials. Reads are capped at 256 KB. |

## Backends

A capability a backend lacks shows up as `null` or as a missing tool. See [Adding a backend](#adding-a-backend).

| Backend | Timeout (default / max) | Live view | CDP over HTTP | Metadata | List files | Download link | Read file |
| --- | --- | :---: | :---: | :---: | :---: | :---: | :---: |
| [`nap`](https://github.com/neutree-ai/agent-platform) | 1 hour / 24 hours | ✅ | ✅ | ✅ | ✅ | ✅ | ❌ |
| [`kernel`](https://www.kernel.sh) | 1 hour idle / 72 hours idle | ✅ | ❌ | ✅ | ✅ | ❌ | ✅ |
| [`browser-use`](https://browser-use.com) | 1 hour / 4 hours | ✅ | ✅ | ✅ | ❌ | ❌ | ❌ |
| [`hyperbrowser`](https://hyperbrowser.ai) | 1 hour / 12 hours | ✅ | ❌ | ❌ | ❌ | ❌ | ❌ |

More backends are planned.

## Run it as a server

Requires Node.js 22 or newer. The server speaks MCP over stdio.

```json
{
  "mcpServers": {
    "browser": {
      "command": "npx",
      "args": ["-y", "neu-browser-mcp"],
      "env": {
        "NEU_BROWSER_BACKEND": "nap",
        "NAP_BROWSER_URL": "https://browser.example.com",
        "NAP_BROWSER_TOKEN": "<token>"
      }
    }
  }
}
```

`NEU_BROWSER_BACKEND` picks the backend and defaults to `nap`. Each backend then reads its own variables.

**`nap`**

| Variable | Meaning |
| --- | --- |
| `NAP_BROWSER_URL` | Base URL of the browser service API. Required. |
| `NAP_BROWSER_TOKEN` | Bearer token for the browser service. Required. |
| `NAP_BROWSER_PUBLIC_URL` | Base URL used in file download links. Set it when `NAP_BROWSER_URL` is an internal address users cannot reach. Defaults to `NAP_BROWSER_URL`. |

**`kernel`**

| Variable | Meaning |
| --- | --- |
| `KERNEL_API_KEY` | Kernel API key. Required. |

**`browser-use`**

| Variable | Meaning |
| --- | --- |
| `BROWSER_USE_API_KEY` | Browser Use Cloud API key. Required. |

**`hyperbrowser`**

| Variable | Meaning |
| --- | --- |
| `HYPERBROWSER_API_KEY` | Hyperbrowser API key. Required. |

## Use it as a library

To add the tools to an MCP server you already run, register them on it:

```ts
import { McpServer } from '@modelcontextprotocol/server'
import { createNapProvider, registerBrowserTools } from 'neu-browser-mcp'

const server = new McpServer({ name: 'my-server', version: '1.0.0' })

registerBrowserTools(server, {
  provider: createNapProvider({
    baseUrl: 'http://browser.internal:3005',
    publicBaseUrl: 'https://browser.example.com',
    token: async () => lookUpTokenFor(tenant),
  }),
  scope: { tenant_id: tenant.id },
})
```

The server can come from either generation of the MCP TypeScript SDK: `@modelcontextprotocol/server` (v2) or `@modelcontextprotocol/sdk` (v1).

`scope` is a set of tags that confines the tools to one tenant. Every browser they create carries the tags, `list_browsers` returns only browsers that carry them, and a tool given a browser ID refuses it unless that browser carries them. This is what keeps tenants apart when they share the backend's credentials. `token` can be a string or an async function; a function is called once, on first use.

To pick the backend from the environment, the way the standalone server does, use `providerFromEnv(process.env)`.

## Adding a backend

A backend implements `BrowserProvider` from [`src/types.ts`](src/types.ts):

```ts
interface BrowserProvider {
  readonly name: string
  readonly capabilities: ProviderCapabilities
  createSession(opts?: CreateSessionOptions): Promise<BrowserSession>
  listSessions(opts?: ListSessionsOptions): Promise<BrowserSession[]>
  releaseSession(id: string): Promise<void>
  readonly files?: BrowserFiles // list, and optionally downloadUrl and read
}

interface ProviderCapabilities {
  timeout: { kind: 'absolute' | 'idle'; defaultSeconds: number; maxSeconds: number }
  liveView: boolean
  httpCdp: boolean
  metadata: boolean
}
```

The required part is the session lifecycle plus a CDP WebSocket URL. Everything else is declared, and the tools adapt:

| Declaration | Effect when absent |
| --- | --- |
| `timeout` | Always present. Sets the default and maximum the agent is told about, and whether the timeout counts from creation (`absolute`) or inactivity (`idle`). |
| `liveView` | `live_view_url` is null and the tool descriptions do not mention it. |
| `httpCdp` | `cdp_url` is null, and the agent is told to use `connect_url` with Playwright too. |
| `metadata` | `registerBrowserTools` throws if a `scope` is given, since the backend could not keep tenants apart. |
| `files` member | No file tools are registered. |
| `files.downloadUrl` | `get_browser_file_url` is not registered. |
| `files.read` | `read_browser_file` is not registered. |

[`src/providers/nap.ts`](src/providers/nap.ts) is the reference implementation.

## Development

```bash
npm install
npx tsc --noEmit
npx biome check .
npm test
```

See [CONTRIBUTING.md](CONTRIBUTING.md).

## License

[Apache License 2.0](LICENSE)

TDQS

A3.9/5.0

Scored across 5 tools

Disambiguation4/5

Each tool has a distinct role: lifecycle (create/list/delete), file listing, and file URL retrieval. The only mild overlap is that list_browsers re-exposes connection info also returned by create_browser, but that is a documented reconnect convenience rather than a genuine ambiguity.

Naming Consistency5/5

All five tools follow a consistent snake_case verb_noun pattern (create_browser, list_browsers, delete_browser, list_browser_files, get_browser_file_url). The singular/plural browser vs. browsers variation is trivial and matches the resource semantics.

Tool Count5/5

Five tools is well-scoped for a browser-sandbox lifecycle server: create, list, delete, and two file-access tools. Nothing feels padded or missing to the point of being thin.

Completeness4/5

Full lifecycle (create/list/delete) plus sandbox file discovery and download URL generation are covered, which is appropriate given that actual browsing is delegated to the external agent-browser/CDP connection. Minor gaps exist, such as no way to extend a browser's timeout or delete sandbox files.

Maintenance

ActivityMaintained
ResponsivenessNo issues