section · decisions
0031 Browser Automation Is Opt In
ADR 0031: Browser automation (Playwright MCP) is opt-in, not a default-managed MCP
- Date: 2026-06-08
- Status: proposed
- Deciders: Construct·Architect
- Supersedes: none
- Extends: ADR 0024 (rejected Puppeteer's bundled headless Chromium on footprint grounds — applies the same footprint test to the Playwright MCP), ADR 0025 ("aware, never ambient" — a heavyweight capability no prompt invokes is ambient), ADR 0027 and ADR 0029 (project-local-by-default; no silent heavyweight writes).
Problem
The Playwright MCP server (@playwright/mcp) was written into every Construct project's Claude config by default, but it was not earning that ambient slot:
- No prompt directs its use. Neither
personas/construct.md,specialists/prompts/cx-qa.md, norspecialists/prompts/cx-researcher.mdinstruct the agent to drive a browser, take screenshots, or run Playwright.lib/mcp-catalog.jsonlists it inusedBy: [construct, cx-qa, cx-researcher], but that is a capability inventory, never a goal-directed instruction. - Redundant for the common case.
cx-researcheralready hasWebSearch/WebFetch(specialists/registry.json:275) andcontext7(core docs MCP) covers library/framework lookups. Playwright adds value only for interactive DOM/JS/screenshot work, which no prompt requests. - Heavyweight footprint.
npx -y @playwright/mcpdownloads browser binaries (hundreds of MB) on first use — the exact cost ADR 0024 rejected Puppeteer for, and whichCHANGELOG.mdcompares toplaywright installpre-fetching browsers. - Inconsistent with its own opt-in model.
lib/mcp-catalog.jsonalready classifies Playwrightcategory: "optional"and a first-class opt-in command exists (construct mcp add \<id>,lib/mcp-manager.mjs:255). But the project-scope sync (scripts/sync-specialists.mjs,writeProjectClaudeSettings) merges the fullplatforms/claude/settings.template.jsonmcpServersand the fullspecialists/registry.jsonmcpServersinto every project regardless of catalog category — so Playwright was installed despite the opt-in model. (Global scope had already pruned it via theGLOBAL_CLAUDE_MCP_IDSallowlist; the inconsistency was project scope.)
The decision-forcing tension: keep browser automation as an ambient default for the rare case it might be used, or move it behind the existing explicit opt-in to honor the footprint doctrine while preserving the one genuine niche (cx-qa live web-UI testing).
Decision
Demote Playwright from the default-managed MCP set to explicitly opt-in. Keep the capability; drop the ambient install.
- Remove
playwrightfromspecialists/registry.jsonmcpServersand fromplatforms/claude/settings.template.jsonmcpServers— the two sourceswriteProjectClaudeSettingsmerges into project config. Project sync no longer writes it. - Retain the
playwrightentry inlib/mcp-catalog.json(category: "optional"), version-pinned to@playwright/mcp@0.0.75(no cold-start@latestresolution), soconstruct mcp add playwrightinstalls it on demand. - Because Playwright is no longer in the registry, the global prune (
syncGlobalClaudeMcpServers) treats a user-added entry as a non-Construct MCP and leaves it alone — opt-in persists acrossconstruct sync. - Document the opt-in in
specialists/prompts/cx-qa.md: live-browser UI testing requiresconstruct mcp add playwright.
Full deletion was rejected: QA browser-based UI testing (E2E, visual regression, keyboard/a11y of a live page) is a legitimate future need; the capability should remain one command away, not gone.
Consequences
- Footprint: a fresh
construct init/syncno longer carries a hundreds-of-MB browser-binary liability that no prompt exercises. Consistent with ADR-0024/0025/0027/0029. - Opt-in cost: agents/users who genuinely need browser automation run
construct mcp add playwrightonce. The capability is discoverable viaconstruct mcp list(under "Enhancements"). - Scope:
construct mcp addwrites to the user's global~/.claude.jsontop-levelmcpServers(lib/mcp-manager.mjsgetClaudeUserConfigPath— corrected from~/.claude/settings.jsonby construct-ranh, which is not a file Claude Code reads MCP server definitions from), so opt-in is per-user, not per-project. Acceptable because it is an explicit user choice, not a silent default; narrowing it to project scope is a follow-up if desired. - Follow-up (not in this ADR): the systemic fix is to make
writeProjectClaudeSettingshonor catalogcategoryso alloptional/integrationMCPs (memory,sequential-thinking,github) follow the same opt-in rule at project scope — mirroring theGLOBAL_CLAUDE_MCP_IDSallowlist already used for global scope. - Coverage:
tests/sync-contract.test.mjsasserts project settings excludeplaywrightand that the global prune preserves a user-addedplaywrightentry.