From edb126f139577788bb313d9c5a3dcfe285e88b21 Mon Sep 17 00:00:00 2001 From: Shrey Pandya Date: Tue, 4 Aug 2026 14:31:49 -0700 Subject: [PATCH 1/2] feat(mastra): add Stagehand code-mode integration --- .github/workflows/stagehand-codemode.yml | 19 +++++++ .../mastra/stagehand-codemode/README.md | 11 ++++ .../mastra/stagehand-codemode/package.json | 19 +++++++ .../mastra/stagehand-codemode/src/agent.ts | 57 +++++++++++++++++++ .../mastra/stagehand-codemode/src/smoke.ts | 26 +++++++++ .../mastra/stagehand-codemode/tsconfig.json | 11 ++++ pnpm-workspace.yaml | 1 + 7 files changed, 144 insertions(+) create mode 100644 examples/integrations/mastra/stagehand-codemode/README.md create mode 100644 examples/integrations/mastra/stagehand-codemode/package.json create mode 100644 examples/integrations/mastra/stagehand-codemode/src/agent.ts create mode 100644 examples/integrations/mastra/stagehand-codemode/src/smoke.ts create mode 100644 examples/integrations/mastra/stagehand-codemode/tsconfig.json diff --git a/.github/workflows/stagehand-codemode.yml b/.github/workflows/stagehand-codemode.yml index 550144e..68c9ce7 100644 --- a/.github/workflows/stagehand-codemode.yml +++ b/.github/workflows/stagehand-codemode.yml @@ -4,6 +4,7 @@ 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' @@ -11,6 +12,7 @@ on: branches: [main] paths: - '.github/workflows/stagehand-codemode.yml' + - 'examples/integrations/mastra/stagehand-codemode/**' - 'examples/integrations/vercel/stagehand-codemode/**' - 'packages/stagehand-codemode/**' - 'pnpm-workspace.yaml' @@ -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 diff --git a/examples/integrations/mastra/stagehand-codemode/README.md b/examples/integrations/mastra/stagehand-codemode/README.md new file mode 100644 index 0000000..85fc5ae --- /dev/null +++ b/examples/integrations/mastra/stagehand-codemode/README.md @@ -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. diff --git a/examples/integrations/mastra/stagehand-codemode/package.json b/examples/integrations/mastra/stagehand-codemode/package.json new file mode 100644 index 0000000..e4ff6c2 --- /dev/null +++ b/examples/integrations/mastra/stagehand-codemode/package.json @@ -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" + } +} diff --git a/examples/integrations/mastra/stagehand-codemode/src/agent.ts b/examples/integrations/mastra/stagehand-codemode/src/agent.ts new file mode 100644 index 0000000..f9c53a8 --- /dev/null +++ b/examples/integrations/mastra/stagehand-codemode/src/agent.ts @@ -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_CLI_PATH = fileURLToPath( + new URL( + '../../../../../packages/stagehand-codemode/dist/cli.js', + import.meta.url + ) +); + +export function createStagehandMcpClient( + cliPath = DEFAULT_CLI_PATH +): MCPClient { + return new MCPClient({ + id: 'stagehand-codemode', + servers: { + stagehand: { + command: process.execPath, + args: [cliPath], + env: definedEnvironment(), + }, + }, + }); +} + +export async function runStagehandAgent(prompt: string): Promise { + 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 { + return Object.fromEntries( + Object.entries(process.env).filter( + (entry): entry is [string, string] => entry[1] !== undefined + ) + ); +} diff --git a/examples/integrations/mastra/stagehand-codemode/src/smoke.ts b/examples/integrations/mastra/stagehand-codemode/src/smoke.ts new file mode 100644 index 0000000..f59e112 --- /dev/null +++ b/examples/integrations/mastra/stagehand-codemode/src/smoke.ts @@ -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(); +} diff --git a/examples/integrations/mastra/stagehand-codemode/tsconfig.json b/examples/integrations/mastra/stagehand-codemode/tsconfig.json new file mode 100644 index 0000000..4f77ad9 --- /dev/null +++ b/examples/integrations/mastra/stagehand-codemode/tsconfig.json @@ -0,0 +1,11 @@ +{ + "extends": "../../../../tsconfig.json", + "compilerOptions": { + "module": "NodeNext", + "moduleResolution": "NodeNext", + "target": "ES2022", + "types": ["node"], + "noEmit": true + }, + "include": ["src/**/*.ts"] +} diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml index 3195168..6bf8705 100644 --- a/pnpm-workspace.yaml +++ b/pnpm-workspace.yaml @@ -1,4 +1,5 @@ packages: - 'examples/*' + - 'examples/integrations/mastra/stagehand-codemode' - 'examples/integrations/vercel/stagehand-codemode' - 'packages/*' From 0e173ae8db1d3cb4d5d0863faff71603358bc4a1 Mon Sep 17 00:00:00 2001 From: Shrey Pandya Date: Tue, 4 Aug 2026 15:54:34 -0700 Subject: [PATCH 2/2] refactor(mastra): launch internal stdio server --- .../integrations/mastra/stagehand-codemode/src/agent.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/examples/integrations/mastra/stagehand-codemode/src/agent.ts b/examples/integrations/mastra/stagehand-codemode/src/agent.ts index f9c53a8..7463233 100644 --- a/examples/integrations/mastra/stagehand-codemode/src/agent.ts +++ b/examples/integrations/mastra/stagehand-codemode/src/agent.ts @@ -3,22 +3,22 @@ import { Agent } from '@mastra/core/agent'; import { MCPClient } from '@mastra/mcp'; import { STAGEHAND_CODEMODE_SKILL } from '@browserbasehq/stagehand-codemode'; -const DEFAULT_CLI_PATH = fileURLToPath( +const DEFAULT_STDIO_SERVER_PATH = fileURLToPath( new URL( - '../../../../../packages/stagehand-codemode/dist/cli.js', + '../../../../../packages/stagehand-codemode/dist/stdio-server.js', import.meta.url ) ); export function createStagehandMcpClient( - cliPath = DEFAULT_CLI_PATH + stdioServerPath = DEFAULT_STDIO_SERVER_PATH ): MCPClient { return new MCPClient({ id: 'stagehand-codemode', servers: { stagehand: { command: process.execPath, - args: [cliPath], + args: [stdioServerPath], env: definedEnvironment(), }, },