Skip to content

feat: add WebSocket message inspection tools#2370

Open
johan-gorter wants to merge 1 commit into
ChromeDevTools:mainfrom
johan-gorter:feat/websocket-inspection
Open

feat: add WebSocket message inspection tools#2370
johan-gorter wants to merge 1 commit into
ChromeDevTools:mainfrom
johan-gorter:feat/websocket-inspection

Conversation

@johan-gorter

Copy link
Copy Markdown

Summary

Adds three read-only tools that let an agent inspect the WebSocket traffic of
the selected page — the one common network primitive the server currently
can't see, since Puppeteer does not expose WebSocket frames:

  • list_websocket_connections — connections of the selected page, with a
    stable wsId, status, and sent/received counts.
  • list_websocket_messages — messages of a connection (oldest first) as
    single-line previews, filterable by direction and by a payload substring,
    with pagination.
  • get_websocket_message — the full payload of one message, optionally saved
    to a file inside the configured workspace roots.

How it works

Because Puppeteer doesn't surface WebSocket frames, a WebSocketCollector
consumes the CDP Network.webSocket* events directly from the page session
that is already enabled. It follows the existing PageCollector pattern used
by the console and network collectors, so it plugs into the same
per-page/per-navigation storage, disposal, and stable-id machinery with no
base-class changes. The CDP session is obtained via page._client(), the same
way PageCollector and WaitForHelper already do.

Capture starts the moment the page is inspected — reload to capture a
connection from its start. This is documented in the tool descriptions, the
README, and the skill.

Bounded memory

Retention is capped per connection so long-lived sockets can't grow unbounded:
the last 500 data messages within a 2 MB total budget, each stored payload
capped at 100k characters. Dropped and truncated messages are counted and
marked explicitly rather than silently discarded. Control frames (ping/pong)
are not recorded. All constants are exported and easy to tune — happy to adjust
if you'd prefer different defaults.

Single-page-app correctness

Field testing against a real SPA surfaced a subtlety worth calling out: an app
that rewrites its URL with history.pushState triggers Puppeteer's
framenavigated, but that is a same-document navigation and does not close
WebSockets. The collector therefore:

  • decides a connection is dead on Runtime.executionContextsCleared
    (cross-document navigations only), not on framenavigated, and
  • carries still-open connections across each navigation split, so a socket
    opened during page boot stays visible.

list_websocket_connections also accepts includePreservedConnections to list
connections closed by earlier navigations.

Docs & tests

  • README, tool reference (npm run gen), CLI options, and the
    chrome-devtools skill are updated.
  • 13 tests: unit tests for retention/truncation/control-frame handling, plus
    integration tests against a new dependency-free WebSocket echo endpoint in
    the test server (capture, direction/text filtering, truncation, same-document
    navigation survival, preserved connections, drop accounting, and the
    unknown-connection error path).

Open questions for reviewers

  • Retention defaults (500 / 2 MB / 100k) — reasonable, or would you prefer
    different limits or configurability?
  • includePreservedConnections and filePath saving add surface area; happy
    to drop either for a leaner first version if you'd rather.

@google-cla

google-cla Bot commented Jul 15, 2026

Copy link
Copy Markdown

Thanks for your pull request! It looks like this may be your first contribution to a Google open source project. Before we can look at your pull request, you'll need to sign a Contributor License Agreement (CLA).

View this failed invocation of the CLA check for more information.

For the most up to date status, view the checks section at the bottom of the pull request.

Adds list_websocket_connections, list_websocket_messages and
get_websocket_message so that WebSocket traffic of the selected page can
be inspected. Puppeteer does not expose WebSocket frames, so a per-page
WebSocketCollector consumes the CDP Network.webSocket* events from the
existing page session, following the same PageCollector pattern as the
network and console collectors.

Messages are retained per connection with bounded memory: the last 500
data messages within a 2MB per-connection budget, each stored payload
capped at 100k characters, with explicit dropped/truncated markers.
Control frames (ping/pong) are not recorded.

Connections are decided dead on Runtime.executionContextsCleared
(cross-document navigations only) rather than framenavigated, and
still-open connections are carried across navigation splits, so a socket
opened during page boot stays visible when a single-page app rewrites
its URL with history.pushState. list_websocket_connections accepts
includePreservedConnections to also list connections closed by earlier
navigations.

The test server gained a dependency-free WebSocket echo endpoint to test
against a real connection.
@johan-gorter
johan-gorter force-pushed the feat/websocket-inspection branch 2 times, most recently from f9f361e to 78148ee Compare July 16, 2026 06:42
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.

1 participant