Skip to content
Closed
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
19 changes: 19 additions & 0 deletions .github/workflows/stagehand-codemode.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,15 @@ on:
pull_request:
paths:
- '.github/workflows/stagehand-codemode.yml'
- 'examples/integrations/mastra/stagehand-codemode/**'
- 'examples/integrations/vercel/stagehand-codemode/**'
- 'packages/stagehand-codemode/**'
- 'pnpm-workspace.yaml'
push:
branches: [main]
paths:
- '.github/workflows/stagehand-codemode.yml'
- 'examples/integrations/mastra/stagehand-codemode/**'
- 'examples/integrations/vercel/stagehand-codemode/**'
- 'packages/stagehand-codemode/**'
- 'pnpm-workspace.yaml'
Expand Down Expand Up @@ -51,3 +53,20 @@ jobs:
- run: pnpm --filter @browserbasehq/stagehand-codemode run build
- run: pnpm --filter stagehand-codemode-ai-sdk-example run check
- run: pnpm --filter stagehand-codemode-ai-sdk-example run smoke

mastra:
runs-on: ubuntu-latest
timeout-minutes: 20
steps:
- uses: actions/checkout@v4
- uses: pnpm/action-setup@v4
with:
version: 10.9.0
- uses: actions/setup-node@v4
with:
node-version: 24.x

- run: pnpm install --no-frozen-lockfile
- run: pnpm --filter @browserbasehq/stagehand-codemode run build
- run: pnpm --filter stagehand-codemode-mastra-example run check
- run: pnpm --filter stagehand-codemode-mastra-example run smoke
11 changes: 11 additions & 0 deletions examples/integrations/mastra/stagehand-codemode/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
# Mastra + Stagehand code mode

Mastra launches the local stdio MCP through one long-lived `MCPClient`. The same client must remain
connected for the complete agent run so all `code_execute` calls reach the same browser-owning
process. `disconnect()` closes the process and browser afterward.

The shared Stagehand V4 syntax skill is supplied as the agent's `instructions` and is also present
in the discovered tool description.

See [`src/agent.ts`](./src/agent.ts). `pnpm smoke` performs real stdio MCP discovery without creating
a browser.
19 changes: 19 additions & 0 deletions examples/integrations/mastra/stagehand-codemode/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
{
"name": "stagehand-codemode-mastra-example",
"private": true,
"type": "module",
"scripts": {
"check": "tsc --noEmit",
"smoke": "tsx src/smoke.ts"
},
"dependencies": {
"@browserbasehq/stagehand-codemode": "workspace:*",
"@mastra/core": "^1.55.0",
"@mastra/mcp": "^1.15.0"
},
"devDependencies": {
"@types/node": "^25.0.9",
"tsx": "^4.20.6",
"typescript": "^5.9.3"
}
}
57 changes: 57 additions & 0 deletions examples/integrations/mastra/stagehand-codemode/src/agent.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
import { fileURLToPath } from 'node:url';
import { Agent } from '@mastra/core/agent';
import { MCPClient } from '@mastra/mcp';
import { STAGEHAND_CODEMODE_SKILL } from '@browserbasehq/stagehand-codemode';

const DEFAULT_STDIO_SERVER_PATH = fileURLToPath(
new URL(
'../../../../../packages/stagehand-codemode/dist/stdio-server.js',
import.meta.url
)
);

export function createStagehandMcpClient(
stdioServerPath = DEFAULT_STDIO_SERVER_PATH
): MCPClient {
return new MCPClient({
id: 'stagehand-codemode',
servers: {
stagehand: {
command: process.execPath,
args: [stdioServerPath],
env: definedEnvironment(),
},
},
});
}

export async function runStagehandAgent(prompt: string): Promise<string> {
const mcp = createStagehandMcpClient();
try {
const { tools, errors } = await mcp.listToolsWithErrors();
if (Object.keys(errors).length > 0) {
throw new Error(
`Stagehand MCP discovery failed: ${JSON.stringify(errors)}`
);
}
const agent = new Agent({
id: 'stagehand-browser-agent',
name: 'Stagehand browser agent',
instructions: STAGEHAND_CODEMODE_SKILL,
model: process.env.MASTRA_MODEL ?? 'openai/gpt-5-mini',
tools,
});
const result = await agent.generate(prompt, { maxSteps: 8 });
return result.text;
} finally {
await mcp.disconnect();
}
}

function definedEnvironment(): Record<string, string> {
return Object.fromEntries(
Object.entries(process.env).filter(
(entry): entry is [string, string] => entry[1] !== undefined
)
);
}
26 changes: 26 additions & 0 deletions examples/integrations/mastra/stagehand-codemode/src/smoke.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import { STAGEHAND_CODEMODE_SKILL } from '@browserbasehq/stagehand-codemode';
import { createStagehandMcpClient } from './agent.js';

const mcp = createStagehandMcpClient();
try {
const { tools, errors } = await mcp.listToolsWithErrors();
if (Object.keys(errors).length > 0) throw new Error(JSON.stringify(errors));
const names = Object.keys(tools);
if (names.length !== 1 || !names[0]?.endsWith('code_execute')) {
throw new Error(`Expected one code_execute tool, got ${names.join(', ')}`);
}
const description = Object.values(tools)[0]?.description ?? '';
if (!description.includes('Stagehand V4 code-mode syntax')) {
throw new Error(
'Discovered tool description did not include the Stagehand syntax skill.'
);
}
if (!STAGEHAND_CODEMODE_SKILL.includes('stagehand.extract')) {
throw new Error(
'Agent instructions did not load the Stagehand syntax skill.'
);
}
process.stdout.write(`Mastra stdio discovery PASS: ${names[0]}\n`);
} finally {
await mcp.disconnect();
}
11 changes: 11 additions & 0 deletions examples/integrations/mastra/stagehand-codemode/tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
{
"extends": "../../../../tsconfig.json",
"compilerOptions": {
"module": "NodeNext",
"moduleResolution": "NodeNext",
"target": "ES2022",
"types": ["node"],
"noEmit": true
},
"include": ["src/**/*.ts"]
}
1 change: 1 addition & 0 deletions pnpm-workspace.yaml
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
packages:
- 'examples/*'
- 'examples/integrations/mastra/stagehand-codemode'
- 'examples/integrations/vercel/stagehand-codemode'
- 'packages/*'
Loading