Skip to content

[MCP] No way to disable the auto-injected CDP TCP port (pipe-mode) for isolated/persistent browser contexts #41166

@j-madrone

Description

@j-madrone

Summary

@playwright/mcp's IsolatedContextFactory and PersistentContextFactory both call injectCdpPort() unconditionally for Chromium, which assigns launchOptions.cdpPort = <random free TCP port>. Playwright then launches Chromium with --remote-debugging-port=<port> and connects to it over 127.0.0.1.

There is no config option to opt out of this and have Playwright drive the browser over --remote-debugging-pipe (the parent/child fd transport that browserType.launch() uses by default when no cdpPort is set).

Why this matters (sandboxed environments)

In hardened/sandboxed deployments the process running the MCP server may not be allowed to make loopback TCP connections. In our case the MCP server runs as a dedicated unprivileged uid whose loopback egress is blocked by an iptables rule (so untrusted code in the same container cannot reach other in-pod services). Because injectCdpPort forces a TCP CDP port, every browser action fails at launch with:

browserType.launch: WebSocket error: connect ECONNREFUSED 127.0.0.1:<cdpPort>

A plain chromium.launch() (no cdpPort) works fine in the same environment because it uses --remote-debugging-pipe — no TCP involved. So the browser engine is fine; only the MCP-injected TCP port is the problem.

Repro

  1. Run @playwright/mcp with isolated: true (Chromium) in an environment where the server process cannot open loopback TCP connections (e.g. an iptables -A OUTPUT -o lo -m owner --uid-owner <uid> -j REJECT rule).
  2. Call browser_navigate.
  3. Launch fails with connect ECONNREFUSED 127.0.0.1:<port>.

Requested change

Add an opt-in config option to skip the injected CDP port and let Playwright use pipe transport — e.g. browser.launchOptions.cdpPort: false (or a top-level browser.useCdpPort: false / env PLAYWRIGHT_MCP_NO_CDP_PORT). When set, injectCdpPort becomes a no-op and Chromium launches with --remote-debugging-pipe.

This keeps the current default (TCP CDP port) for everyone who relies on it, while letting locked-down/sandboxed deployments run without granting loopback TCP.

Workaround

We currently patch-package injectCdpPort to a no-op. A supported config option would let us drop the patch.

Versions: @playwright/mcp@0.0.68 (also confirmed present in 0.0.75 / playwright-core@1.61.0-alpha).

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions