Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions packages/docs/docs.json
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@
"v4/integrations/claude-code",
"v4/integrations/codex",
"v4/integrations/eve",
"v4/integrations/flue",
"v4/integrations/deep-agents",
"v4/integrations/crewai",
"v4/integrations/mastra",
Expand Down
1 change: 1 addition & 0 deletions packages/docs/images/integrations/flue.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
2 changes: 1 addition & 1 deletion packages/docs/v4/best-practices/mcp-integrations.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ description: "Call Model Context Protocol (MCP) servers alongside Stagehand's pr
Stagehand v4 does not include an autonomous agent or a general-purpose MCP client. To call third-party MCP tools alongside Stagehand, orchestrate both from your own code as shown below.
</Note>

Stagehand also provides experimental [integrations](/v4/integrations/overview) that expose a persistent Stagehand browser to CrewAI, Deep Agents, Mastra, and the Vercel AI SDK over MCP. The Eve integration exposes the same tools natively.
Stagehand also provides experimental [integrations](/v4/integrations/overview) that expose a persistent Stagehand browser to CrewAI, Deep Agents, Mastra, and the Vercel AI SDK over MCP. The Eve, Flue, and Pi integrations expose the same tools natively.

<Card title="Stagehand integrations" icon="puzzle-piece" href="/v4/integrations/overview">
Give an agent the `run`, `snapshot`, and `screenshot` browser tools.
Expand Down
84 changes: 84 additions & 0 deletions packages/docs/v4/integrations/flue.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
---
title: "Flue"
description: "Give a Flue agent native Stagehand browser tools backed by one persistent browser session."
---

