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

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 (lines 72-77), the context initializes this setting:

// 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.

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

// 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 (lines 221-234), the setup proceeds as follows:

// 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 (lines 235-241), these URLs are deduplicated using a Set:

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:

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, the data is assigned:

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:

// 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:

$ mcp --no-performance-crux

Or programmatically:

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 Core trace start/stop logic and populateCruxData implementation (lines 206-259)
src/McpContext.ts Stores the performanceCrux option and exposes isCruxEnabled() (lines 72-77)
src/cli.ts CLI flag --no-performance-crux definition and description (lines 303-308)
src/main.ts Startup banner mentioning CrUX integration (lines 133-135)
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 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.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →