Repository navigation
feat(drivers): the browser computer serves Playwright MCP's browser_* tools, attached over CDP (FD-4955) - #615
Merged
earakely-scale merged 8 commits intoNov 11, 2026
Conversation
… tools, attached over CDP (FD-4955) The page driver installs Node 24.22.2 and Playwright MCP 0.0.84 from pinned downloads and serves its tools, with --caps vision, beside computer_*: a Playwright MCP it runs attached to the browser over a CDP address the browser's relay issues it, a session per caller, started again when it exits or the browser's data reset ends its address, ended with the driver, with none of the driver's environment and its file access confined to ~/Uploads. browser_evaluate and browser_run_code_unsafe are hidden by default, through ForwardedTool.hidden_by_default, which hides a forwarded tool as @tool(hidden_by_default=True) hides one of an env's own. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… sessions close, a stuck start is stopped A call races Playwright MCP's exit and its session's end, so one Playwright MCP stops answering fails then, without being sent again, since it may have acted. A caller's session left idle for an hour is closed when another caller comes, since a caller's own MCP session ends with no word to the driver. A start that never listens is stopped, and its address and URL are recorded only once it does, so the next call starts afresh. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A caller cancelled while its call waited left the call to Playwright MCP running; it's cancelled and awaited now, and not sent again. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A call is bounded by PLAYWRIGHT_MCP_CALL_TIMEOUT_S, and one that times out or fails in transport closes its session, so the next call opens a fresh one rather than reusing a session already closing. Opening a session is bounded by PLAYWRIGHT_MCP_START_S, and a Playwright MCP that can't open one in that time is stopped, so the next call starts it afresh instead of every caller waiting on the one that hangs. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The driver's own check of Playwright MCP's CDP address is sent without the agent's tool-call header, so the browser's log doesn't credit it to the browser_* call it's made during. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… script - scripts/upgrade_playwright_mcp.py pins a version: the install's downloads in env.toml, every npm package Playwright MCP needs checked against npm's integrity, and the manifest from that version's tools/list. Run at 0.0.84 it writes what was committed. - install-playwright-mcp.sh lays out whatever packages were downloaded, by the name each package.json gives, and fails when a dependency isn't there at the version it names. - The unit test pins the manifest's tool names, so an upgrade that adds a tool fails until someone looks at whether to hide it, and checks the manifest is of the version env.toml downloads. - Playwright MCP's process and sessions move to page/playwright_mcp.py. - ForwardedTool.listed builds a forwarded tool from another server's tools/list entry; the five drivers that forward tools use it. - mcp_port() is the port create_fastmcp_app serves on, which the page driver's CDP origin now reads instead of repeating its default. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The install moved each package's mktemp folder into node_modules, and mktemp makes its folder 0700, so the session user's Playwright MCP couldn't load its own cli.js and exited at start. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2 tasks done
earakely-scale
deleted the
edgararakelyan/fd-4955-playwright-mcp-on-chromium
branch
November 11, 2026 23:42
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
M8.8 (FD-4955), migration step 1. The browser computer, chromium alone, now serves Playwright MCP's
browser_*tools at its/mcp, besidecomputer_*and the page tools. Playwright MCP is 0.0.84, with--caps visionas today'splaywright-mcpruns, attached to chromium over CDP.browser_evaluate(JavaScript in the page) andbrowser_run_code_unsafe(code in Playwright MCP's own process).What changes
The page driver (
drivers/envs/page/)@playwright/mcp,playwrightandplaywright-core, which have no other dependencies.install-playwright-mcp.shlays them out as npm would, since an install has no network and the composer locks no npm packages. It lays out whatever packages were downloaded, by the name eachpackage.jsongives, and fails the build when a package's dependency isn't there at the version it names.python scripts/upgrade_playwright_mcp.py <version>pins a version:env.toml, following@playwright/mcp's dependencies (refusing a range, since the install takes exact versions)tools/list, run on this machine's Node of the same versionforwarded_tools, fromplaywright-mcp-tools.json, 0.0.84's owntools/listwithvision. An integration test checks the manifest against the installed version.page/playwright_mcp.py, besidedriver.py, which imports it as cua-driver importshyprland.py.playwright-mcp. The address is under the driver's loopback origin (fix(browser): CDP addresses through a TLS-ending tunnel are https and wss (FD-5470) #608'sorigin), through the driver's/svc/chromium/route, so the relay's guard applies to it as to any CDP client, and its connections show in the record.--isolated, no--headless, no idle timeout.setpriv --pdeathsigends it with the driver, so a restarted driver finds none of its predecessor's.playwright-mcp.browser_*call.browser_*call they serve is a follow-up.~/Uploads, its working folder with--ignore-roots, matchingcomputer_upload.The SDK (
agentenv_protocol)ForwardedTool.hidden_by_defaulthides a forwarded tool as@tool(hidden_by_default=True)hides one of the env's own:_metamarkertools="*"ForwardedTool.listed(tool, call)builds a forwarded tool from another server'stools/listentry: its name, description, schemas and annotations as listed. The five drivers that forward tools (page, cua-driver, agent-desktop, aside, codex-computer-use) use it, each forwarding the same tools as before.mcp_port()is the portcreate_fastmcp_appserves on (MCP_PORT, else 18765), which the page driver's loopback origin reads rather than repeating the default.Not in this PR:
playwright-mcp's users is M8.27's bar.The 0.0.41 → 0.0.84 contract change is on FD-4975 for that measurement:
browser_installis gone, and eight tools are new.refis renamedtargetin seven tools.Decisions (FD-4955, in chat with Edgar)
--caps visionis on, as today.browser_file_uploadis confined to~/Uploads. The rest are visible.ForwardedTool.hidden_by_defaultin the SDK, rather than a startup tool-access rule, which "every tool" steps and restarts would undo.Test plan
Unit:
_metamarker.env.tomldownloadstst/unit/drivers,env,bundleandtask_step6,022 passed, apart from the known Mac-only Windows disk-recipe case.tst/unit/drivers, 1,588 passed, 1 skipped.ForwardedTool.listedandmcp_port. After the refactor: the protocol package,tst/unit/driversandtst/unit/bundle, 2,822 passed, 12 skipped;tst/unit/env, 2,228 passed, apart from the Mac-only Windows disk-recipe case and a recorder timing case that passes alone.The install script, in an Ubuntu 24.04 container (arm64), with no network: with the pinned downloads it lays out the three packages and
cli.js --versionanswers 0.0.84; withoutplaywright-coreit fails, naming both packages that need it.Integration, local Docker (Mac, arm64): the new browser computer test, beside gate 3, the computer tools, the TLS front and
browser_connect_cdp: 5 of 5. On the real composed computer:browser_snapshotandbrowser_typetype into the page, and its viewport is unchangedbrowser_clickopens a confirm thatbrowser_handle_dialoganswersbrowser_evaluateonce it's namedtools/listequals the manifestplaywright-mcpconnectionA real agent, local Docker: Claude Code (Sonnet 5.5) completed a synthetic-page task through
browser_*alone. It snapshotted, typedlantern, clicked Reveal and answeredLANTERN-7777. Its trajectory has onlybrowser_*calls.A real agent,
modal_vm:LANTERN-7777, and the check scored 1.0.browser_snapshot,browser_type,browser_clickandbrowser_find.amd64 on local Docker is covered by CI's slow tier, which runs
browser_computer_test.pyon Linux amd64.Chaos and e2e, local Docker (arm64), timed. Playwright MCP's tools (navigate, type, click, read) still work:
browser_closereset_env's fresh containerEach answered within about a second.
Known limit: two
browser_*agents on one browser at once interfere. Today'splaywright-mcpgives each MCP session an isolated, headless context of its own. Here every session shares the one headful browser and its default context:One agent works, and so does a CDP agent beside it. Keeping agents apart is contexts' work (M8.15, M8.16). It's recorded on M8.27 (FD-4975) for its no-regression bar.
Fixed in #628: an intermittent
browser_evaluatehang. In a new session,browser_evaluatesometimes got no answer from Playwright MCP within the 300 s bound. A stress loop reproduced it within 2 to 16 rounds.The cause is in the browser's CDP relay, not here. It could deliver
Runtime.executionContextCreatedahead ofPage.getFrameTree's answer, so Playwright dropped the page's main world. #628 makes the relay keep the browser's order.With #628 on this branch, 80 rounds in a row answered within 0.6 s each. Until it merges, this PR's browser computer test can rarely time out at
browser_evaluate.Docs
The user guide's browser page should cover:
browser_*tools the browser computer serves (Playwright MCP 0.0.84,vision)~/Uploadsas their file root🤖 Generated with Claude Code


Confidence Score: 5/5
The PR appears safe to merge; no new blocking issue was found.Summary
The page driver serves Playwright MCP’s
browser_*tools on the computer’s existing browser through guarded CDP access.Diagram
%%{init: {'theme': 'neutral'}}%% flowchart LR Agent[Agent] --> Driver[Page driver MCP] Driver --> Access[Tool access check] Access --> MCP[Playwright MCP session] MCP --> Relay[Guarded CDP relay] Relay --> Browser[Existing Chromium browser]Reviews (11) · Last reviewed commit: "fix(drivers): let the session user read ..." · Reviewed by Greptile