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.