Claude
Skills
Sign in
Back

websocket-monitor

Included with Lifetime
$97 forever

Monitor WebSocket connections via CDP: lifecycle events, message payloads with JSON auto-parse, frequency and size analysis, latency measurement, reconnection tracking, and Socket.IO/engine.io protocol detection.

Data & Analytics

What this skill does


# WebSocket Monitor

Instrument a page to capture all WebSocket activity using Chrome DevTools
Protocol events. Provides real-time visibility into connection lifecycle,
message flow, payload analysis, and protocol-level detection for Socket.IO
and engine.io.

## When to Use

- Debugging WebSocket connection issues (handshake failures, unexpected closes).
- Analyzing message payloads and frequency for optimization.
- Verifying Socket.IO or engine.io protocol behavior.
- Tracking reconnection patterns and connection stability.
- Measuring message latency between sent and received pairs.
- Profiling WebSocket bandwidth usage (message sizes).

## Prerequisites

- **Playwright MCP server** connected and responding (all `mcp__playwright__browser_*` tools available).
- **Chromium-based browser** required for CDP WebSocket events.
- Target page must establish WebSocket connections (the monitor captures connections created after instrumentation is installed).

## Workflow

### Step 1 -- Install CDP WebSocket Listeners

Set up CDP session and register all WebSocket event handlers before navigating
to the target page.

```javascript
browser_run_code({
  code: `async (page) => {
    const client = await page.context().newCDPSession(page);
    await client.send('Network.enable');

    // Storage for all WebSocket data
    window.__wsMonitor = {
      connections: {},
      messages: [],
      timeline: [],
      stats: { totalSent: 0, totalReceived: 0, totalBytesSent: 0, totalBytesReceived: 0 }
    };
    // Attach to page for later retrieval
    await page.evaluate(() => {
      window.__wsMonitor = {
        connections: {},
        messages: [],
        timeline: [],
        stats: { totalSent: 0, totalReceived: 0, totalBytesSent: 0, totalBytesReceived: 0 }
      };
    });

    const monitor = { connections: {}, messages: [], timeline: [] };
    let totalSent = 0, totalReceived = 0, totalBytesSent = 0, totalBytesReceived = 0;

    client.on('Network.webSocketCreated', (params) => {
      monitor.connections[params.requestId] = {
        requestId: params.requestId,
        url: params.url,
        createdAt: Date.now(),
        status: 'connecting',
        handshake: null,
        closedAt: null,
        closeCode: null,
        closeReason: null,
        messageCount: { sent: 0, received: 0 },
        byteCount: { sent: 0, received: 0 }
      };
      monitor.timeline.push({ type: 'created', requestId: params.requestId, url: params.url, time: Date.now() });
    });

    client.on('Network.webSocketHandshakeResponseReceived', (params) => {
      const conn = monitor.connections[params.requestId];
      if (conn) {
        conn.status = 'open';
        conn.handshake = {
          status: params.response.status,
          statusText: params.response.statusText,
          headers: params.response.headers
        };
      }
      monitor.timeline.push({ type: 'handshake', requestId: params.requestId, time: Date.now() });
    });

    client.on('Network.webSocketFrameSent', (params) => {
      const payload = params.response.payloadData;
      const size = new Blob([payload]).size;
      totalSent++;
      totalBytesSent += size;
      const conn = monitor.connections[params.requestId];
      if (conn) { conn.messageCount.sent++; conn.byteCount.sent += size; }
      monitor.messages.push({
        direction: 'sent',
        requestId: params.requestId,
        time: Date.now(),
        size: size,
        payload: payload.substring(0, 2000),
        opcode: params.response.opcode
      });
    });

    client.on('Network.webSocketFrameReceived', (params) => {
      const payload = params.response.payloadData;
      const size = new Blob([payload]).size;
      totalReceived++;
      totalBytesReceived += size;
      const conn = monitor.connections[params.requestId];
      if (conn) { conn.messageCount.received++; conn.byteCount.received += size; }
      monitor.messages.push({
        direction: 'received',
        requestId: params.requestId,
        time: Date.now(),
        size: size,
        payload: payload.substring(0, 2000),
        opcode: params.response.opcode
      });
    });

    client.on('Network.webSocketClosed', (params) => {
      const conn = monitor.connections[params.requestId];
      if (conn) {
        conn.status = 'closed';
        conn.closedAt = Date.now();
      }
      monitor.timeline.push({ type: 'closed', requestId: params.requestId, time: Date.now() });
    });

    // Store reference for later harvest
    page.__wsMonitorData = monitor;
    page.__wsMonitorStats = () => ({
      totalSent, totalReceived, totalBytesSent, totalBytesReceived
    });

    return 'WebSocket CDP listeners installed';
  }`
})
```

