TL;DR: Replace the monolithic 2223-LOC MCP server with a layered architecture: a routing layer for tool dispatch and an execution layer for browser automation, plus deletion of ~933 LOC of obsoleted protocol modules.
Context
Part of Epic #35 (v0.2.0 architecture cleanup). src/mcp/server.ts currently mixes 7 concerns in a single file: type definitions, mutable state management, session handling, event listeners, browser lifecycle, 26 tool registrations with inline logic, and signal handlers. Splitting these into a routing layer and execution layer enables Epic 2–3 improvements (caching, test scenarios) to be added as interceptors without coupling to specific tools.
This sub-issue also deletes 3 protocol-validation modules (~933 LOC) that become redundant when SDK-level protocol handling takes over.
Acceptance Criteria
Scope
In Scope:
- Create
src/proxy/proxy-server.ts (~280 LOC) — MCP Server, tool list merge, call routing, cleanup handlers
- Create
src/proxy/upstream-client.ts (~250 LOC) — subprocess lifecycle, auto-restart with rate-limit guard
- Replace
src/mcp/server.ts content with 3-line backward-compat shim
- Add
@playwright/mcp@0.0.75 to dependencies (exact pin)
- Update
tsup.config.ts: new proxy entry + @playwright/mcp external
- Delete
src/core/protocol-validator.ts (276 LOC)
- Delete
src/core/protocol-validation-layer.ts (320 LOC)
- Delete
src/core/protocol-error-recovery.ts (337 LOC)
- Remove protocol exports from
src/index.ts: DefaultMCPProtocolValidator, ProtocolError, ProtocolValidationLayer, ProtocolErrorRecovery
- Delete protocol test files:
tests/protocol-basic.test.js, tests/protocol-validation.test.js, tests/protocol-validation-simple.test.js
Out of Scope:
- Interception logic for click/type (deferred to Epic 2)
- Local tools (
browser_save_test, browser_session_*) — empty local-tool registry in Epic 1, populated in Epic 3
--caps flag forwarding (deferred)
BASE_URL env var URL rewriting (acknowledged alpha.1 regression; restored in Epic 2)
Technical Implementation
-
New files:
| File |
LOC est. |
Purpose |
src/proxy/proxy-server.ts |
~280 |
MCP Server (low-level), setRequestHandler for ListToolsRequestSchema + CallToolRequestSchema, inline local-tool registry, signal cleanup |
src/proxy/upstream-client.ts |
~250 |
Spawn @playwright/mcp subprocess, Client + StdioClientTransport, auto-restart on transport.onclose with rate-limit (max 5/min), restartWith() for Epic 3 session/profile changes |
-
Backward-compat shim (src/mcp/server.ts):
import '../proxy/proxy-server.js';
This keeps dist/mcp/server.cjs as a valid entry point for existing .mcp.json configs.
-
tsup.config.ts changes:
- Add entry for
src/proxy/proxy-server.ts → dist/proxy/
- Add
'@playwright/mcp' to externals in MCP server entry
- Both
dist/mcp/server.cjs (shim) and dist/proxy/proxy-server.cjs get built
-
package.json changes:
- Add
"@playwright/mcp": "0.0.75" to dependencies
- Verify
engines.node is >=18.0.0
-
Auto-restart design: transport.onclose triggers restart with rate-limit guard. Max 5 restarts per 60s window; beyond that, exit with error. intentionalShutdown flag prevents restart on clean stop().
-
Subprocess resolution: require.resolve('@playwright/mcp/cli.js') — uses Node module resolution, no network per start. Fallback: document env var PLAYWRIGHT_MCP_CLI_PATH for unusual setups.
Architecture Reference
From Epic #35 Architecture Decisions:
- MCP SDK
Server class (low-level), not McpServer
@playwright/mcp@0.0.75 pinned
- Backward-compat shim at
src/mcp/server.ts
- Protocol modules deleted
browser_* prefix for all tools
tsup.config.ts updated
Dependencies
TL;DR: Replace the monolithic 2223-LOC MCP server with a layered architecture: a routing layer for tool dispatch and an execution layer for browser automation, plus deletion of ~933 LOC of obsoleted protocol modules.
Context
Part of Epic #35 (v0.2.0 architecture cleanup).
src/mcp/server.tscurrently mixes 7 concerns in a single file: type definitions, mutable state management, session handling, event listeners, browser lifecycle, 26 tool registrations with inline logic, and signal handlers. Splitting these into a routing layer and execution layer enables Epic 2–3 improvements (caching, test scenarios) to be added as interceptors without coupling to specific tools.This sub-issue also deletes 3 protocol-validation modules (~933 LOC) that become redundant when SDK-level protocol handling takes over.
Acceptance Criteria
src/proxy/module structure in place (proxy-server.ts,upstream-client.ts)npm test.mcp.jsonconfigurations from prior versions work without modificationprotocol-validator.ts,protocol-validation-layer.ts,protocol-error-recovery.tstsup.config.tsupdated with new entry point and externalsScope
In Scope:
src/proxy/proxy-server.ts(~280 LOC) — MCPServer, tool list merge, call routing, cleanup handlerssrc/proxy/upstream-client.ts(~250 LOC) — subprocess lifecycle, auto-restart with rate-limit guardsrc/mcp/server.tscontent with 3-line backward-compat shim@playwright/mcp@0.0.75todependencies(exact pin)tsup.config.ts: new proxy entry +@playwright/mcpexternalsrc/core/protocol-validator.ts(276 LOC)src/core/protocol-validation-layer.ts(320 LOC)src/core/protocol-error-recovery.ts(337 LOC)src/index.ts:DefaultMCPProtocolValidator,ProtocolError,ProtocolValidationLayer,ProtocolErrorRecoverytests/protocol-basic.test.js,tests/protocol-validation.test.js,tests/protocol-validation-simple.test.jsOut of Scope:
browser_save_test,browser_session_*) — empty local-tool registry in Epic 1, populated in Epic 3--capsflag forwarding (deferred)BASE_URLenv var URL rewriting (acknowledged alpha.1 regression; restored in Epic 2)Technical Implementation
New files:
src/proxy/proxy-server.tsServer(low-level),setRequestHandlerforListToolsRequestSchema+CallToolRequestSchema, inline local-tool registry, signal cleanupsrc/proxy/upstream-client.ts@playwright/mcpsubprocess,Client+StdioClientTransport, auto-restart ontransport.onclosewith rate-limit (max 5/min),restartWith()for Epic 3 session/profile changesBackward-compat shim (
src/mcp/server.ts):This keeps
dist/mcp/server.cjsas a valid entry point for existing.mcp.jsonconfigs.tsup.config.tschanges:src/proxy/proxy-server.ts→dist/proxy/'@playwright/mcp'to externals in MCP server entrydist/mcp/server.cjs(shim) anddist/proxy/proxy-server.cjsget builtpackage.jsonchanges:"@playwright/mcp": "0.0.75"to dependenciesengines.nodeis>=18.0.0Auto-restart design:
transport.onclosetriggers restart with rate-limit guard. Max 5 restarts per 60s window; beyond that, exit with error.intentionalShutdownflag prevents restart on cleanstop().Subprocess resolution:
require.resolve('@playwright/mcp/cli.js')— uses Node module resolution, no network per start. Fallback: document env varPLAYWRIGHT_MCP_CLI_PATHfor unusual setups.Architecture Reference
From Epic #35 Architecture Decisions:
Serverclass (low-level), notMcpServer@playwright/mcp@0.0.75pinnedsrc/mcp/server.tsbrowser_*prefix for all toolstsup.config.tsupdatedDependencies