# How MCP's Performance Trace Analysis Uses the Google CrUX API: A Deep Dive into Chrome DevTools MCP

> Discover how MCP's performance trace analysis integrates Google CrUX API data into Chrome DevTools for real-user insights and enhanced web performance evaluation.

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

---

**When a performance trace is stopped, MCP enriches the resulting `TraceResult` with real-user field data from the Chrome User Experience Report (CrUX) by fetching metrics for all URLs detected in the trace via the Google CrUX API.**

The Chrome DevTools MCP (Model Context Protocol) server bridges automated performance tracing with real-world user experience data. By integrating the Google CrUX API directly into its trace analysis pipeline, MCP allows developers to compare lab metrics against field data from actual Chrome users. This article examines how the `ChromeDevTools/chrome-devtools-mcp` repository implements this enrichment process.

## How CrUX Integration Works in MCP's Performance Tool

### Feature Flag Control (--performance-crux)

The CrUX integration is controlled by the `--performance-crux` CLI flag, which is **enabled by default**. This flag is stored in the `McpContext` class as the `performanceCrux` option and exposed through the `Context.isCruxEnabled()` method.

In [`src/McpContext.ts`](https://github.com/ChromeDevTools/chrome-devtools-mcp/blob/main/src/McpContext.ts) (lines 72-77), the context initializes this setting:

```typescript
// McpContext stores the performanceCrux option
this.performanceCrux = options.performanceCrux ?? true;

```

When users want to disable field data fetching, they pass `--no-performance-crux`, which sets this value to `false` and prevents any CrUX API calls during trace analysis.

### Trace Stop Handling and populateCruxData

The enrichment process triggers when a performance trace stops. Whether invoked manually via `performance_stop_trace` or through the auto-stop path, the system calls `stopTracingAndAppendOutput` in [`src/tools/performance.ts`](https://github.com/ChromeDevTools/chrome-devtools-mcp/blob/main/src/tools/performance.ts).

At lines 206-209, after parsing the raw trace into a `TraceResult`, the code checks the CrUX flag:

```typescript
// After trace parsing completes
if (context.isCruxEnabled()) {
  await populateCruxData(result);
}

```

If enabled, `populateCruxData(result)` executes, transforming the trace result with real-user metrics before returning the data to the client.

### CrUXManager Setup and API Configuration

The `populateCruxData` function interfaces with DevTools' internal CrUX infrastructure. It obtains the CrUX manager via `DevTools.CrUXManager.instance()`—a class bundled within the `chrome-devtools-frontend` package.

In [`src/tools/performance.ts`](https://github.com/ChromeDevTools/chrome-devtools-mcp/blob/main/src/tools/performance.ts) (lines 221-234), the setup proceeds as follows:

```typescript
// Obtain the DevTools CrUX manager
const cruxManager = DevTools.CrUXManager.instance();

// Force the CrUX endpoint to use a public API key for testing
cruxManager.getConfigSetting().set({
  ...cruxManager.getConfigSetting().get(),
  enabled: true,
  key: 'AIzaSy...'  // Public API key for testing
});

// Enable the 'field-data' setting so the manager requests field data
const fieldDataSetting = Common.Settings.Settings.instance().createSetting('field-data', false);
fieldDataSetting.set(true);

```

This configuration ensures the CrUX manager actively queries the Google CrUX API rather than returning cached or disabled states.

### URL Collection and Field Data Fetching

Once configured, MCP identifies all unique URLs present in the trace. The system collects URLs from two sources:

1. All URLs referenced in trace insights (`insight.values()`)
2. The main frame URL from the trace metadata

In [`src/tools/performance.ts`](https://github.com/ChromeDevTools/chrome-devtools-mcp/blob/main/src/tools/performance.ts) (lines 235-241), these URLs are deduplicated using a `Set`:

```typescript
const urls = new Set<string>();
for (const insight of result.insights.values()) {
  // Collect URLs from insights
  urls.add(insight.url);
}
urls.add(result.parsedTrace.Meta.mainFrameUrl);

```

The system then fetches CrUX data for each URL in parallel. At lines 247-255, `getFieldDataForPage(url)` is invoked for every unique URL:

```typescript
const cruxData = await Promise.all(
  Array.from(urls).map(async url => {
    const data = await cruxManager.getFieldDataForPage(url);
    return data;
  })
);

```

This returns an array of CrUX field data objects (or `null` for URLs not present in the CrUX dataset).

### Embedding CrUX Data in Trace Metadata

Finally, the fetched field data is embedded directly into the trace result's metadata structure. At lines 257-259 in [`src/tools/performance.ts`](https://github.com/ChromeDevTools/chrome-devtools-mcp/blob/main/src/tools/performance.ts), the data is assigned:

```typescript
result.parsedTrace.metadata = result.parsedTrace.metadata || {};
result.parsedTrace.metadata.cruxFieldData = cruxData;

```

This makes the CrUX data available to downstream consumers. When `response.attachTraceSummary` or `response.attachTraceInsight` processes the trace, it can access `trace.metadata.cruxFieldData` to display Core Web Vitals scores, LCP/CLS field values, and other real-user metrics alongside the lab data.

## Implementing CrUX Enrichment in Your MCP Workflow

To leverage CrUX data in your performance traces, ensure the feature is enabled (it is by default) and handle the enriched metadata in your response processing:

```typescript
// 1️⃣ Enable CrUX (default) – no extra flag needed
const mcp = await McpContext.from(browser, logger, {
  performanceCrux: true,
  experimentalDevToolsDebugging: false
});

// 2️⃣ Start a performance trace on the selected page
await mcp.performTool('performance_start_trace', {
  reload: true,
  autoStop: true,          // automatically stop after 5s
  filePath: 'trace.json',
});

// 3️⃣ The trace stops, populateCruxData runs, and the result contains CrUX data
// The response object can be inspected:
const summary = response.getAttachedTraceSummary();   // contains metadata.cruxFieldData
console.log(summary.metadata.cruxFieldData);

```

To disable CrUX fetching, pass the `--no-performance-crux` flag when launching MCP:

```bash
$ mcp --no-performance-crux

```

Or programmatically:

```typescript
const mcp = await McpContext.from(browser, logger, {
  performanceCrux: false,
  experimentalDevToolsDebugging: false
});

```

In both cases, trace parsing proceeds normally, but `populateCruxData` is never invoked, and no calls are made to the Google CrUX API.

## Key Source Files and Architecture

The CrUX integration spans several files within the `ChromeDevTools/chrome-devtools-mcp` repository:

| File | Purpose |
|------|---------|
| [`src/tools/performance.ts`](https://github.com/ChromeDevTools/chrome-devtools-mcp/blob/main/src/tools/performance.ts) | Core trace start/stop logic and `populateCruxData` implementation (lines 206-259) |
| [`src/McpContext.ts`](https://github.com/ChromeDevTools/chrome-devtools-mcp/blob/main/src/McpContext.ts) | Stores the `performanceCrux` option and exposes `isCruxEnabled()` (lines 72-77) |
| [`src/cli.ts`](https://github.com/ChromeDevTools/chrome-devtools-mcp/blob/main/src/cli.ts) | CLI flag `--no-performance-crux` definition and description (lines 303-308) |
| [`src/main.ts`](https://github.com/ChromeDevTools/chrome-devtools-mcp/blob/main/src/main.ts) | Startup banner mentioning CrUX integration (lines 133-135) |
| [`tests/tools/performance.test.ts`](https://github.com/ChromeDevTools/chrome-devtools-mcp/blob/main/tests/tools/performance.test.ts) | Test suite stubbing the CrUX endpoint and verifying data flow |

These files demonstrate how MCP bridges recorded performance traces with the Google CrUX API, enabling direct comparison between lab metrics and real-world field data.

## Summary

- **MCP enriches performance traces** with real-user data from the Google CrUX API when the `--performance-crux` flag is enabled (default).
- **The `populateCruxData` function** in [`src/tools/performance.ts`](https://github.com/ChromeDevTools/chrome-devtools-mcp/blob/main/src/tools/performance.ts) orchestrates the enrichment by configuring the DevTools `CrUXManager`, collecting unique URLs from trace insights, and fetching field data in parallel.
- **CrUX data is embedded** in `result.parsedTrace.metadata.cruxFieldData`, making Core Web Vitals and other field metrics available to downstream consumers alongside lab measurements.
- **Disabling the integration** via `--no-performance-crux` prevents all API calls while maintaining standard trace functionality.

## Frequently Asked Questions

### What is the Google CrUX API and why does MCP use it?

The Chrome User Experience Report (CrUX) API provides real-world performance metrics from actual Chrome users, including Core Web Vitals like Largest Contentful Paint (LCP) and Cumulative Layout Shift (CLS). MCP uses this API to enrich lab-based performance traces with field data, allowing developers to compare their local testing results against actual user experiences in the wild.

### How do I disable CrUX data fetching in MCP?

You can disable CrUX integration by passing the `--no-performance-crux` flag when launching the MCP server from the command line. Alternatively, when initializing `McpContext` programmatically, set the `performanceCrux` option to `false` in the configuration object. When disabled, MCP skips the `populateCruxData` call entirely, and no requests are made to the Google CrUX API.

### What URLs does MCP query against the CrUX API?

MCP collects all unique URLs that appear in the trace insights (accessed via `insight.values()`) plus the main frame URL from the trace metadata (`result.parsedTrace.Meta.mainFrameUrl`). These URLs are deduplicated using a JavaScript `Set` and then queried in parallel via the `CrUXManager.getFieldDataForPage(url)` method for each unique URL.

### Where is the CrUX field data stored in the trace result?

After fetching, the CrUX field data is stored in the `cruxFieldData` property of the trace metadata object, specifically at `result.parsedTrace.metadata.cruxFieldData`. This array contains the field data objects (or `null` values for URLs not found in CrUX) and is accessible to downstream consumers when generating trace summaries or insights via `response.attachTraceSummary` or `response.attachTraceInsight`.