### Step 2 -- Install Protocol Detection (Socket.IO / engine.io)

Patch the WebSocket constructor on the page to detect Socket.IO and engine.io
handshake patterns.

```javascript
browser_evaluate({
  function: `() => {
    window.__wsProtocolInfo = [];
    const OrigWS = window.WebSocket;
    window.WebSocket = function(url, protocols) {
      const ws = new OrigWS(url, protocols);
      const info = {
        url: url,
        protocols: protocols || null,
        detectedProtocol: 'raw-websocket',
        engineIO: false,
        socketIO: false
      };

      // Detect engine.io / Socket.IO from URL patterns
      if (url.includes('engine.io') || url.includes('EIO=')) {
        info.engineIO = true;
        info.detectedProtocol = 'engine.io';
      }
      if (url.includes('socket.io') || url.includes('transport=websocket')) {
        info.socketIO = true;
        info.detectedProtocol = 'socket.io';
      }

      // Listen for first message to detect Socket.IO packet format
      const origOnMessage = ws.onmessage;
      const messageListener = (event) => {
        const data = typeof event.data === 'string' ? event.data : '';
        // engine.io open packet starts with '0'
        if (data.startsWith('0{') || data.startsWith('0\\x7b')) {
          info.engineIO = true;
          if (!info.socketIO) info.detectedProtocol = 'engine.io';
        }
        // Socket.IO connect packet
        if (data === '40' || data.startsWith('40{') || data.startsWith('42[')) {
          info.socketIO = true;
          info.detectedProtocol = 'socket.io';
        }
      };
      ws.addEventListener('message', messageListener);

      window.__wsProtocolInfo.push(info);
      return ws;
    };
    window.WebSocket.prototype = OrigWS.prototype;
    window.WebSocket.CONNECTING = OrigWS.CONNECTING;
    window.WebSocket.OPEN = OrigWS.OPEN;
    window.WebSocket.CLOSING = OrigWS.CLOSING;
    window.WebSocket.CLOSED = OrigWS.CLOSED;

    return 'WebSocket constructor patched for protocol detection';
  }`
})
```

### Step 3 -- Navigate to the Target Page

Navigate after instrumentation is installed to capture all connections from
page load.

```
browser_navigate({ url: "<target_url>" })
```

### Step 4 -- Wait for WebSocket Activity

Allow time for connections to establish and messages to flow.

```
browser_wait_for({ time: 10 })
```

Adjust the wait time based on expected application behavior. For real-time
apps, 10-30 seconds captures a representative sample. For apps that only
connect on specific user actions, perform those actions between waiting.

### Step 5 -- Harvest Connection Data

Retrieve all captured WebSocket data from the CDP monitor.

```javascript
browser_run_code({
  code: `async (page) => {
    const monitor = page.__wsMonitorData;
    const stats = page.__wsMonitorStats();
    if (!monitor) return { error: 'Monitor data not found' };

    return {
      connections: Object.values(monitor.connections),
      stats: stats,
      timelineLength: monitor.timeline.length,
      messageCount: monitor.messages.length
    };
  }`
})
```

### Step 6 -- Analyze Message Payloads

Parse JSON payloads and compute frequency/size statistics.

```javascript
browser_run_code({
  code: `async (page) => {
    const monitor = page.__wsMonit

Related in Data & Analytics