Skip to content

execute_playwright_code: drop replays + browser lifecycle; add manage_replays tool#124

Merged
dprevoznik merged 9 commits into
mainfrom
hypeship/playwright-drop-replays
Jul 21, 2026
Merged

execute_playwright_code: drop replays + browser lifecycle; add manage_replays tool#124
dprevoznik merged 9 commits into
mainfrom
hypeship/playwright-drop-replays

Conversation

@dprevoznik

@dprevoznik dprevoznik commented Jul 16, 2026

Copy link
Copy Markdown
Contributor

Summary

Splits browser lifecycle and replay recording out of execute_playwright_code and gives replays a dedicated tool. Net effect: the execute path is a lean passthrough, and recording becomes opt-in and session-scoped.

1. Remove per-call replay recording from execute_playwright_code

Every call wrapped the run in replays.start / replays.stop and validated existing sessions with browsers.retrieve, so an existing-session call made four sequential round tripsretrievereplays.startexecutereplays.stop — when only execute is useful work. The replay start/stop fired unconditionally whether or not the caller brought their own session, so this tax was on the critical path of every call in a tight agent loop.

Removed the replay start/stop (success and error paths), dropped replay_url from the response, and dropped the pre-execute browsers.retrieve validation (execute surfaces a not-found error on its own).

2. Make execute_playwright_code a passthrough — no browser lifecycle

session_id is now required. The tool no longer creates a stealth browser when session_id is omitted, and no longer deletes the browser afterward. It's now a thin wrapper over browsers.playwright.execute — inputs in, outputs back. Session lifecycle belongs to manage_browsers.

3. Add manage_replays tool

New replays toolset (src/lib/mcp/tools/replays.ts), backing browsers.replays with start / stop / list actions, gated by KERNEL_MCP_DISABLED_TOOLSETS like the rest. This restores the replay capability removed in (1), but session-scoped and opt-in: start once, run the automation, then stop — instead of recording each call separately. start takes optional framerate, max_duration_in_seconds, and record_audio.

⚠️ Breaking change

Callers that relied on omitting session_id to get a one-shot, auto-created-and-deleted browser must now:

  1. create a session with manage_browsers (action create),
  2. pass its session_id to execute_playwright_code, and
  3. delete it with manage_browsers when done.

Callers wanting a video replay must now manage_replays start/stop around their calls instead of reading replay_url off each response. Existing callers that already pass a session_id and don't use the replay URL are otherwise unaffected.

Follow-ups (not in this PR)

  • computer_action latency/defaults tweaks (reword away from batch-by-default, viewport-default screenshots).
  • Consider giving execute_playwright_code a fuller description again — it's now a short one-liner, and more usage guidance could help agents use it correctly.

Testing

  • tsc --noEmit and prettier --check pass.
  • Verified end-to-end against a live server using the MCP Inspector CLI:
bun run dev  # starts on :3002

npx -y @modelcontextprotocol/inspector --cli http://127.0.0.1:3002/mcp \
  --transport streamable-http \
  --header "Authorization: Bearer $KERNEL_API_KEY" \
  --method tools/list
# ...and --method tools/call --tool-name <tool> --tool-arg key=value

Confirmed: manage_replays is registered; execute_playwright_code requires session_id and returns no replay_url; a browser is not deleted after execute (lifecycle removed); omitting session_id is rejected; and manage_replays start/stop/list all work.

Two gotchas when running the Inspector: use 127.0.0.1 (not localhost, which its Node fetch resolves to IPv6 and fails), and pass --transport streamable-http. No .env/Clerk/Redis is needed — the API-key bearer path skips them, so bun run dev boots clean.


Note

Medium Risk
Breaking MCP contract for callers that omitted session_id or relied on automatic replay_url; behavior change is intentional but affects agent integrations and tight automation loops.

Overview
Breaking: execute_playwright_code now requires session_id and only calls browsers.playwright.execute—no implicit browser create/delete, no per-run replays.start/stop, and no replay_url in responses. Agents must use manage_browsers for lifecycle and opt into recording separately.

Adds a replays toolset with manage_replays (start / stop / list) for session-scoped MP4 recording (optional framerate, max_duration_in_seconds, record_audio), registered in register.ts and documented in README / .env.example alongside KERNEL_MCP_DISABLED_TOOLSETS.

Reviewed by Cursor Bugbot for commit dc60a9f. Bugbot is set up for automated code reviews on this repo. Configure here.

