sendblue-browser

sendblue-browser

MCP server that wraps sendblue-browser-use's HTTP API, providing tools to control a stealth-patched Chromium browser with persistent sessions, navigation, screenshots, and script execution.

Category
Visit Server

README

sendblue-browser-use

A standalone debug browser for agents. Stealth-patched Chromium behind a tiny HTTP API, with persistent named sessions, an easy purge endpoint, configurable auto screenshots, and CDP attach so any client (Playwright, Puppeteer, undetected-chromedriver, custom scripts) can drive it.

It runs as its own process — not tied to any one Claude / Cursor / Codex session — so multiple agents can share it concurrently, debug each other's flows, and reuse a logged-in session without paying the auth tax every run.

Why this exists

Need What you get
Not coupled to a single agent Long-running HTTP daemon. Any tool with a bearer token can drive it.
Doesn't look automated Chromium patched via patchright: navigator.webdriver is hidden, CDP detection vectors are patched, real Chromium binary (no Playwright fingerprints). Passes typical Cloudflare Turnstile and similar low-friction checks; not for hostile scraping at scale.
Reusable sessions Named persistent profiles. Log in once, every subsequent debug run reuses the cookies.
One-click reset POST /sessions/:name/purge clears cookies + active-page storage but keeps the session id, so your client code keeps working.
Multi-agent friendly Each session is an isolated BrowserContext (or its own profile). Run 5+ debug sessions in parallel without state bleed.
CDP attach GET /sessions/:name/cdp-url returns the shared browser CDP URL for non-persistent sessions, so Playwright / Puppeteer / any CDP client can connect() to the debug browser.
Auto evidence Navigation screenshots follow NAV_SCREENSHOT_POLICY (default: headless only). Optional Playwright traces.
HTTP-controlled Plain curl works. No SDK required.
Self-contained deploy One docker compose up on any Mac mini / EC2 / Codespace. No host Chromium needed.

Quick start (local)

Prereqs: Bun >=1.3 and Node.js >=20 on your PATH.

git clone https://github.com/sendblue-api/sendblue-browser-use.git
cd sendblue-browser-use
bun install
bun --bun x patchright install chromium

cp .env.example .env
# generate and set the required token
TOKEN="$(openssl rand -hex 32)"
sed -i.bak "s/^BROWSER_USE_API_KEY=.*/BROWSER_USE_API_KEY=$TOKEN/" .env
rm .env.bak

bun run dev

Then in another terminal:

source .env
TOKEN="Authorization: Bearer $BROWSER_USE_API_KEY"

# 1. create a persistent session (log in here once, reuse it forever)
curl -s -X POST http://127.0.0.1:8787/sessions \
  -H "$TOKEN" -H "Content-Type: application/json" \
  -d '{"name":"qa","persistent":true}'

# 2. navigate
curl -s -X POST http://127.0.0.1:8787/sessions/qa/navigate \
  -H "$TOKEN" -H "Content-Type: application/json" \
  -d '{"url":"https://trybloom.so/"}'

# 3. screenshot
curl -s http://127.0.0.1:8787/sessions/qa/screenshot?fullPage=true \
  -H "$TOKEN" -o /tmp/qa.png

# 4. wipe state without losing the session
curl -s -X POST http://127.0.0.1:8787/sessions/qa/purge -H "$TOKEN"

Quick start (Docker)

echo "BROWSER_USE_API_KEY=$(openssl rand -hex 32)" > .env
docker compose up -d

Same API, exposed on 127.0.0.1:8787. Data persists in ./data/. CDP stays loopback-only inside the container by default; to attach CDP from the host, uncomment CDP_BIND and the 127.0.0.1:9222:9222 port mapping in docker-compose.yml on a trusted machine only.

Install into your agent

The repo ships three packaging layers so any agent can drive the daemon with one line of config.

MCP (Claude Desktop, Codex, Cursor, Antigravity, Cline, Windsurf)

The cross-agent path. Wraps the HTTP API as MCP tools (health, create_session, navigate, screenshot, script, get_cdp_url, purge_session, etc).

{
  "mcpServers": {
    "sendblue-browser": {
      "command": "npx",
      "args": ["-y", "sendblue-browser-mcp@0.2.3"],
      "env": {
        "BROWSER_USE_URL": "http://127.0.0.1:8787",
        "BROWSER_USE_API_KEY": "<same token you start the daemon with>"
      }
    }
  }
}

Drop into ~/.cursor/mcp.json, ~/Library/Application Support/Claude/claude_desktop_config.json, ~/.gemini/config/mcp_config.json, etc. For Codex:

codex mcp add sendblue-browser \
  --env BROWSER_USE_URL=http://127.0.0.1:8787 \
  --env BROWSER_USE_API_KEY=<daemon-token> \
  -- npx -y sendblue-browser-mcp@0.2.3

Wrapper source: mcp/.

Claude Code plugin

/plugin install sendblue-api/sendblue-browser-use

Pulls .claude-plugin/plugin.json + skills/sendblue-browser/SKILL.md from this repo.

Codex / Antigravity / Cline skill