The Stagehand integration binds `run`, `snapshot`, and `screenshot` as native in-process [Flue](https://flueframework.com/) tools backed by one browser session, without starting an MCP server or bridge process.

<Note>
Stagehand ships this experimental integration from the repository rather than publishing it as a standalone adapter.
</Note>

## Prerequisites

- Node.js 24 or newer
- pnpm 11.10.0
- A model-provider credential for Flue
- A current Google Chrome installation for local browser mode

## Quickstart

<Steps>
<Step title="Clone and build Stagehand">
```bash
git clone https://github.com/browserbase/stagehand.git
cd stagehand
pnpm install --frozen-lockfile
pnpm exec turbo run build \
--filter @browserbasehq/stagehand-integrations
```
</Step>
<Step title="Configure the Flue model">
The example uses an OpenAI model by default:

```bash
export OPENAI_API_KEY="your-openai-api-key"
```

Set `FLUE_STAGEHAND_MODEL` to select another Flue model.
</Step>
<Step title="Choose the browser">
The example defaults to Browserbase when `BROWSERBASE_API_KEY` is set, otherwise it uses local Chrome:
Comment thread
antonvishal marked this conversation as resolved.

```bash
export STAGEHAND_BROWSER="browserbase"
export BROWSERBASE_API_KEY="your-browserbase-api-key"
```
</Step>
<Step title="Run a browser task">
```bash
pnpm --filter @browserbasehq/stagehand-integrations-example-flue-facade start \
"Open https://example.com and report the page title."
```

Flue uses the `run` tool and should report `Example Domain` before the process exits.
</Step>
</Steps>

## Configuration

| Variable | Purpose |
| --- | --- |
| `FLUE_STAGEHAND_MODEL` | Flue agent model. Defaults to `openai/gpt-5.6-luna`. |
| `OPENAI_API_KEY` | Credential for the example's default Flue model. |
| `STAGEHAND_BROWSER` | Select `local` or `browserbase`. Defaults to `browserbase` when `BROWSERBASE_API_KEY` is set, otherwise `local`. |
| `BROWSERBASE_API_KEY` | Required for Browserbase. |
| `BROWSERBASE_PROJECT_ID` | Optional Browserbase project ID. |
| `STAGEHAND_MODEL_NAME` | Optional. The facade tools are deterministic and do not call a Stagehand model today. |
Comment thread
antonvishal marked this conversation as resolved.
| `STAGEHAND_MODEL_API_KEY` | Optional credential for `STAGEHAND_MODEL_NAME`; the native integration can infer a supported provider key. |

## Screenshot behavior

Flue's current tool loop is text-only. This integration saves each screenshot to a temporary file and returns the path to the agent. Open the returned path to inspect the image.

## Session lifecycle

One Flue CLI run owns one lazily-created browser session. All three tools reuse it, so navigation, authentication, and snapshot IDs survive between calls. The browser closes when the run finishes or receives an interrupt.

<Warning>
`run` executes model-authored JavaScript in the browser. Use Browserbase for untrusted tasks and review the [integration security boundary](/v4/integrations/overview#security-boundary).
</Warning>

<Card title="Flue integration source" icon="github" href="https://github.com/browserbase/stagehand/tree/main/packages/integrations/flue">
Read the native tool bindings and browser lifecycle implementation.
</Card>
13 changes: 8 additions & 5 deletions packages/docs/v4/integrations/overview.mdx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
---
title: "Integrations"
sidebarTitle: "Overview"
description: "Connect Claude Code, Codex, CrewAI, Deep Agents, Eve, Mastra, fx, Pi, or the Vercel AI SDK to a persistent Stagehand browser."
description: "Connect Claude Code, Codex, CrewAI, Deep Agents, Eve, Flue, Mastra, fx, Pi, or the Vercel AI SDK to a persistent Stagehand browser."
---

Each integration gives your agent one persistent browser and three tools: `run`, `snapshot`, and `screenshot`. Your agent decides how to navigate and interact while Stagehand manages the browser session.
Expand All @@ -22,6 +22,9 @@ Stagehand ships these experimental integrations from the monorepo and does not p
<Card title="Eve by Vercel" icon="/images/integrations/eve.svg" href="/v4/integrations/eve">
Give Vercel's framework for building durable agents native Stagehand tools.
</Card>
<Card title="Flue" icon="/images/integrations/flue.svg" href="/v4/integrations/flue">
Give a Flue agent native Stagehand tools backed by one persistent browser.
Comment thread
antonvishal marked this conversation as resolved.
</Card>
<Card title="Deep Agents" icon="/images/integrations/deep-agents.svg" href="/v4/integrations/deep-agents">
Run locally over MCP or deploy native tools with Managed Deep Agents.
</Card>
Expand Down Expand Up @@ -70,15 +73,15 @@ Every integration exposes the same browser capabilities.
```
</Accordion>
<Accordion title="screenshot">
Capture the active page as a PNG or JPEG for visual inspection. Frameworks that support multimodal tool results receive the image directly; the CrewAI adapter writes it to a temporary file because its current tool loop is text-only.
Capture the active page as a PNG or JPEG for visual inspection. Frameworks that support multimodal tool results receive the image directly; the CrewAI and Flue adapters write it to a temporary file because their current tool loops are text-only.
</Accordion>
</AccordionGroup>

## How sessions work

The tools share a browser for the lifetime of the integration's client session. A navigation performed by `run` is visible to the next `snapshot`, and authentication and page state remain available across calls.

The Claude Code, Codex, CrewAI, Mastra, fx, Vercel AI SDK, and local Deep Agents examples keep one MCP client session open so the stdio server and browser stay alive. Eve, Pi, and Managed Deep Agents bind equivalent tools in-process.
The Claude Code, Codex, CrewAI, Mastra, fx, Vercel AI SDK, and local Deep Agents examples keep one MCP client session open so the stdio server and browser stay alive. Eve, Flue, Pi, and Managed Deep Agents bind equivalent tools in-process.

The private [`core/` workspace package](https://github.com/browserbase/stagehand/tree/main/packages/integrations/core) owns the shared TypeScript tool contract, browser runtime, native bindings, and stdio MCP server. The TypeScript integrations and CrewAI use this package.

Expand All @@ -88,7 +91,7 @@ Do not create a new MCP process for every tool call. Doing so starts a new brows

## Run the integrations from source

Claude Code, Codex, CrewAI, Mastra, fx, and the Vercel AI SDK use the shared TypeScript Stagehand facade MCP server. Eve and Pi use native in-process bindings from the same package. These integrations require Node.js 24 or newer and [pnpm](https://pnpm.io/installation) 11.10.0. CrewAI also requires Python 3.11–3.13 and [uv](https://docs.astral.sh/uv/).
Claude Code, Codex, CrewAI, Mastra, fx, and the Vercel AI SDK use the shared TypeScript Stagehand facade MCP server. Eve, Flue, and Pi use native in-process bindings from the same package. These integrations require Node.js 24 or newer and [pnpm](https://pnpm.io/installation) 11.10.0. CrewAI also requires Python 3.11–3.13 and [uv](https://docs.astral.sh/uv/).

<Steps>
<Step title="Clone and install Stagehand">
Expand Down Expand Up @@ -126,7 +129,7 @@ export BROWSERBASE_API_KEY="your-browserbase-api-key"
| `BROWSERBASE_API_KEY` | Required for Browserbase. |
| `BROWSERBASE_PROJECT_ID` | Optional Browserbase project ID for the TypeScript integrations. |
| `STAGEHAND_MODEL_NAME` | Optional model for Stagehand AI methods called from TypeScript integration tools. |
| `STAGEHAND_MODEL_API_KEY` | Credential for `STAGEHAND_MODEL_NAME`. Eve and Pi can infer a supported provider key; the MCP examples require this variable. |
| `STAGEHAND_MODEL_API_KEY` | Credential for `STAGEHAND_MODEL_NAME`. Eve, Flue, and Pi can infer a supported provider key; the MCP examples require this variable. |

The framework's agent model and Stagehand's optional browser model are separate. The agent model decides which tool to call. Configure a Stagehand model only when JavaScript passed to `run` calls AI methods such as `act`, `extract`, or `observe`.

Expand Down
1 change: 1 addition & 0 deletions packages/integrations/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ else, never restated.
| `crewai/` | Python CrewAI example over MCP/stdio (uv project). |
| `deepagents/` | Python LangChain Deep Agents integrations: a local stdio MCP server and a Managed Deep Agents project with native tools. |
| `eve/` | Eve example with the tools bound natively via `defineTool` (Eve has no external-process tool mounting). |
| `flue/` | Flue example with the tools bound natively in-process (Flue has no external-process tool mounting). |
| `fx/` | fx configuration templates and skill — fx consumes the facade via its user-global MCP config. |
| `mastra/` | Mastra example over MCP/stdio via Mastra's `MCPClient`. |
| `pi/` | Pi extension registering the tools natively (Pi ships without built-in MCP). |
Expand Down
51 changes: 51 additions & 0 deletions packages/integrations/flue/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
# Flue + Stagehand facade (native tools)

This example gives a Flue agent the native tools `run`, `snapshot`, and `screenshot`. The tools
share a Stagehand session directly; no MCP connection or bridge process is required.

## Setup

Use Node.js 24 or later. From the repository root, build the integrations package before running
the example:

```bash
pnpm exec turbo run build --filter @browserbasehq/stagehand-integrations
```

Configure the environment as needed:

| Variable | Purpose |
| ------------------------- | ------------------------------------------------------------------------------------------------ |
| `STAGEHAND_BROWSER` | Browser backend. Defaults to `browserbase` when `BROWSERBASE_API_KEY` is set, otherwise `local`. |
| `BROWSERBASE_API_KEY` | Browserbase API key. |
| `STAGEHAND_MODEL_NAME` | Optional Stagehand model name, such as `openai/gpt-5.6-luna`. |
Comment thread
antonvishal marked this conversation as resolved.
| `STAGEHAND_MODEL_API_KEY` | Optional explicit API key for `STAGEHAND_MODEL_NAME`; otherwise a supported provider key is inferred. |
| `FLUE_STAGEHAND_MODEL` | Flue agent model; defaults to `openai/gpt-5.6-luna`. |
| `OPENAI_API_KEY` | Used by the Flue agent model in the host process. |

## Run

```bash
pnpm --filter @browserbasehq/stagehand-integrations-example-flue-facade test
pnpm --filter @browserbasehq/stagehand-integrations-example-flue-facade typecheck
pnpm --filter @browserbasehq/stagehand-integrations-example-flue-facade start \
"Open https://example.com and report the page title."
```

## Screenshots

Flue's current tool loop is text-only. This example saves each screenshot to a temporary file and
returns its path and MIME type to the agent; open the file to inspect it.

## Security model

`run(code)` executes model-authored JavaScript in the extension service worker:
it runs browser-side, never in the host process. Browserbase is the
recommended isolation boundary. The Flue process holds only the browser session
handle; model-authored JavaScript does not execute inside the host process.

## Session lifecycle

One CLI run owns one lazily-created browser session. All three tools reuse that browser, so page
state and snapshot IDs survive between tool calls. The browser closes when the Flue run finishes
or is interrupted.
21 changes: 21 additions & 0 deletions packages/integrations/flue/agent/agent.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
"use agent";

import { useModel, useTool } from "@flue/runtime";

import { FACADE_AGENT_INSTRUCTIONS } from "@browserbasehq/stagehand-integrations/facade";

import runTool from "./tools/run.js";
import screenshotTool from "./tools/screenshot.js";
import snapshotTool from "./tools/snapshot.js";

export const FLUE_STAGEHAND_INSTRUCTIONS = `${FACADE_AGENT_INSTRUCTIONS}

The screenshot tool returns a temporary local file path and MIME type. Report that path so the user can inspect the image.`;

export function StagehandAgent() {
useModel(process.env.FLUE_STAGEHAND_MODEL ?? "openai/gpt-5.6-luna");
useTool(runTool);
useTool(snapshotTool);
useTool(screenshotTool);
return FLUE_STAGEHAND_INSTRUCTIONS;
}
43 changes: 43 additions & 0 deletions packages/integrations/flue/agent/tools/run.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
import { defineTool } from "@flue/runtime";
import * as v from "valibot";

import {
CodeModeRunInputSchema,
RUN_TOOL_DESCRIPTION,
} from "@browserbasehq/stagehand-integrations/facade";

import { discardFacadeToolsIfUnhealthy, getFacadeTools } from "../../src/session.js";

const runInput = v.strictObject({
code: v.optional(v.pipe(v.string(), v.minLength(1))),
actions: v.optional(v.pipe(v.array(v.record(v.string(), v.unknown())), v.minLength(1))),
});

export default defineTool({
name: "run",
description: RUN_TOOL_DESCRIPTION,
input: runInput,
async run({ data }) {
const input = CodeModeRunInputSchema.parse(data);
const tools = await getFacadeTools();
try {
const result =
input.code !== undefined
? await tools.run(input.code)
: await tools.runActions(input.actions!);
return stringifyResult(result);
} catch (error) {
await discardFacadeToolsIfUnhealthy(tools);
throw error;
Comment thread
antonvishal marked this conversation as resolved.
}
},
});

function stringifyResult(value: unknown): string {
Comment thread
antonvishal marked this conversation as resolved.
if (typeof value === "string") return value;
try {
return JSON.stringify(value, null, 2) ?? String(value);
} catch {
return String(value);
}
}
31 changes: 31 additions & 0 deletions packages/integrations/flue/agent/tools/screenshot.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import { defineTool } from "@flue/runtime";
import * as v from "valibot";

import {
SCREENSHOT_TOOL_DESCRIPTION,
ScreenshotInputSchema,
} from "@browserbasehq/stagehand-integrations/facade";

import { writeScreenshotArtifact } from "../../src/artifacts.js";
import { discardFacadeToolsIfUnhealthy, getFacadeTools } from "../../src/session.js";

export default defineTool({
name: "screenshot",
description: SCREENSHOT_TOOL_DESCRIPTION,
input: v.strictObject({
fullPage: v.optional(v.boolean()),
type: v.optional(v.picklist(["png", "jpeg"])),
quality: v.optional(v.pipe(v.number(), v.minValue(0), v.maxValue(100))),
Comment thread
antonvishal marked this conversation as resolved.
}),
async run({ data }) {
const input = ScreenshotInputSchema.parse(data);
const tools = await getFacadeTools();
try {
const image = await tools.screenshot(input);
return { output: await writeScreenshotArtifact(image) };
} catch (error) {
await discardFacadeToolsIfUnhealthy(tools);
throw error;
}
},
});
27 changes: 27 additions & 0 deletions packages/integrations/flue/agent/tools/snapshot.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import { defineTool } from "@flue/runtime";
import * as v from "valibot";

import {
SNAPSHOT_TOOL_DESCRIPTION,
SnapshotInputSchema,
} from "@browserbasehq/stagehand-integrations/facade";

import { discardFacadeToolsIfUnhealthy, getFacadeTools } from "../../src/session.js";

export default defineTool({
name: "snapshot",
description: SNAPSHOT_TOOL_DESCRIPTION,
input: v.strictObject({
includeIframes: v.optional(v.boolean()),
}),
async run({ data }) {
const input = SnapshotInputSchema.parse(data);
const tools = await getFacadeTools();
try {
return await tools.snapshot(input);
} catch (error) {
await discardFacadeToolsIfUnhealthy(tools);
throw error;
}
},
});
29 changes: 29 additions & 0 deletions packages/integrations/flue/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
{
"name": "@browserbasehq/stagehand-integrations-example-flue-facade",
"version": "4.0.1",
"private": true,
"type": "module",
"scripts": {
"start": "tsx src/run-agent.ts",
"test": "vitest run",
"test:unit": "vitest run",
"typecheck": "tsc --noEmit"
},
"dependencies": {
"@browserbasehq/stagehand": "workspace:*",
"@browserbasehq/stagehand-integrations": "workspace:*",
"@flue/runtime": "catalog:",
"dotenv": "catalog:",
"valibot": "catalog:"
},
"devDependencies": {
"@earendil-works/pi-ai": "catalog:",
"@types/node": "catalog:",
"tsx": "catalog:",
"typescript": "catalog:",
"vitest": "catalog:"
},
"engines": {
"node": ">=24"
}
}
21 changes: 21 additions & 0 deletions packages/integrations/flue/src/artifacts.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import { mkdtemp, writeFile } from "node:fs/promises";
import os from "node:os";
import path from "node:path";

let artifactDirectory: string | undefined;
let screenshotNumber = 0;

export async function writeScreenshotArtifact(image: {
data: string;
mimeType: string;
}): Promise<{ path: string; mimeType: string }> {
artifactDirectory ??= await mkdtemp(path.join(os.tmpdir(), "stagehand-flue-"));
screenshotNumber += 1;
const extension = image.mimeType === "image/jpeg" ? "jpg" : "png";
const screenshotPath = path.join(
artifactDirectory,
`screenshot-${screenshotNumber}.${extension}`,
);
await writeFile(screenshotPath, Buffer.from(image.data, "base64"));
return { path: screenshotPath, mimeType: image.mimeType };
}
Loading
Loading