Connecting Mastra agents to Browserless
Mastra is a TypeScript framework for building AI agents, workflows, and tools. This guide connects a Mastra agent to Browserless two ways: as the browser behind Mastra's AgentBrowser, so the agent can click, type, and navigate, and as an MCP tool source for one-shot scraping and search.
- A Browserless API token from your account dashboard
- Node.js 22.13 or higher
- An LLM API key (Anthropic, OpenAI, etc.) for the agent's model
- Familiarity with Mastra agents
Which approach to use
| Approach | Use it when | What the agent gets |
|---|---|---|
AgentBrowser + Browserless | The agent has to interact with pages: log in, fill forms, click through flows | Mastra's 16 browser tools (browser_goto, browser_click, browser_snapshot, ...) running on a Browserless session |
| Browserless MCP server | The agent needs page content, search results, or crawls, without holding browser state | Browserless MCP tools (browserless_smartscraper, browserless_search, browserless_crawl, ...) |
You can give one agent both. If you'd rather hand off the whole browsing task instead of driving it step by step from Mastra, use the Browser Agent.
Connect AgentBrowser to Browserless
Set your API keys
Copy your API token from the account dashboard, then set it with your model provider's key:
- .env file
- Command line
BROWSERLESS_TOKEN=YOUR_API_TOKEN_HERE
ANTHROPIC_API_KEY=your-anthropic-keyexport BROWSERLESS_TOKEN=YOUR_API_TOKEN_HERE
export ANTHROPIC_API_KEY=your-anthropic-keyInstall dependencies
- npm
- pnpm
- yarn
npm install @mastra/core @mastra/agent-browserpnpm add @mastra/core @mastra/agent-browseryarn add @mastra/core @mastra/agent-browserYou don't need
npx playwright install. WithcdpUrlset, Browserless supplies the browser.Create the agent
Create
src/mastra/agents/browser-agent.ts. Pass your Browserless endpoint ascdpUrland attach the browser to the agent:import { Agent } from "@mastra/core/agent";
import { AgentBrowser } from "@mastra/agent-browser";
const TOKEN = process.env.BROWSERLESS_TOKEN;
export const browser = new AgentBrowser({
// A CDP URL makes AgentBrowser connect instead of launching local Chromium.
cdpUrl: `wss://production-sfo.browserless.io?token=${TOKEN}`,
});
export const browserAgent = new Agent({
id: "browser-agent",
name: "Browser Agent",
model: "anthropic/claude-sonnet-5-5",
browser,
instructions: `You are a web research assistant.
When interacting with pages:
1. Use browser_snapshot to get the page state and element refs.
2. Use the refs (like @e1, @e2) to target clicks and typing.
3. Take another snapshot after each action to confirm the result.`,
});Run the agent
Create
src/run.ts:import "dotenv/config";
import { browser, browserAgent } from "./mastra/agents/browser-agent";
async function main() {
try {
const result = await browserAgent.generate(
"Go to https://news.ycombinator.com and list the titles of the top 3 stories."
);
console.log(result.text);
} finally {
// Close explicitly so the Browserless session ends now, not at its timeout.
await browser.close();
}
}
main();npm install dotenv && npx tsx src/run.tsResponse:
Here are the top 3 stories on Hacker News right now:
1. <story title>
2. <story title>
3. <story title>
How it works
cdpUrlswitchesAgentBrowserfrom launching Chromium to connecting over CDP. Browserless starts a session when the agent first uses a browser tool.- With
cdpUrlset, every thread shares one browser. Mastra throws if you combinecdpUrlwithscope: 'thread', because it can't spawn separate browsers over one CDP connection. To isolate conversations, create a separateAgentBrowserand agent for each one, so each gets its own Browserless session. cdpUrlalso accepts a function, so you can build the URL when the browser connects, for example to pick a region or proxy country at runtime:
const browser = new AgentBrowser({
cdpUrl: () =>
`wss://production-sfo.browserless.io?token=${process.env.BROWSERLESS_TOKEN}&proxy=residential&proxyCountry=us`,
});
Add Browserless MCP tools
For scraping, search, and crawling, connect Mastra's MCPClient to the hosted Browserless MCP server. Each tool call is a single request, so there's no session for the agent to manage.
npm install @mastra/core @mastra/mcp dotenv
Top-level await needs ES modules, so set "type": "module" in package.json before running this file with npx tsx.
import "dotenv/config";
import { Agent } from "@mastra/core/agent";
import { MCPClient } from "@mastra/mcp";
const mcp = new MCPClient({
servers: {
browserless: {
url: new URL("https://mcp.browserless.io/mcp"),
requestInit: {
headers: { Authorization: `Bearer ${process.env.BROWSERLESS_TOKEN}` },
},
},
},
});
try {
// listTools() swallows connection errors and returns no tools, so check explicitly.
const { tools, errors } = await mcp.listToolsWithErrors();
if (errors.browserless) {
throw new Error(`Browserless MCP server unavailable: ${errors.browserless}`);
}
const researchAgent = new Agent({
id: "research-agent",
name: "Research Agent",
model: "anthropic/claude-sonnet-5-5",
instructions: "Use the Browserless tools to search the web and read pages before answering.",
tools,
});
const result = await researchAgent.generate(
"Scrape https://browserless.io/pricing and summarize the plans."
);
console.log(result.text);
} finally {
// Close the MCP connection even if a call fails, so the process can exit.
await mcp.disconnect();
}
Response:
Browserless offers <n> plans: ...
Tool names arrive namespaced by server, for example browserless_browserless_smartscraper. See the MCP tool reference for what each tool does.
Configure the browser session
For the AgentBrowser route, set every Browserless session option on cdpUrl. Routes such as /stealth go in the path, and options such as proxies and timeouts go in the query string.
Stealth
Use the stealth route for sites with bot detection:
cdpUrl: `wss://production-sfo.browserless.io/stealth?token=${TOKEN}`,
Residential proxies
Route traffic through residential IPs for geo-targeted pages or sites that rate-limit datacenter IPs:
cdpUrl: `wss://production-sfo.browserless.io?token=${TOKEN}&proxy=residential&proxyCountry=us`,
See proxies for every proxy option.
Regional and self-hosted endpoints
| Deployment | cdpUrl |
|---|---|
| US West (San Francisco) | wss://production-sfo.browserless.io?token=${TOKEN} |
| Europe (London) | wss://production-lon.browserless.io?token=${TOKEN} |
| Europe (Amsterdam) | wss://production-ams.browserless.io?token=${TOKEN} |
| Self-hosted Docker | ws://localhost:3000?token=${TOKEN} |
FAQ & Troubleshooting
I'm getting browser executable is missing
AgentBrowser is launching locally because cdpUrl is empty. Check that BROWSERLESS_TOKEN is loaded before the agent module is imported. Import dotenv/config first, or export the variable in your shell.
Two conversations are seeing each other's pages
With cdpUrl set, threads share one browser, and Mastra rejects scope: 'thread' alongside cdpUrl. Create a separate AgentBrowser and agent per conversation so each gets its own Browserless session, and close each browser when its conversation ends.
The session closes partway through a long task
Browserless sessions have a timeout. Add a timeout query parameter in milliseconds to cdpUrl, for example &timeout=600000, up to your plan's limit. See launch options.
The agent gets blocked or hits CAPTCHAs
Switch cdpUrl to the stealth route and add a residential proxy. For sites that still block, the Browser Agent handles CAPTCHA solving for you.