feat: add Stagehand code execution tool - #2619
Conversation
|
There was a problem hiding this comment.
All reported issues were addressed across 18 files
Architecture diagram
sequenceDiagram
participant Host as Agent Framework / Host
participant Exec as StagehandCodeExecutor
participant Queue as Serial Queue
participant Snip as executeStagehandSnippet
participant Stagehand as Stagehand Instance
participant Browser as Browser (local/Browserbase)
participant Page as Page
participant MCP as MCP Server / Tool
participant Config as stagehandCodeConfigFromEnv
Note over Host,Config: Startup: Configuration and Browser Selection
Host->>Config: Read environment variables
Config->>Config: Determine browser type (local/browserbase)
alt STAGEHAND_BROWSER=local
Config->>Config: Set local headless browser
else STAGEHAND_BROWSER=browserbase
Config->>Config: Validate BROWSERBASE_API_KEY present
Config->>Config: Forward project ID if set
else No explicit setting
alt BROWSERBASE_API_KEY exists
Config->>Config: Select Browserbase
else
Config->>Config: Select local headless
end
end
Config->>Config: Resolve model name and API key
alt Explicit STAGEHAND_MODEL_NAME
Config->>Config: Use provider-specific API key
alt Provider is Anthropic
Config->>Config: Add dangerous-direct-browser-access header
end
else No model name, Google key present
Config->>Config: Default to google/gemini-2.5-flash-lite
end
Config-->>Host: Return StagehandCodeConfig
Note over Host,MCP: Execution Flow
Host->>Exec: new StagehandCodeExecutor(config)
Host->>Exec: execute({ code }, signal?)
Note over Exec,Queue: Serialization and Validation
Exec->>Exec: validate input (size, non-empty)
alt Invalid input
Exec-->>Host: Return failure with kind="validation"
else Valid input
Exec->>Queue: Chain onto FIFO queue
Queue-->>Exec: Wait for previous operations
end
Note over Exec,Stagehand: Lazy Browser Initialization
Exec->>Exec: ensureStagehand()
alt Stagehand not yet created
Exec->>Browser: Launch (local or browserbase)
alt Launch successful
Exec->>Stagehand: Stagehand.create(browser, config)
alt Stagehand create fails
Exec->>Browser: Close browser
Exec-->>Host: Return failure with kind="runtime"
end
else Launch fails
Exec-->>Host: Return failure with kind="runtime"
end
end
Exec-->>Exec: Stagehand instance ready
Note over Exec,Page: Snippet Execution
Exec->>Page: Get active page (or first/new)
alt Signal aborted before execution
Exec-->>Host: Return failure with kind="aborted"
else Signal not aborted
Exec->>Snip: executeStagehandSnippet({ code, page, context, stagehand, console })
Snip->>Snip: Create AsyncFunction with bindings
Note over Snip: Injects page, context, stagehand, z (Zod), console
Snip->>Page: Execute snippet code
alt Snippet succeeds
Page-->>Snip: Return value
Snip-->>Exec: Return value
Exec->>Page: Read page state (URL, title)
Exec-->>Host: Return success with page state and value
else Snippet throws
Page-->>Snip: Throw error
Snip-->>Exec: Throw error
Exec->>Exec: normalizeError (redact secrets)
Exec-->>Host: Return failure with kind="runtime"
end
end
Note over Exec: Cleanup and Shutdown
Host->>Exec: close()
Exec->>Exec: Drain queued operations
Exec->>Stagehand: stagehand.close()
Exec->>Browser: browser.close()
alt Both close fail
Exec-->>Host: AggregateError
end
Exec-->>Host: Cleanup complete
Note over MCP,MCP: MCP Tool Registration
Host->>MCP: createCodeModeMcpServer(executor)
MCP->>MCP: registerTool("code_execute", schema, handler)
MCP-->>Host: McpServer ready
Host->>MCP: connect to stdio transport
Note over MCP,Host: Tool calls flow through MCP protocol
Host->>MCP: callTool("code_execute", { code })
MCP->>Exec: executor.execute(input, signal)
Exec-->>MCP: result
MCP->>MCP: Format as text + structured content
alt result.ok
MCP-->>Host: isError=false, content=JSON
else
MCP-->>Host: isError=true, content=JSON
end
Reply with feedback, questions, or to request a fix.
Re-trigger cubic
There was a problem hiding this comment.
All reported issues were addressed across 8 files (changes from recent commits).
Reply with feedback, questions, or to request a fix.
Re-trigger cubic
## Why The code-mode product work spans three distinct review domains: process hosting, browser code execution, and agent guidance. This bottom PR isolates the package and Model Context Protocol (MCP) host so its build, transport, and shutdown behavior can be reviewed without the execution engine or prompt content. ## Stack 1. **This PR:** private package, MCP stdio host, lifecycle, repository build/test wiring 2. #2619: Stagehand executor, local and Browserbase configuration, schemas, queueing, and `code_execute` registration 3. #2620: `SKILL.md`, `REFERENCE.md`, generated exports, package assets, and guidance loading checks 4. [#2626](#2626) — Vercel AI SDK MCP example and smoke flows 5. [#2627](#2627) — Mastra MCP example and smoke flows 6. [#2628](#2628) — CrewAI MCP example and smoke flows 7. [#2629](#2629) — LangChain Deep Agents MCP example and smoke flows Each PR is intended to build, test, and make a truthful claim independently. ## What changed - adds the private `@browserbasehq/stagehand-integrations` workspace package - adds a compiled stdio entrypoint backed by the MCP SDK - negotiates MCP server metadata without advertising capabilities that do not exist yet - bounds concurrent shutdown cleanup to five seconds - preserves conventional process exit codes for `SIGINT` and `SIGTERM` - wires the package into workspace, Turbo, Vitest, and CI discovery ## Intentionally not included - no MCP tools - no browser or model configuration - no Stagehand executor - no skill or reference content - no published package surface; the package remains private ## E2E Test Matrix | Command / flow | Observed output | Confidence / sufficiency | | --- | --- | --- | | `pnpm --filter @browserbasehq/stagehand-integrations typecheck && pnpm --filter @browserbasehq/stagehand-integrations build && pnpm --filter @browserbasehq/stagehand-integrations test:unit` | Package typecheck and build passed; 3 test files and 9 tests passed. | Covers host construction, bounded cleanup, compiled stdio startup, end-of-file shutdown, and signal exit codes. It intentionally does not prove a tool or browser session. | | Manual MCP client connected to the compiled stdio entrypoint | `{"initialized":true,"toolsCapability":null,"readyMessage":true}` | Proves the built artifact starts as a child process, negotiates MCP, emits its readiness message, and truthfully advertises no tools. | | `pnpm exec turbo run test:unit --filter=@browserbasehq/stagehand-integrations` | 2/2 Turbo tasks passed; package build plus 9/9 tests passed. | Proves the repository task graph builds the package before compiled-child tests. | | `pnpm check` | 9/9 repository tasks passed. | Supports repository-wide formatting, lint, and type compatibility for this layer. | ## Changeset None. This introduces a private workspace package and does not publish a release. --------- Co-authored-by: miguel <miguelg71921@gmail.com>
a70af28 to
eac5f35
Compare
Why
This second stack layer adds the independently usable code-execution product core on top of the package and MCP host from #2597. Keeping execution separate from generated agent guidance lets reviewers focus on browser ownership, configuration, schemas, queueing, redaction, and lifecycle behavior.
Stack
code_execute, Stagehand executor, local/Browserbase configuration, schemas, and runtime testsSKILL.md,REFERENCE.md, generated exports, package assets, and guidance loading checksWhat changed
StagehandCodeExecutorwith lazy browser startup and one long-lived session per executorcode_executepage,context,stagehand, Zod, and a bounded console into async JavaScript snippetsLocal and remote startup
The stdio process reads startup configuration from its environment:
STAGEHAND_BROWSER=localSTAGEHAND_BROWSER=browserbaseBROWSERBASE_API_KEY.BROWSERBASE_PROJECT_IDis forwarded when present. Explicit model names select only their matching provider key, and Google-key precedence matches the eval-native configuration.Timeout and cancellation boundary
An abort signal can cancel queued work before its snippet begins. Arbitrary JavaScript already executing in-process cannot be safely preempted. If code blocks the Node event loop, the owning framework must terminate the entire child process tree, escalate to
SIGKILLafter its deadline, and create a replacement process. Killing only the Node process can leave a local browser descendant alive.Intentionally not included
SKILL.mdorREFERENCE.mdE2E Test Matrix
pnpm --filter @browserbasehq/stagehand-integrations typecheck && pnpm --filter @browserbasehq/stagehand-integrations testcode_executewas discovered and state persisted across both calls.code_executewas discovered and both remote pages persisted.pnpm exec turbo run fmt:check lint typecheck --concurrency=1Changeset
None. This changes a private workspace package and does not publish a release.