# How the MCP Telemetry System Sends Usage Data to Google Clearcut

> Learn how the MCP telemetry system sends usage data to Google Clearcut. Discover its watchdog subprocess, batching, POST requests, and retry logic for efficient logging.

- Repository: [ChromeDevTools/chrome-devtools-mcp](https://github.com/chromedevtools/chrome-devtools-mcp)
- Tags: internals
- Published: 2026-02-19

---

**The MCP telemetry system uses a watchdog subprocess to batch events and POST them to Google Clearcut's logging API at 15-minute intervals, with automatic rate-limit handling and retry logic.**

The Chrome DevTools MCP (Model Context Protocol) server implements a privacy-conscious telemetry pipeline that buffers usage data locally before transmitting it to Google Clearcut. This article examines the complete data flow—from event generation in the main process to the final HTTP POST—based on the actual implementation in the `ChromeDevTools/chrome-devtools-mcp` repository.

## Architecture Overview of the MCP Telemetry Pipeline

The MCP telemetry system operates as a four-stage pipeline that isolates network I/O from the main server process. This design ensures that telemetry collection never blocks tool execution, even during network outages or high-latency scenarios.

The pipeline consists of:

1. **Event Generation**: `ClearcutLogger` creates structured JSON events
2. **Inter-Process Communication**: `WatchdogClient` streams events via stdin to a detached subprocess
3. **Buffering & Scheduling**: `ClearcutSender` accumulates events and manages flush timers
4. **Network Transmission**: Batched HTTP POST requests to `play.googleapis.com/log`

## Stage 1: Event Generation with ClearcutLogger

The `ClearcutLogger` class in [`src/telemetry/ClearcutLogger.ts`](https://github.com/ChromeDevTools/chrome-devtools-mcp/blob/main/src/telemetry/ClearcutLogger.ts) serves as the primary interface for the MCP telemetry system. It exposes strongly-typed methods for recording specific event types while abstracting the underlying transport mechanism.

### Initializing the Logger

When the MCP server starts in [`src/main.ts`](https://github.com/ChromeDevTools/chrome-devtools-mcp/blob/main/src/main.ts), it instantiates `ClearcutLogger` with configuration options that control the telemetry endpoint and flush behavior:

```typescript
import { ClearcutLogger } from './src/telemetry/ClearcutLogger.js';

const telemetry = new ClearcutLogger({
  appVersion: '1.2.3',
  clearcutEndpoint: 'https://play.googleapis.com/log?format=json_proto',
  clearcutForceFlushIntervalMs: 15 * 60 * 1000, // 15 minutes
  clearcutIncludePidHeader: false,
});

```

### Recording Tool Invocations

The logger provides specific methods for different telemetry events. When a DevTools tool executes, the server calls `logToolInvocation()`:

```typescript
await telemetry.logToolInvocation({
  toolName: 'coverage',
  success: true,
  latencyMs: 124,
});

```

Internally, this method constructs a `WatchdogMessage` and forwards it to the `WatchdogClient` instance, converting the structured data into a JSON line written to the subprocess stdin.

## Stage 2: Inter-Process Communication via WatchdogClient

The `WatchdogClient` class in [`src/telemetry/WatchdogClient.ts`](https://github.com/ChromeDevTools/chrome-devtools-mcp/blob/main/src/telemetry/WatchdogClient.ts) manages the lifecycle of the telemetry subprocess. By running the network client in a separate Node.js process, the MCP telemetry system ensures that crashes or hangs in the telemetry logic cannot affect the main MCP server.

### Spawning the Watchdog Subprocess

The client spawns the watchdog entry point ([`watchdog/main.js`](https://github.com/ChromeDevTools/chrome-devtools-mcp/blob/main/watchdog/main.js)) as a detached child process with a pipe on stdin:

```typescript
import { spawn } from 'child_process';
import { fileURLToPath } from 'url';

const watchdogPath = fileURLToPath(
  new URL('./watchdog/main.js', import.meta.url)
);

const child = spawn(process.execPath, [watchdogPath, ...cliArgs], {
  stdio: ['pipe', 'ignore', 'ignore'],
  detached: true,
});

```

The CLI arguments passed to the watchdog include the Clearcut endpoint URL, flush interval, and optional PID header flag.

### Message Transport Protocol

Communication between the parent and watchdog uses newline-delimited JSON (NDJSON). When `ClearcutLogger` records an event, `WatchdogClient.send()` serializes the message:

```typescript
// Inside ClearcutLogger.logToolInvocation()
this.#watchdog.send({
  type: WatchdogMessageType.LOG_EVENT,
  payload: {
    tool_invocation: {
      tool_name: args.toolName,
      success: args.success,
      latency_ms: args.latencyMs,
    },
  },
});

```

The watchdog process reads these lines using a `readline.Interface` and forwards valid events to the `ClearcutSender` for buffering and transmission.

## Stage 3: Event Buffering and Session Management

Inside the watchdog subprocess, `ClearcutSender` (located in [`src/telemetry/watchdog/ClearcutSender.ts`](https://github.com/ChromeDevTools/chrome-devtools-mcp/blob/main/src/telemetry/watchdog/ClearcutSender.ts)) implements the client-side buffering logic. This component ensures efficient network usage by aggregating multiple telemetry events into single HTTP requests.

### The ClearcutSender Buffer

The sender maintains an in-memory array with a maximum capacity of 1000 events (`MAX_BUFFER_SIZE`). When the buffer reaches capacity, the oldest events are dropped to prevent unbounded memory growth:

```typescript
private enqueueEvent(event: TelemetryEvent): void {
  if (this.#buffer.length >= MAX_BUFFER_SIZE) {
    this.#buffer.shift(); // Drop oldest event
  }
  
  const enrichedEvent = {
    ...event,
    session_id: this.#sessionId,
    app_version: this.#appVersion,
    os_type: this.#osType,
  };
  
  this.#buffer.push(enrichedEvent);
  
  if (!this.#flushScheduled) {
    this.#scheduleFlush();
  }
}

```

### Session Decoration and Metadata

Every event is enriched with session metadata before buffering. The `session_id` is generated once per watchdog process using `crypto.randomUUID()`, ensuring that events from a single MCP server instance can be correlated. The `app_version` and `os_type` are derived from CLI arguments passed during watchdog initialization.

## Stage 4: HTTP Transmission to Google Clearcut

The final stage occurs when the flush timer fires or the buffer reaches capacity. The `ClearcutSender.#sendBatch()` method handles the HTTP POST to Google Clearcut's logging infrastructure.

### Batch Request Format

The sender constructs a `LogRequest` payload conforming to the Clearcut JSON-proto format. The request includes a `log_source` identifier (2839 for Chrome DevTools MCP), `client_info`, and an array of `log_event` objects:

```typescript
const requestBody: LogRequest = {
  log_source: LOG_SOURCE, // 2839
  request_time_ms: Date.now().toString(),
  client_info: { client_type: CLIENT_TYPE }, // 47
  log_event: events.map(({event, timestamp}) => ({
    event_time_ms: timestamp.toString(),
    source_extension_json: JSON.stringify(event),
  })),
};

await fetch(this.#clearcutEndpoint, {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    ...(this.#includePidHeader ? { 'X-Watchdog-Pid': process.pid.toString() } : {}),
  },
  body: JSON.stringify(requestBody),
  signal: controller.signal,
});

```

The `source_extension_json` field contains the stringified telemetry event, allowing Clearcut to store arbitrary JSON structures while maintaining a consistent schema at the transport layer.

### Rate Limiting and Retry Logic

The MCP telemetry system implements sophisticated back-off handling to respect Clearcut's rate limits. When the server responds with a `next_request_wait_millis` value, the sender adjusts its flush interval accordingly, with a minimum wait time of 30 seconds between requests:

- **Transient errors** (5xx, network timeouts): Events are re-queued for the next flush attempt
- **Permanent errors** (4xx client errors): Events are discarded to prevent infinite retry loops
- **Timeout handling**: Each request uses an `AbortController` with a 30-second timeout to prevent hanging connections

### Shutdown Flushing

When the MCP server shuts down, the watchdog process detects the stdin closure and triggers a final flush. The `sendShutdownEvent()` method enqueues a `server_shutdown` event and calls `#finalFlush()`, which bypasses the normal scheduling logic to ensure all pending telemetry reaches Clearcut before the process exits:

```typescript
// In watchdog/main.ts
rl.on('close', () => {
  sender.sendShutdownEvent();
  setTimeout(() => process.exit(0), 5000); // Allow 5s for final flush
});

```

## Summary

The MCP telemetry system in Chrome DevTools implements a robust, multi-process pipeline for sending usage data to Google Clearcut:

- **Process isolation** prevents telemetry operations from impacting the main MCP server performance
- **Batching and buffering** reduce network overhead by aggregating up to 1000 events per HTTP request
- **Automatic retry and rate-limit handling** ensure reliable delivery without overwhelming the Clearcut endpoint
- **Graceful shutdown flushing** guarantees that final events are transmitted before process termination
- **Configurable endpoints** allow for testing and custom telemetry backends via CLI arguments

## Frequently Asked Questions

### What is the default flush interval for MCP telemetry events?

The default flush interval is **15 minutes** (900,000 milliseconds). This value is passed to the watchdog subprocess via the `--clearcut-force-flush-interval-ms` CLI argument and can be overridden when initializing the `ClearcutLogger` for testing or specific deployment requirements.

### How does the MCP telemetry system handle network failures?

The system implements a multi-layered resilience strategy. `ClearcutSender` distinguishes between transient errors (5xx status codes, timeouts) and permanent errors (4xx client errors). Transient failures trigger re-queueing of events for the next flush cycle, while permanent failures result in event discard to prevent infinite loops. Additionally, the system respects server-side rate limiting via the `next_request_wait_millis` response field, enforcing a minimum 30-second wait between requests.

### Can the Clearcut endpoint be customized for testing?

Yes, the endpoint is fully configurable. When creating a `ClearcutLogger` instance, you can pass `clearcutEndpoint` in the constructor options to override the default `https://play.googleapis.com/log?format=json_proto`. This is particularly useful for integration testing or when routing telemetry through a proxy. Additionally, the `clearcutIncludePidHeader` option enables the `X-Watchdog-Pid` header for process tracking in test environments.

### What happens to telemetry events when the MCP server shuts down?

During shutdown, the watchdog process detects the closure of stdin from the parent process and triggers a graceful final flush. The `sendShutdownEvent()` method enqueues a `server_shutdown` event and invokes `#finalFlush()`, which bypasses the normal scheduling timer to immediately attempt transmission of all buffered events. The process waits up to 5 seconds (configurable via shutdown timeout) to allow the HTTP request to complete before exiting, ensuring minimal data loss during routine restarts or updates.