The same skills/sendblue-browser/SKILL.md works as a skill across Codex, Google Antigravity, and Cline. Copy the folder into:

Agent Path
Codex ~/.codex/skills/sendblue-browser/
Antigravity ~/.gemini/skills/sendblue-browser/
Cline enable Skills in Settings, then drop in its skills directory

AGENTS.md repos

If your repo already follows the agents.md convention, the AGENTS.md in this repo doubles as a copy-pasteable section for any consumer repo that wants to call this daemon.

API

All routes require Authorization: Bearer $BROWSER_USE_API_KEY except /health.

Method Path Body / Query Returns
GET /health { ok, service, version, sessions, defaultHeadless, navScreenshotPolicy }
GET /sessions { sessions: [...] }
POST /sessions { name, persistent?, headless?, viewport?, userAgent?, locale?, timezone?, traces?, proxy? } { session }
GET /sessions/:name session info + current page url/title
POST /sessions/:name/purge { session } — clear cookies + active-origin storage, keep session id
DELETE /sessions/:name { ok } — close context, remove profile
POST /sessions/:name/navigate { url, waitUntil?, timeoutMs? } { url, title, status }
GET /sessions/:name/screenshot ?fullPage=true&selector=... image/png
POST /sessions/:name/script { code, timeoutMs? } (≤200kB) { result } — code runs in page context
GET /sessions/:name/cookies ?url=... { cookies }
POST /sessions/:name/cookies { cookies: [...] } { ok }
GET /sessions/:name/console ?limit=100 { messages: [...] } (ring buffer)
GET /sessions/:name/cdp-url { cdpUrl, targetId } — for non-persistent sessions only

POST /sessions/:name/script passes code directly to Playwright's page.evaluate. Send a JavaScript expression, or wrap statements in an IIFE. Scripts time out after 30s by default; pass timeoutMs up to 120000, or 0 to disable the request timeout.

{ "code": "(() => { const x = 1; return x; })()" }

Errors

All non-2xx responses share the same envelope. Failures from Playwright are truncated to one line before being returned; the full message is logged server-side with proxy credentials redacted.

{ "error": { "code": "navigate_failed", "message": "net::ERR_NAME_NOT_RESOLVED" } }
Status Code Meaning
400 empty_body request body is required but was missing
400 malformed_json body was not valid JSON
400 invalid_body body failed Zod validation (see error.details)
401 unauthorized missing or wrong bearer token
404 not_found session does not exist
409 already_exists a session with that name is already running
409 cdp_unavailable_for_persistent_session persistent sessions don't expose the shared CDP url
422 script_failed /script request was valid but the in-page eval threw or timed out
500 internal_error unexpected — check server logs
502 navigate_failed screenshot_failed cookie_read_failed cookie_write_failed session_unreachable Chromium-side failure

Session options

{
  name: string;            // required, [a-z0-9_-]
  persistent?: boolean;    // default true — profile data survives restarts
  headless?: boolean | "new"; // persistent sessions only; non-persistent uses DEFAULT_HEADLESS
  viewport?: { width, height };
  userAgent?: string;
  locale?: string;         // default "en-US"
  timezone?: string;
  traces?: boolean;        // capture a Playwright trace.zip on session close
  proxy?: { server, username?, password?, bypass? };
}

Cookie shape

POST /sessions/:name/cookies accepts up to 500 Playwright-style cookies:

{
  cookies: Array<{
    name: string;
    value: string;
    url?: string;       // absolute URL, or domain + path
    domain?: string;
    path?: string;
    expires?: number;
    httpOnly?: boolean;
    secure?: boolean;
    sameSite?: "Strict" | "Lax" | "None";
  }>;
}

Each cookie must include either url or both domain and path.

Persistent vs non-persistent

  • persistent: true (default) — cookies, localStorage, IndexedDB persist under ~/.sendblue-browser-use/profiles/<name>/. After a daemon restart, recreate the same named persistent session to reuse that profile. CDP attach is not available for these sessions because each gets its own private browser instance.
  • persistent: false — shares the central Chromium process via a fresh BrowserContext. State is in-memory only. CDP attach IS available via /sessions/:name/cdp-url, which returns the shared browser-level CDP endpoint plus a targetId for the session page. CDP clients can see the shared debug browser, so keep the CDP port local and trusted and select the returned target before driving.

Pick persistent for "log in once, debug for a week" workflows. Pick non-persistent when you need to attach Playwright / Puppeteer directly.

Examples

Stealth notes

This is built on patchright, a maintained fork of Playwright that:

  • Hides navigator.webdriver
  • Removes CDP runtime markers
  • Drops Playwright-specific extensions and command flags
  • Uses real Chromium so most fingerprint surfaces match a normal user

We deliberately do not pass --disable-blink-features=AutomationControlled or override userAgent/viewport by default — patchright handles those internally and our overrides would defeat its rebrowser patches. Pass an explicit userAgent per session only if you have a reason.

Why patchright over alternatives:

  • vs nodriver / undetected-playwright — patchright is actively maintained, matches Playwright's API surface, and integrates cleanly with the existing TypeScript ecosystem.
  • vs Camoufox — Camoufox uses Firefox with deeper fingerprint randomization, better for hostile scraping at scale. patchright is the right pick for QAing your own auth flows (Clerk, Stripe, Google OAuth, Cloudflare Turnstile) without the maintenance burden.

Storage

~/.sendblue-browser-use/
├── profiles/
│   └── <session>/          # persistent profile dir
└── runs/
    └── <session>/
        ├── 2026-05-25T...-nav.png   # automatic nav screenshot when policy enables it
        └── 2026-05-25T...-trace.zip # if traces:true at create

POST /sessions/:name/purge clears cookies and permissions context-wide, then clears localStorage, sessionStorage, IndexedDB, ServiceWorker registrations, CacheStorage, and the console buffer for currently open pages/origins. It does not enumerate every historical origin in a persistent profile, does not delete the on-disk profile (so the session id stays valid), and does not clear browser-level HTTP cache or HSTS state. To wipe the profile too, DELETE /sessions/:name and recreate.

Automatic navigation screenshots follow NAV_SCREENSHOT_POLICY:

  • headless (default): only effective-headless sessions write *-nav.png, avoiding visible capture flicker in headed browsers.
  • always: preserve legacy behavior and capture every navigation, including headed sessions.
  • off: disable automatic navigation screenshots.

Auto-screenshots are capped at MAX_NAV_SCREENSHOTS per session (default 200, oldest deleted first). Set MAX_NAV_SCREENSHOTS=0 to disable automatic screenshots entirely. Call /screenshot explicitly when you need evidence from a headed browser.

Security

  • The HTTP API binds to 127.0.0.1 by default. Setting BIND=0.0.0.0 exposes you to your LAN — put a reverse proxy with auth in front before doing this. The Docker image sets BIND=0.0.0.0 so host port-publishing works, but docker-compose.yml publishes only to 127.0.0.1. If you docker run -p 8787:8787 directly, you are choosing to expose the HTTP API.
  • CDP is bound to CDP_BIND (default 127.0.0.1; the Docker image default is also loopback-only). The default docker-compose.yml does not publish CDP; if you opt into host CDP attach, publish it to 127.0.0.1 only. Anyone who can reach this port gets full in-browser RCE — keep it on loopback.
  • POST /sessions/:name/script runs arbitrary JS in the page context. The bearer token is the only gate. Don't share the token.
  • Treat the bearer token as local browser/network control. A token holder can navigate Chromium to any http(s) URL reachable from the daemon host, including localhost and private-network services.
  • POST /sessions/:name/navigate only accepts http(s) URLs — file://, chrome://, etc. are rejected.
  • Healthcheck (GET /health) is public so Docker/k8s probes work without a token; it returns service metadata only.

Architecture

HTTP :8787  ──►  Hono router  ──►  session manager  ──►  patchright Chromium
                                    │                    └─►  CDP :9222 (loopback; Docker opt-in)
                                    └─►  on-disk profiles + runs

Inspired by the Sendblue channel-server pattern (Bun + Hono + bearer auth + simple HTTP surface).

License

MIT. See LICENSE.

Recommended Servers

playwright-mcp

playwright-mcp

A Model Context Protocol server that enables LLMs to interact with web pages through structured accessibility snapshots without requiring vision models or screenshots.

Official
Featured
TypeScript
Magic Component Platform (MCP)

Magic Component Platform (MCP)

An AI-powered tool that generates modern UI components from natural language descriptions, integrating with popular IDEs to streamline UI development workflow.

Official
Featured
Local
TypeScript
Audiense Insights MCP Server

Audiense Insights MCP Server

Enables interaction with Audiense Insights accounts via the Model Context Protocol, facilitating the extraction and analysis of marketing insights and audience data including demographics, behavior, and influencer engagement.

Official
Featured
Local
TypeScript
VeyraX MCP

VeyraX MCP

Single MCP tool to connect all your favorite tools: Gmail, Calendar and 40 more.

Official
Featured
Local
graphlit-mcp-server

graphlit-mcp-server

The Model Context Protocol (MCP) Server enables integration between MCP clients and the Graphlit service. Ingest anything from Slack to Gmail to podcast feeds, in addition to web crawling, into a Graphlit project - and then retrieve relevant contents from the MCP client.

Official
Featured
TypeScript
Kagi MCP Server

Kagi MCP Server

An MCP server that integrates Kagi search capabilities with Claude AI, enabling Claude to perform real-time web searches when answering questions that require up-to-date information.

Official
Featured
Python
E2B

E2B

Using MCP to run code via e2b.

Official
Featured
Neon Database

Neon Database

MCP server for interacting with Neon Management API and databases

Official
Featured
Exa Search

Exa Search

A Model Context Protocol (MCP) server lets AI assistants like Claude use the Exa AI Search API for web searches. This setup allows AI models to get real-time web information in a safe and controlled way.

Official
Featured
Qdrant Server

Qdrant Server

This repository is an example of how to create a MCP server for Qdrant, a vector search engine.

Official
Featured