evals: enable the MCP tool surfaces on the claude_code and codex harnesses - #2650
Merged
Merged
Conversation
|
Contributor
There was a problem hiding this comment.
All reported issues were addressed across 10 files
Architecture diagram
sequenceDiagram
participant Runner as Eval Runner
participant Tool as MCP Surface Tool (playwright_mcp / chrome_devtools_mcp)
participant Browser as CDP Browser (shared)
participant MCP as Agent MCP Server (harness-spawned)
participant CC as Claude Code Harness
participant CX as Codex Harness
participant Obs as Observation Recorder
participant Traj as Trajectory Adapter
Note over Runner,Traj: MCP agent-mount surfaces on claude_code / codex harnesses
Runner->>Tool: start(providedEndpoint)
Tool->>Browser: NEW: harness-side session attaches to CDP browser (evidence capture)
Tool-->>Runner: NEW: agentMount { via: "mcp", mcpServers, promptInstructions } + captureEvidence
alt providedEndpoint present (local or Browserbase runner-provided CDP)
Note over Tool,MCP: Mount requires providedEndpoint so agent's MCP server and harness session share one browser
Runner->>CC: NEW: prepare adapter - mcpServers passthrough, mcp__<server> allowlist, onToolResult
Runner->>CX: NEW: prepare adapter - codexConfig.mcp_servers, recordObservation, observedToolMatcher
else no providedEndpoint (tool_launch_local)
Note over Runner,Tool: No agentMount - agent has no browser surface
end
par Claude Code path
CC->>MCP: NEW: spawn server via SDK mcpServers
MCP->>Browser: attach (same CDP endpoint as harness session)
CC->>MCP: drive mcp__playwright__* / mcp__chrome-devtools__* tools
MCP-->>CC: tool_result (carries only tool_use id)
CC->>CC: NEW: map tool_use id -> tool name from assistant messages
CC->>Obs: NEW: onToolResult(name) -> record()
and Codex path
CX->>MCP: NEW: spawn server via config mcp_servers override
MCP->>Browser: attach (same CDP endpoint as harness session)
CX->>MCP: invoke <server>.<tool> mcp_tool_call
MCP-->>CX: item.completed (mcp_tool_call)
CX->>Obs: NEW: recordObservation() (counts against tool-step budget)
end
loop per agent step (both harnesses)
Obs->>Tool: captureEvidence()
Tool->>Browser: probe url / aria tree / screenshot (best-effort)
Tool-->>Obs: ProbeEvidence
end
Obs-->>Runner: drain stepObservations (runIndex order)
Runner->>Traj: fromHarnessResult(messages/events, stepObservations, observedToolName)
Traj->>Traj: CHANGED: observedToolName matcher selects MCP steps consuming observation indexes
alt observed call count == recorded run count (strict === guard kept)
Traj->>Traj: attach probeEvidence per ordinal (no misattribution)
else mismatch
Note over Traj: Refuse attachment - evidence_insufficient path
end
Traj-->>Runner: NormalizedTrajectory (probeEvidence on MCP steps)
Reply with feedback, questions, or to request a fix.
Re-trigger cubic
This was referenced Aug 9, 2026
miguelg719
force-pushed
the
agent-harness-mcp-surfaces
branch
from
August 9, 2026 02:53
a3c980c to
c7d482f
Compare
…esses
--tool playwright_mcp and --tool chrome_devtools_mcp now run on both
external harnesses. The agent gets its own MCP server instance attached
to the runner-provided browser; the harness observes the same browser
through its own session.
- playwright_mcp / chrome_devtools_mcp export an agentMount
{ via: "mcp", mcpServers } (the launch spec, via the exported
build*LaunchSpec helpers) plus captureEvidence through the harness-
side session. The mount requires a providedEndpoint so agent and
observer can never split across two browsers.
- claude_code: the via:"mcp" branch passes mount.mcpServers to the SDK,
allows mcp__<server> tools, and records per-step observations from
the runner's tool_result stream (tool_use id -> name tracking).
- codex: MCP servers are injected via SDK config overrides
(mcp_servers); observations record on mcp_tool_call completion
events. MCP tool calls count against the tool-step budget.
- Both trajectory adapters take an observedToolName matcher so
observations attach to MCP steps by the same gap-tolerant ordinal
scheme as bridge runs; the strict count guard is kept.
- Startup defaults: MCP surfaces -> runner_provided_{local,browserbase}_cdp.
Smoke-validated on claude_code x playwright_mcp (WebVoyager, LOCAL):
agent drove mcp__playwright__* tools, per-step url/aria evidence
attached, verifier graded a 5-criterion rubric.
…he visible tab Review follow-ups on the MCP surfaces: - ObservationRecorder.settle(): fire-and-forget record() calls (the MCP tool-result streams) are now awaited before drain — the last observations of a run can no longer race the drain and go missing. drainStepObservations is async and settles first on both harnesses. - Codex captureEvidence is wrapped in the same bounded best-effort timeout as the claude adapter (EVAL_CAPTURE_EVIDENCE_TIMEOUT_MS, 15s default) on both the code and MCP branches — a stalled terminal screenshot can no longer hang a completed row before grading. - Visible-tab tracking: the harness observer session's tab selection does not follow the agent's server. Evidence capture now resolves the browser's visible tab over a direct CDP connection (document.visibilityState per page target) and re-points the observer session before probing; selecting the already-visible tab makes the underlying bringToFront a no-op, so the agent is never disturbed. Best-effort with fallback to the session's own selection.
miguelg719
force-pushed
the
agent-harness-mcp-surfaces
branch
from
August 9, 2026 02:57
c7d482f to
544b54a
Compare
akeimach
approved these changes
Aug 9, 2026
miguelg719
added a commit
that referenced
this pull request
Aug 9, 2026
The agent-facing half of the stagehand_code code-mode surface: prepareLLMExposure initializes a v4 Stagehand client via initStagehand and declares a code_handles agent mount — stagehand/page/z in the snippet scope, surface-owned prompt instructions and run-tool copy, final-state evidence capture (screenshot + URL + aria tree), and cleanup that closes the browser as well as the client (stagehand.close() alone leaves the browser running). Nothing consumes the mount here; the claude_code adapter does in #2596 and codex in #2609. Bottom of the nondeterministic-evals stack (#2591 → #2611, with the merge-readiness fixes in #2649 and the MCP tool surfaces in #2650 stacked on top).⚠️ **Base housekeeping before merge**: this PR still targets `evals-v4-root`, which has since merged (via #2570 → #2494) into `v4-spike`. Rebase onto current `v4-spike` and retarget. One known reconciliation: v4-spike removed the `EVAL_VERIFIER_MODEL` override from `verifierAdapter` (2af557b) as redundant with V3Evaluator's own constructor options; this stack's later PRs still carry it — drop it during the rebase rather than reintroducing it. <!-- This is an auto-generated description by cubic. --> --- ## Summary by cubic Adds the `stagehand_code` code-mode tool and registers it in the core tool registry, wiring a v4 `@browserbasehq/stagehand` client with scoped handles, run-tool execution, artifact capture, and full teardown. Enables the v4 code-mode surface for STG-2671 with a fixed SDK model and local/Browserbase startup profiles. - New Features - New `StagehandCodeTool` (`id: stagehand_code`) with session, navigation, evaluation, screenshot, viewport, wait, click, hover, scroll, type, press, tabs, and representation. - Targets: `selector`, `coords`, `focused`. - Agent mount via handles: `stagehand`, `page`, and `z`; scoped prompt instructions; run-tool description, code param description, and deny message. Snippets also get `startUrl`, `task`, and `console` in scope. - Captures screenshot, URL, and ARIA tree; cleanup closes both the Stagehand client and the browser. - Profiles: `tool_launch_local` and `tool_create_browserbase`; connection mode derived; model fixed to `openai/gpt-4.1-mini`. - Registered in `listCoreTools`/`getCoreTool`; tests assert `stagehand_code` is retrievable and that prompt guidance includes awaited locator actions. <sup>Written for commit e579140. Summary will update on new commits.</sup> <a href="https://cubic.dev/pr/browserbase/stagehand/pull/2591?utm_source=github" target="_blank" rel="noopener noreferrer" data-no-image-dialog="true"><picture><source media="(prefers-color-scheme: dark)" srcset="https://www.cubic.dev/buttons/review-in-cubic-dark.svg"><source media="(prefers-color-scheme: light)" srcset="https://www.cubic.dev/buttons/review-in-cubic-light.svg"><img alt="Review in cubic" src="https://www.cubic.dev/buttons/review-in-cubic-dark.svg"></picture></a> <!-- End of auto-generated description by cubic. -->
miguelg719
added a commit
that referenced
this pull request
Aug 9, 2026
## Summary Codex joins claude_code as an external harness across all three code tool surfaces (`stagehand_code`, `playwright_code`, `cdp_code`) — recreating #2397 on the LLMExposure architecture. codex-sdk has no in-process MCP mounting (unlike claude-agent-sdk), and an external MCP process could not share this process's live surface handles. So the codex mount for a `code_handles` exposure is a loopback HTTP bridge: this process executes snippets against the in-memory handles; the codex workspace gets a tiny `browser_run.mjs` client that posts a snippet file to the bridge and prints the result. Scope semantics are identical to the claude_code run tool. - `codexCodeBridge.ts` (new): the bridge server + generated workspace client - `codexToolAdapter.ts`: code-surface mounting (exposure selection, bridge lifecycle, tmpdir cwd, prompt rewrite from run-tool to `browser_run.mjs` invocation) - `codexRunner.ts` / `harnesses/codexAdapter.ts`: harness-observed terminal artifact anchors the verifier's final observation, mirroring the claude_code runner - Tests: bridge round-trip + widened surface resolution Part of the nondeterministic-evals stack; stacks on the exposure-adapter PR. Follow-up on top of the stack: #2650 adds the MCP tool surfaces (playwright_mcp / chrome_devtools_mcp) to both external harnesses; MCP tool calls there count against the codex tool-step budget introduced in #2649. <!-- This is an auto-generated description by cubic. --> --- ## Summary by cubic Enable Codex on `stagehand_code`, `playwright_code`, and `cdp_code` via a loopback HTTP bridge that runs snippets against in-memory handles, with terminal artifact capture in the runner for grounded grading. `browse_cli` remains supported with sane startup defaults, and code surfaces now have clear `browser_run.mjs` guidance. - **New Features** - Bridge server/client in `codexCodeBridge.ts`: `browser_run.mjs` posts snippet code to `/run`; executes with `handles`, `startUrl`, `task`, and `console`; timeout via `EVAL_CODEX_RUN_TOOL_TIMEOUT_MS`; non-zero exit on failure. - Tool adapter and runner: `codexToolAdapter.ts` mounts `browse_cli`, `stagehand_code`, `playwright_code`, `cdp_code`; defaults to tool-owned browsers for `browse_cli`/`stagehand_code` and runner-provided CDP for `playwright_code`/`cdp_code`; writes `browser_run.mjs` and rewrites `AGENT_RUN_TOOL_NAME`; captures evidence via `captureEvidence` and passes it to `codexAdapter.ts`; robust cleanup. - **Bug Fixes** - Final observation is only anchored when a screenshot is present; URL-only artifacts no longer qualify. - Redacts credential fragments (e.g., `signingKey`, `sk-*`) from snippet error messages before crossing the bridge or hitting logs. <sup>Written for commit a710ed6. Summary will update on new commits.</sup> <a href="https://cubic.dev/pr/browserbase/stagehand/pull/2609?utm_source=github" target="_blank" rel="noopener noreferrer" data-no-image-dialog="true"><picture><source media="(prefers-color-scheme: dark)" srcset="https://www.cubic.dev/buttons/review-in-cubic-dark.svg"><source media="(prefers-color-scheme: light)" srcset="https://www.cubic.dev/buttons/review-in-cubic-light.svg"><img alt="Review in cubic" src="https://www.cubic.dev/buttons/review-in-cubic-dark.svg"></picture></a> <!-- End of auto-generated description by cubic. --> Co-authored-by: miguel <miguelg71921@gmail.com>
miguelg719
added a commit
that referenced
this pull request
Aug 9, 2026
…ection (#2610) ## Summary STG-2752: external-harness runs now carry per-step observations and a final observation grounded in harness-captured evidence, and unverifiable-criteria counts are reported per arm and can gate the batch. - `ToolStartResult.captureEvidence` (optional hook): each code surface captures current page state (url + screenshot + aria tree) through its own handles — one shared capture per surface, resolved through `session.activePage()` at capture time - `observationRecorder.ts` (new): run-indexed probe buffer; a failed capture leaves a gap instead of shifting later observations onto the wrong step; `EVAL_HARNESS_OBSERVATIONS=none` excludes a run from evidence collection - Probes record after each run-tool execution in `claude_code` and each bridge run in `codex`; the trajectory adapters attach them to code steps only, and refuse attachment when counts mismatch (misattribution is worse than a gap) - `tui run` prints per-arm verifiability and enforces the `EVAL_MAX_UNVERIFIABLE_CRITERIA` gate Verified live: in webvoyager runs the verifier now rejects agent claims that the collected step evidence does not support (e.g. asserted ingredients absent from the extracted list). Stacked follow-ups: #2649 extends the gate to count verifier-failed (ungraded, self-reported) rows per arm and fail gated batches containing any; #2650 extends per-step observation to the MCP tool surfaces via the runner event streams. <!-- This is an auto-generated description by cubic. --> --- ## Summary by cubic Wires verifier-backed evidence capture into `claude_code` and `codex` so runs include per-step URL/screenshot probes and a harness-observed final artifact. Adds per-arm verifiability reporting and an optional gate for STG-2752. - **New Features** - Added `ObservationRecorder` for per-step probes; opt out with `EVAL_HARNESS_OBSERVATIONS=none`, timeout via `EVAL_OBSERVATION_TIMEOUT_MS`. - Probes record after each `run_tool` in `claude_code` and each bridge run in `codex`; adapters attach only to code steps (`LLM_RUN_TOOL_NAME`, `browser_run.mjs`) as `probeEvidence`. Final observation prefers the harness-captured artifact, else the last agent screenshot. - `tui run` prints per-arm verifiability and enforces the `EVAL_MAX_UNVERIFIABLE_CRITERIA` gate. - **Bug Fixes** - Bridge probe callbacks are non-blocking; failed captures leave indexed gaps instead of shifting evidence. - `codex` only attaches observations when recorded bridge runs match filtered steps; `claude_code` captures a terminal artifact before cleanup; final observation requires a screenshot. - Strict integer parsing for `EVAL_MAX_UNVERIFIABLE_CRITERIA`; the gate fails loudly when set but no arms were graded. <sup>Written for commit 88d32fd. Summary will update on new commits.</sup> <a href="https://cubic.dev/pr/browserbase/stagehand/pull/2610?utm_source=github" target="_blank" rel="noopener noreferrer" data-no-image-dialog="true"><picture><source media="(prefers-color-scheme: dark)" srcset="https://www.cubic.dev/buttons/review-in-cubic-dark.svg"><source media="(prefers-color-scheme: light)" srcset="https://www.cubic.dev/buttons/review-in-cubic-light.svg"><img alt="Review in cubic" src="https://www.cubic.dev/buttons/review-in-cubic-dark.svg"></picture></a> <!-- End of auto-generated description by cubic. --> --------- Co-authored-by: cubic-dev-ai[bot] <191113872+cubic-dev-ai[bot]@users.noreply.github.com>
miguelg719
added a commit
that referenced
this pull request
Aug 9, 2026
…2611) ## Summary Agent suites (webvoyager, onlineMind2Web, webtailbench, odysseysbench) run exclusively through the external harnesses (claude_code / codex); the v3 agent path is removed from the bench framework. Net −5,028 lines. ## Why now The agent tier's history on this lineage: | Date | Change | Event | |---|---|---| | 2026-05-01 | #2011 Evals v2 | agent bench tasks created | | 2026-07-23 | v4-spike v3 purge | deleted | | 2026-07-27 | #2401 Restore v3 evals | restored against the published stagehand-v3 npm package | #2401 restored the v3 agent tier as a stopgap because v4 had no agent story. The nondeterministic-evals stack under this PR — the LLMExposure contract, the external harnesses, per-step evidence collection — is that story, so the stopgap retires. Suites are now planner-level constructs (virtual registry entries over the dataset builders in `suites/`), not task files. ## What changes - stagehand bench harness is act/extract/observe-only on the Stagehand SDK; both `initV3` branches, the `--api` path, and the v3 context fields are gone - An explicit suite target on the stagehand harness errors with harness guidance; broad targets omit suites; dry-runs surface planner errors in the payload instead of crashing - Deleted as dead once the path is gone: the 47 v3-context agent tasks, `runWithVerifier`, `TrajectoryRecorder` (its external-harness successors — `gradeExternalTrajectory`, `observationRecorder` — live on the stack below), the agent-mode/CUA matrix machinery, and the `--agent-mode`/`--agent-modes` flags - Plus three run fixes found during live benchmarking: extract-schema steering in the stagehand_code prompt, `@browserbasehq/sdk`/`ws` resolved from the evals package (the old resolver pointed at `packages/core`, which does not exist on this lineage), and a CLI stderr filter for the stale-frame CDP log flood ## Scope boundaries - Deterministic evals untouched: zero changes to `defineTask.ts` / `types.ts` / `context.ts` / framework barrel (the #2494 / #2587 stack); a/e/o task-file v4 typing remains that stack's deliverable - ~~The legacy runner (`--legacy`) keeps its own v3 wiring and is scheduled for a separate removal~~ — done: #2649 (stacked on this PR) removes the legacy runner outright, along with GAIA - Zero changes under `packages/server` / `packages/extension` ## Stacked on top - #2649 — merge-readiness fixes: gate counts verifier-failed rows as ungraded and fails gated batches; GAIA removed entirely; legacy runner removed; codex tool-step budget; per-dataset prompt constraints restored (Mind2Web stay-on-site); agent-mode tag hygiene; stagehand_code screenshot Buffer fix - #2650 — playwright_mcp / chrome_devtools_mcp enabled on both external harnesses <!-- This is an auto-generated description by cubic. --> --- ## Summary by cubic Routes all agent benchmark suites (`webvoyager`, `onlineMind2Web`, `webtailbench`, `odysseysbench`) through external harnesses (`claude_code`, `codex`) and removes the v3 agent path. The `stagehand` harness is now act/extract/observe-only; suites are discovered as virtual entries; and the `stagehand_code` prompt now requires single-word, all-required extract schema keys. - **Bug Fixes** - Filtered Stagehand stale-frame CDP ERROR lines from CLI stderr; other errors still print. - Fixed Browserbase target prep by resolving `@browserbasehq/sdk` and `ws` from `packages/evals` dependencies. - **Migration** - Run suites with `--harness claude_code` or `--harness codex`; the `stagehand` harness no longer runs agent suites and will error on explicit suite targets (dry-run payloads include planner errors). - Remove usage of `--agent-mode`, `--agent-modes`, and the `agentModes` config key. The legacy runner (`--legacy`) keeps its own v3 wiring. <sup>Written for commit 600adf1. Summary will update on new commits.</sup> <a href="https://cubic.dev/pr/browserbase/stagehand/pull/2611?utm_source=github" target="_blank" rel="noopener noreferrer" data-no-image-dialog="true"><picture><source media="(prefers-color-scheme: dark)" srcset="https://www.cubic.dev/buttons/review-in-cubic-dark.svg"><source media="(prefers-color-scheme: light)" srcset="https://www.cubic.dev/buttons/review-in-cubic-light.svg"><img alt="Review in cubic" src="https://www.cubic.dev/buttons/review-in-cubic-dark.svg"></picture></a> <!-- End of auto-generated description by cubic. -->
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Stacked on #2649.
--tool playwright_mcpand--tool chrome_devtools_mcpnow execute on--harness claude_codeand--harness codex— the first wiring of the MCP surfaces into the agent harnesses (previously core-tier only).Design: the generic
agentMount { via: "mcp", mcpServers }contract variant, produced by the tools and delivered by the adapters — no surface-specific adapter code. The agent spawns its own MCP server instance against the runner-provided CDP browser; the harness keeps its own session on the same browser for evidence capture.buildPlaywrightMcpLaunchSpec/buildChromeDevtoolsMcpLaunchSpechelpers — named for the facade branch to rebase onto) +captureEvidence. Mount requires aprovidedEndpoint, so a tool-launched isolated browser can never split agent and observer.mcpServerspassthrough to the SDK,mcp__<server>tool allowlisting, per-step observation triggered from the runner'stool_resultstream keyed bytool_useid.mcp_serversvia SDK config overrides; observation onmcp_tool_callcompletion events; MCP calls count against the evals: merge-readiness fixes for the external-harness stack #2649 tool-step budget.observedToolNamematcher generalizes observation attachment to MCP steps; the strict===count guard is kept (the facade's>=relaxation is deliberately not adopted — it reintroduces positional misattribution; the trailing-capture undercount wants an attempted-run count instead, follow-up).Deliberately out (punted with the facade):
stagehand_playwright_code, callback batching, the facade's codex-onlyprepareCodexPlaywrightMcpAdapter(superseded by the generic path).Verification
mcp__playwright__*tools for 50 steps, per-step url/aria probe evidence attached to MCP steps only, verifier graded a 5-criterion rubric (0 unverifiable)mcp_serversreached Codex and servers started)Known rough edge: probe screenshots through the harness-side
playwright_mcpsession are flaky under the 10s observation timeout (url/aria reliable; ~1/40 screenshots in the smoke). Follow-up: bump timeout for MCP surfaces or capture via raw CDP.Summary by cubic
Enable MCP browser tool surfaces on the
claude_codeandcodexharnesses.--tool playwright_mcpand--tool chrome_devtools_mcpnow run end-to-end with per-step evidence attached to MCP steps; plus reliability fixes for capture and tab syncing.New Features
via: "mcp") withbuildPlaywrightMcpLaunchSpec/buildChromeDevtoolsMcpLaunchSpec, pluscaptureEvidence.mcpServersto the SDK, allowsmcp__<server>tools, and attaches observations from thetool_resultstream (trackstool_useid → name).mcp_serversvia SDK config overrides; records observations onmcp_tool_callcompletion; MCP calls count toward the tool-step budget.observedToolNamematcher so observations attach to MCP steps; strict count guard remains to avoid misattribution.Bug Fixes
drainStepObservationsis async on both harnesses to avoid missing last-step evidence.captureEvidenceis bounded byEVAL_CAPTURE_EVIDENCE_TIMEOUT_MS(default 15s) to prevent stalls.Written for commit 544b54a. Summary will update on new commits.