Vercel AI SDK

ai-sdk-tool-antibrow gives an AI SDK agent four browser tools that all drive the same persistent profile. A tool that starts a fresh Chromium is fine for reading a public page and useless for anything behind a login: the session dies with the process. Here the cookies, the storage, the fingerprint and the exit IP are the same ones the agent used last time.

browserGoto url Navigates the profile; returns the HTTP status, the page title and the final URL.
browserRead selector? Visible text, from one element or the whole page, truncated to readLimit and flagged when it was.
browserClick selector Clicks, then reports the URL it landed on.
browserFill selector, text Types into an input.

Install

npm install ai-sdk-tool-antibrow
export ANTI_DETECT_BROWSER_KEY=ab_live_...

ai and zod are peer dependencies - the versions your app already has. The browser engine downloads on first launch and is cached. Unlimited local profiles and one concurrent browser are free; the SDK reference has every launch option.

Use it with generateText

import { gateway, generateText, stepCountIs } from 'ai'
import { antibrowTools } from 'ai-sdk-tool-antibrow'

const browser = antibrowTools({ profile: 'research-01' })

try {
  const { text } = await generateText({
    model: gateway('openai/gpt-5-mini'),
    prompt: 'Open https://example.com and tell me the heading.',
    tools: browser.tools,
    stopWhen: stepCountIs(5),
  })
  console.log(text)
} finally {
  await browser.close()
}

Close the session in a finally: a closed browser leaves nothing behind, an abandoned one is a whole Chromium still running. The same object works with streamText and with any agent loop that takes AI SDK tools - they are ordinary tool() definitions, with nothing AntiBrow-specific about how they are called.

The four tools

antibrowTools() is the shortcut: one session, four tools, one close(). Each tool is also exported on its own, so you can take two of them or rename them - what matters is that they share a session, which is what makes them composable: a read reads whatever the last navigation opened.

import { AntibrowSession, browserGoto, browserRead } from 'ai-sdk-tool-antibrow'

// Only want two of them, or want to name them yourself:
const session = new AntibrowSession({ profile: 'research-01' })
const tools = { open: browserGoto(session), read: browserRead(session) }

Options

antibrowTools({
  profile: 'research-01',                // same name -> same fingerprint, cookies, storage
  proxy: 'http://user:pass@host:5001',   // one exit IP per profile
  temporary: true,                       // discard the profile on close
  headless: false,
  readLimit: 4000,                       // characters a read may return
  key: process.env.ANTI_DETECT_BROWSER_KEY,
})

Two agents that must not share an identity need two profile names, not two tabs. How many may run at once is your plan's concurrency limit, and a launch over it raises rather than silently queueing.

How this was tested

Two checks ship with the package (src/test.ts). First the tools run for real, against a live profile:

const browser = antibrowTools({ profile: 'ai-sdk-selfcheck', temporary: true })
await browser.tools.browserGoto.execute({ url: 'https://example.com' }, ctx)
// -> { status: 200, title: 'Example Domain', url: 'https://example.com/' }
await browser.tools.browserRead.execute({ selector: 'h1' }, ctx)
// -> { url: 'https://example.com/', truncated: false, text: 'Example Domain' }

Then the whole AI SDK tool-call path runs with a mock model standing in for the LLM, so the input schema, the tool-call dispatch and the tool result round-trip are exercised rather than assumed:

tool results -> [
 {
  "type": "tool-result",
  "toolCallId": "call-1",
  "toolName": "browserGoto",
  "input": { "url": "https://example.com" },
  "output": { "status": 200, "title": "Example Domain", "url": "https://example.com/" },
  "dynamic": false
 }
]

What it does not do

It does not promise that a given site will accept an automated session. A persistent identity removes the tells that come from starting over every run - a fresh profile, a stock automation fingerprint, your own IP - and that is all it removes. Our dated measurements, including the checks that fail, are published under reports. Driving a browser from an agent without writing code? See the MCP server; using LangChain instead? See the LangChain guide.