Each execute_playwright_code call wrapped the run in replays.start /
replays.stop and validated existing sessions with a browsers.retrieve,
adding three round trips around the one useful call. In a tight agent
loop that latency lands on the critical path every turn.

Drop the replay start/stop and the pre-execute retrieve; execute
surfaces a not-found error on its own. Existing-session calls now make
a single round trip. Video replay is handled separately via managed
replay controls.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@vercel

vercel Bot commented Jul 16, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
mcp Ready Ready Preview, Comment Jul 21, 2026 2:06am

Fold browser lifecycle out of the execute tool: session_id is now
required, and the tool no longer creates a stealth browser when it is
omitted or deletes the browser afterward. It is now a thin passthrough
to browsers.playwright.execute -- inputs in, outputs back.

Creating and deleting a browser inside an "execute" call hid a lifecycle
(and a baked-in stealth default) the caller never chose, and put create/
delete round trips on the path. Session lifecycle belongs to
manage_browsers; recording belongs to managed replays.

BREAKING CHANGE: callers that relied on omitting session_id to get a
one-shot auto-created browser must now create a session with
manage_browsers first and pass its session_id.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@dprevoznik dprevoznik changed the title Remove per-call replay recording from execute_playwright_code Make execute_playwright_code a lean passthrough (drop replays + auto browser lifecycle) Jul 16, 2026
dprevoznik and others added 2 commits July 16, 2026 20:53
Introduce a dedicated replays toolset backing browsers.replays with
start/stop/list actions, registered as the "replays" toolset (gated by
KERNEL_MCP_DISABLED_TOOLSETS like the others). Recording is now
session-scoped and opt-in: start once, run the automation, then stop --
replacing the per-call recording that used to be wrapped around every
execute_playwright_code call.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@dprevoznik dprevoznik changed the title Make execute_playwright_code a lean passthrough (drop replays + auto browser lifecycle) execute_playwright_code: drop replays + browser lifecycle; add manage_replays tool Jul 16, 2026
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Let the agent choose between page.screenshot() and computer_action
rather than steering it toward one.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes using high effort and found 1 potential issue.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Want fixes drafted automatically? Bugbot Autofix can create code changes for findings. A team admin can enable Autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit 1265a2d. Configure here.

Comment thread src/lib/mcp/tools/replays.ts

@dprevoznik dprevoznik left a comment

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewed mcp changes locally and breaking decision to simplify playwright execution tool.

@dprevoznik
dprevoznik requested a review from masnwilliams July 17, 2026 12:37

@masnwilliams masnwilliams left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

approving — clean, well-scoped refactor. splitting recording into manage_replays and making execute_playwright_code do one thing reads well, and the toolset gating is wired consistently across register.ts, .env.example, and the README. typecheck + prettier are clean, and the SDK method signatures / response shapes all match usage (replays.list returns a plain array, so itemsJsonResponse is the right call).

breaking change to execute_playwright_code (now requires session_id, no auto-create/cleanup) is intended — noting it here for consumers, no action needed.

two nits, non-blocking:

  • src/lib/mcp/tools/playwright.ts:16session_id: z.string() now accepts "". the old code guarded with !session_id; an empty string now flows into playwright.execute("", …) and surfaces as an opaque API error. consider .min(1) for a cleaner validation message.
  • src/lib/mcp/tools/replays.ts:37framerate has .min(1) but no upper bound; the description notes >20 needs GPU. fine as-is, just flagging there's no max enforced.

Add .min(1, "session_id is required") so an empty string is caught by
schema validation with a clear message instead of flowing into
playwright.execute and surfacing as an opaque API error.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@dprevoznik
dprevoznik force-pushed the hypeship/playwright-drop-replays branch from 719dffa to dc60a9f Compare July 21, 2026 02:06
@dprevoznik

Copy link
Copy Markdown
Contributor Author

src/lib/mcp/tools/playwright.ts:16 — session_id: z.string() now accepts "". the old code guarded with !session_id; an empty string now flows into playwright.execute("", …) and surfaces as an opaque API error. consider .min(1) for a cleaner validation message.

Fixed this here

src/lib/mcp/tools/replays.ts:37 — framerate has .min(1) but no upper bound; the description notes >20 needs GPU. fine as-is, just flagging there's no max enforced.

Leaving this out for now.

@dprevoznik
dprevoznik merged commit ed1f7e7 into main Jul 21, 2026
10 checks passed
@dprevoznik
dprevoznik deleted the hypeship/playwright-drop-replays branch July 21, 2026 02:12
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants