Firecrawl just published a walkthrough for @mastra/browser-firecrawl, the browser provider their team built for Mastra. It gives your agent a hosted browser for pages behind a login, with no Chromium running on your machine. Their demo logs into Sauce Labs' test store and checks out a backpack.
Their post covers setup. We'll walk through what happens inside Mastra when the agent logs in. One detail matters once you move from the demo to a real login: where the password ends up.
Same tools, different browser
FirecrawlBrowser extends our AgentBrowser, so the model drives the page the same way with either one. Both give the agent the same 16 browser tools by default. The only difference is where Chromium runs. AgentBrowser launches it on your machine with Playwright. FirecrawlBrowser gets a session from Firecrawl and connects to it over CDP.
Each browser session belongs to a thread. Inside @mastra/agent-browser, every page tool pulls threadId from the agent's execution context before running:
import { createTool } from "@mastra/core/tools";
import type { AgentBrowser } from "../agent-browser";
import { typeInputSchema } from "../schemas";
import { BROWSER_TOOLS } from "./constants";
export function createTypeTool(browser: AgentBrowser) {
return createTool({
id: BROWSER_TOOLS.TYPE,
description:
"Type text into an input element. Use clear: true to replace existing content.",
inputSchema: typeInputSchema,
execute: async (input, { agent }) => {
const threadId = agent?.threadId;
browser.setCurrentThread(threadId);
await browser.ensureReady();
return browser.type(input, threadId);
},
});
}agent.threadId is the same thread ID you pass to memory when calling the agent. The first browser tool call on a thread opens a Firecrawl session; subsequent calls on that thread reuse it. When a user follows up with "now check out" on turn two, Memory loads the prior conversation and FirecrawlBrowser reconnects to the session that still holds the logged-in cookies and cart. Two users on different threads get isolated histories and separate browser sessions.
The map from thread to session lives in your server process, though. If the server restarts or a request lands on another instance, the thread gets a fresh browser with no cookies. By default, Firecrawl also ends a session after five minutes idle or ten minutes total.
The password ends up in your traces
When the agent logs in, the model calls browser_type with something like this:
{ "ref": "@e2", "text": "secret_sauce" }That's a tool call. Mastra treats it like any other. Memory stores it with the thread's messages, and tracing records it as a span. ToolCallFilter can keep old tool calls out of the prompt, but the stored copy stays.
Our sensitive data filter runs on traces by default, but it matches on field names like password and token. The browser tool's field is called text. So the password lands in your traces as plain text.
That's harmless with Sauce Labs' public test login. With a real account like your ops team's vendor portal, you want the password out of the agent entirely. Firecrawl's named profiles handle this. A person or a setup script opens a session on a profile with saveChanges: true and logs in once. Closing that session saves the login. Your agent opens the same profile with saveChanges: false and starts out signed in. The model never sees the password, so it never reaches your memory or your traces. And since the login lives in the profile, it survives server restarts and expired sessions too.
On the agent, set saveChanges: false explicitly. Firecrawl defaults it to true, and only one session can write to a profile at a time. With the default on, a second thread that opens the profile at the same time gets a 409.
Get started
npm install @mastra/browser-firecrawlSet FIRECRAWL_API_KEY, pass a FirecrawlBrowser to your agent's browser field, and follow Firecrawl's walkthrough for the full demo. The FirecrawlBrowser reference has every option.
Thanks to the Firecrawl team for building this.
