completefrontend
Open lab My progress
THE WEB PERFORMANCE FIELD GUIDE

Performance work with AI agents

Let AI assistance and coding agents record and read traces, and check every claim they make against the trace. The Chrome DevTools MCP server, driven the way an agent drives it, on the course's store.

By the end, you can
  • 01
    Hand over the repetitive workLet AI assistance, or a coding agent with the Chrome DevTools MCP server, record traces and sum them up for you.
  • 02
    Check every claimTake an agent's diagnosis back to the trace, and compare several runs before you believe a difference.
  • 03
    Keep what's yoursSay what an agent can read through the browser, and keep real accounts and secrets out of its reach.

An agent with DevTools

Asha's message is still on your desk. The Leaf and Kettle page is slow on her phone, and the text jumps while she reads. You have two jobs left. First, find out what makes the text jump. Then try a fix, and prove that it worked. Both jobs mean recording the store again and again on a slow phone setting. Each recording is a trace: everything the page did, with its times. Then you have to read every one.

A friend says: "Let an AI agent do it." An AI agent is an AI model that can act, not only chat. It picks a tool, uses it, reads what comes back, and then picks the next step. Give it the right tools, and it can open Chrome, record a trace, and tell you what it found.

That raises three questions:

  • How does an agent use DevTools at all?
  • Can you trust what it tells you?
  • What can it see while it's in your browser?

This lesson answers them in that order. Along the way, you'll meet two kinds of helper:

  • AI assistance: a chat panel inside DevTools, powered by Gemini, Google's AI model.
  • A coding agent in your editor or terminal, connected to Chrome through the Chrome DevTools MCP server. That's a small program that hands the agent DevTools' tools.

A model's words change from one try to the next, so a lesson can't test them. The course did the next best thing. It ran the real server, chrome-devtools-mcp@1.12.0, and called its tools exactly the way an agent's app calls them. So every answer below is the text a model would read. Here is one whole session, on the store from Performance insights. Pick a step:

Playground: an agent's session
The callperformance_start_trace
{
  "pageId": 1,
  "reload": true,
  "autoStop": true,
  "ignoreCache": true,
  "filePath": "trace.json.gz"
}
What the model readsrecorded
## Summary of Performance trace findings:
URL: http://127.0.0.1:8080/
Trace bounds: {min: 1155324780727µs, max: 1155333384152µs}
CPU throttling: 4x
Network throttling: Slow 4G

# Available insight sets

The following is a list of insight sets. An insight set covers a specific part of the trace, split by navigations. The insights within each insight set are specific to that part of the trace. Be sure to consider the insight set id and bounds when calling functions. If no specific insight set or navigation is mentioned, assume the user is referring to the first one.

## insight set id: NAVIGATION_0

URL: http://127.0.0.1:8080/
Bounds: {min: 1155324783607µs, max: 1155333384152µs}
Metrics (lab / observed):
  - LCP: 2828 ms, event: (eventKey: r-8423, ts: 1155327611625), nodeId: 4
  - LCP breakdown:
    - TTFB: 704 ms, bounds: {min: 1155324783607µs, max: 1155325487328.9998µs}
    - Load delay: 30 ms, bounds: {min: 1155325487328.9998µs, max: 1155325517158µs}
    - Load duration: 1,734 ms, bounds: {min: 1155325517158µs, max: 1155327251030µs}
    - Render delay: 361 ms, bounds: {min: 1155327251030µs, max: 1155327611625µs}
  - CLS: 0.16, event: (eventKey: c-10068, ts: 1155328264837)
Metrics (field / real users): n/a – no data for this page in CrUX
Available insights:
  - insight name: DocumentLatency
    description: Your first network request is the most important. [Reduce its latency](https://developer.chrome.com/docs/performance/insights/document-latency) by avoiding redirects, ensuring a fast server response, and enabling text compression.
    relevant trace bounds: {min: 252µs, max: 1155325521694µs}
    estimated metric savings: FCP 603 ms, LCP 603 ms
    example question: How do I decrease the initial loading time of my page?
    example question: Did anything slow down the request for this document?
  - insight name: LCPBreakdown
    description: Each [subpart has specific improvement strategies](https://developer.chrome.com/docs/performance/insights/lcp-breakdown). Ideally, most of the LCP time should be spent on loading the resources, not within delays.
    relevant trace bounds: {min: 1155324783607µs, max: 1155327611625µs}
    example question: Help me optimize my LCP score
    example question: Which LCP subpart was most problematic?
    example question: What can I do to reduce the LCP time for this page load?
  - insight name: LCPDiscovery
    description: [Optimize LCP](https://developer.chrome.com/docs/performance/insights/lcp-discovery) by making the LCP image discoverable from the HTML immediately, and avoiding lazy-loading
    relevant trace bounds: {min: 1155325487328.9998µs, max: 1155327252394µs}
    example question: Suggest fixes to reduce my LCP
    example question: What can I do to reduce my LCP discovery time?
    example question: Why is LCP discovery time important?
  - insight name: CLSCulprits
    description: Layout shifts occur when elements move absent any user interaction. [Investigate the causes of layout shifts](https://developer.chrome.com/docs/performance/insights/cls-culprit), such as elements being added, removed, or their fonts changing as the page loads.
    relevant trace bounds: {min: 1155328264837µs, max: 1155330263108µs}
    example question: Help me optimize my CLS score
    example question: How can I prevent layout shifts on this page?
  - insight name: RenderBlocking
    description: Requests are blocking the page’s initial render, which may delay LCP. [Deferring or inlining](https://developer.chrome.com/docs/performance/insights/render-blocking) can move these network requests out of the critical path.
    relevant trace bounds: {min: 1155325515647µs, max: 1155327537967µs}
    estimated metric savings: FCP 1737 ms, LCP 0 ms
    example question: Show me the most impactful render-blocking requests that I should focus on
    example question: How can I reduce the number of render-blocking requests?
  - insight name: NetworkDependencyTree
    description: [Avoid chaining critical requests](https://developer.chrome.com/docs/performance/insights/network-dependency-tree) by reducing the length of chains, reducing the download size of resources, or deferring the download of unnecessary resources to improve page load
    relevant trace bounds: {min: 1155324784116µs, max: 1155328285894µs}
    example question: How do I optimize my network dependency tree?
  - insight name: ImageDelivery
    description: Reducing the download time of images can improve the perceived load time of the page and LCP. [Learn more about optimizing image size](https://developer.chrome.com/docs/performance/insights/image-delivery).
    relevant trace bounds: {min: 1155325517158µs, max: 1155327252394µs}
    estimated metric savings: FCP 0 ms, LCP 0 ms
    estimated wasted bytes: 97 kB
    example question: What should I do to improve and optimize the time taken to fetch and display images on the page?
    example question: Are all images on my site optimized?
  - insight name: FontDisplay
    description: Consider setting [`font-display`](https://developer.chrome.com/docs/performance/insights/font-display) to `swap` or `optional` to ensure text is consistently visible. `swap` can be further optimized to mitigate layout shifts with [font metric overrides](https://developer.chrome.com/blog/font-fallbacks).
    relevant trace bounds: {min: 1155327555314µs, max: 1155328285894µs}
    estimated metric savings: FCP 725 ms
    example question: How can I update my CSS to avoid layout shifts caused by incorrect `font-display` properties?
  - insight name: ThirdParties
    description: 3rd party code can significantly impact load performance. [Reduce and defer loading of 3rd party code](https://developer.chrome.com/docs/performance/insights/third-parties) to prioritize your page’s content.
    relevant trace bounds: {min: 1155325517087µs, max: 1155327935907µs}
    example question: Which third parties are having the largest impact on my page performance?
  - insight name: ForcedReflow
    description: A forced reflow occurs when JavaScript queries geometric properties (such as `offsetWidth`) after styles have been invalidated by a change to the DOM state. This can result in poor performance. Learn more about [forced reflows](https://developer.chrome.com/docs/performance/insights/forced-reflow) and possible mitigations.
    relevant trace bounds: {min: 1155328285942µs, max: 1155328370493µs}
    example question: How can I avoid forced reflows and layout thrashing?
    example question: What is forced reflow and why is it problematic?

Try it yourself
performance_start_trace

It records a reload. What comes back is text: LCP 2,828 ms, CLS 0.16, and the 10 insights it can ask about. The trace itself goes to a file.

Recorded with chrome-devtools-mcp@1.12.0, called the way an agent's app calls it. Each answer is the server's own text, from its findings on. Notes on the text's format are left out. The page's address and the trace's file name changed every run, so each is shown as one.

Look at step 4. The agent asked for a trace, and got back a page of text. It held two numbers you know: LCP, when the biggest picture or text appeared, and CLS, a score for how much the page jumped. It also listed insights, the checks DevTools runs over a trace, each looking for one kind of problem. The trace itself went to a file. Here is that loop as a picture:

An agent calls the Chrome DevTools MCP server's tools. The server drives Chrome and answers with text, such as a trace's summary, and can save the trace itself to a file. The server offered 32 tools.the agentMCP serverChrometool callstext answerstrace filewhat you check
The agent never touches Chrome itself. It sends tool calls and reads the text that comes back. The trace file stays on disk, where you can check what the text says.

Keep this picture in mind. The agent reads text about the trace. The trace file is where you check what the text says.

Real-life analogyA new assistant at the shop

Leaf and Kettle hires a new assistant. She's quick and keen, and happy to do the dull jobs, like timing every order all day. But she has never seen your kitchen. So you show her the camera footage, and you check her report before you change anything. And on her first day, you don't give her the keys to the safe.

In real life: The new assistant, quick and keen
In DevTools: An AI agent, or AI assistance
In real life: Timing every order, all day long
In DevTools: Recording trace after trace
In real life: Her written report
In DevTools: The text the model reads, and what it tells you
In real life: The camera footage you check it against
In DevTools: The trace file
In real life: The keys to the safe
In DevTools: Your signed-in browser, cookies and all
The main idea

Let an agent do the repetitive work: record, reload, record again. Treat what it tells you as a lead. Check each claim in the trace, and give it only what the job needs.

AI assistance in the Performance panel

The closest helper is already in DevTools. In Performance insights, you saw a Debug with AI button on each expanded insight. It opens the AI assistance panel, with that insight chosen as the topic. It's like asking a colleague who sits beside you and looks at the same screen.

DevTools' docs list what the panel can do. Three things matter most for performance work:

  • It picks its own context. The docs say it "autonomously narrows down and selects specific context", such as "performance trace events".
  • It can act. It "can perform autonomous actions such as run audits and record performance traces." Ask it "What performance issues exist on the page?", and it records a trace with your settings to answer.
  • It shows its working. Under each answer, Show agent walkthrough lists the code it ran and the data that came back, with links into DevTools.

Acting on its own and the walkthrough need Chrome 149 or later. The walkthrough is the part to use. It's the assistant's working notes: the code it ran, and the data it got back. Check the answer against that data, then click through to see it in DevTools.

If the panel wants to run code that could change the page, it pauses first. You read the code, then click Continue or Cancel.

You have to turn AI assistance on yourself. The docs say: "AI assistance is disabled by default." You turn it on in DevTools' Settings. You also need to be signed in to Chrome, and the docs add: "You need to be at least 18 years old and in one of the supported locations."

The docs are frank about its limits: "This feature is experimental and subject to future changes. It may generate inaccurate or offensive information that doesn't represent Google's views." The setup guide says it more plainly. The results "may be inaccurate or misleading, so always double check!"

Why it matters: AI assistance is the quickest second opinion on an insight. But when you start from an insight, the chat starts from what that insight says. If the insight guessed wrong, the chat begins from a wrong guess. You'll meet one later in this lesson.

The Chrome DevTools MCP server

AI assistance lives inside DevTools. But many developers work with a coding agent, in an editor or a terminal. That agent can't see your browser. The Chrome DevTools MCP server gives it a way in. Its README says it "lets your coding agent (such as Antigravity, Claude, Cursor or Copilot) control and inspect a live Chrome browser."

MCP stands for Model Context Protocol. It's a standard way for an AI app to use tools that someone else wrote. The agent's app is the client. The program that offers the tools is the server. A session goes like this:

  1. The client asks the server for its list of tools. Each tool has a name and a short description.
  2. The model reads the list and picks a tool. The client calls it, with the details it needs. Those details are called arguments.
  3. The server does the work in Chrome, and answers with text.
  4. The model reads the text and picks the next step.
Real-life analogyOrdering by phone from a menu

You phone a restaurant you've never visited. They read you the menu, with a line about each dish. You order one by name: "the soup, no onions". Later they phone back and describe what they cooked. You never see the kitchen. You only hear about it.

In real life: The menu, read out with a line about each dish
In DevTools: The list of tools, each with a description
In real life: Ordering one dish, "no onions"
In DevTools: Calling one tool, with its arguments
In real life: Hearing the dish described
In DevTools: The text answer the model reads
In real life: The dish itself, back in the kitchen
In DevTools: The trace file, saved on disk

This version of the server offered 32 tools. Some click and type, some open pages, some read network requests, take screenshots or run Lighthouse, and some record traces. Here is how it describes its trace tool, performance_start_trace, to the model: "Start a performance trace on the target webpage. Use to find frontend performance issues, Core Web Vitals (LCP, INP, CLS), and improve page load speed."

In step 4 of the playground, the agent called that tool on the store. Here is the first part of the answer, about the numbers:

What the model reads about the numbersText
## insight set id: NAVIGATION_0URL: http://127.0.0.1:8080/Metrics (lab / observed):  - LCP: 2828 ms, event: (eventKey: r-8423, ts: 1155327611625), nodeId: 4  - CLS: 0.16, event: (eventKey: c-10068, ts: 1155328264837)Metrics (field / real users): n/a – no data for this page in CrUX
  • Line 1 names this part of the trace: NAVIGATION_0, the first page load. The agent uses that name later, when it asks about one insight.
  • Line 2 is the page it loaded. Line 3 says the numbers below were measured in this recording, in the lab.
  • Line 4 is LCP: 2828 ms. Line 5 is CLS: 0.16. DevTools' own trace engine, the code behind the Performance panel, worked them out. The rest of each line names the event in the trace that the number came from.
  • Line 6 has no field data, the numbers from real visitors. The course turned off the server's lookups of that data, and the store isn't a public page anyway.

The rest of the answer lists what DevTools found:

The insights the agent can ask aboutText
Available insights:  - insight name: DocumentLatency  - insight name: LCPBreakdown  - insight name: LCPDiscovery  - insight name: CLSCulprits  - insight name: RenderBlocking  - insight name: NetworkDependencyTree  - insight name: ImageDelivery  - insight name: FontDisplay  - insight name: ThirdParties  - insight name: ForcedReflow
  • Line 1 starts the list. Lines 2 to 11 name ten insights: DevTools' checks, like the ones in Performance insights.
  • Each one is a lead the agent can follow. It asks about one with another tool, performance_analyze_insight. That's step 5 in the playground. Line 5, CLSCulprits, is the layout shift insight it asks about there.

Notice what isn't in the answer: the trace. The model hears the dish described, but never sees it. The server saved the trace to a file, because the call asked it to. That file is your evidence.

Why it matters: the model works from this text. If an insight guessed wrong, the text carries the wrong guess, and the model reads it as a finding.

Letting an agent record and compare traces

Now the fix. In Performance insights, the store's text jumped when a "Free delivery" promotion appeared above it, a second after the page was ready. One line of CSS keeps space for the promotion before it arrives: #news { min-height: 120px; }. It keeps at least 120 px open for the promotion's box, about as tall as the promotion is on a phone's screen.

Does it work? This is the kind of job an agent is good at: the same steps, many times, without getting bored. The course gave the agent two versions of the store. One was the store as it was. The other had that line of CSS added. Here is the first run of each:

One run eachPop out in the code editor (opens in a new tab)JavaScript
// The first run of each page, from the agent's summaries.const store = { cls: 0.16, lcpMs: 2846 };const reserved = { cls: 0.02, lcpMs: 2825 }; console.log(`CLS: ${store.cls} before the fix, ${reserved.cls} after it.`);console.log(`LCP: ${store.lcpMs} ms before the fix, ${reserved.lcpMs} ms after it.`);console.log(`LCP saved: ${store.lcpMs - reserved.lcpMs} ms.`);
  • Line 1 is a comment. Lines 2 and 3 hold CLS and LCP from each page's first summary.
  • Line 5 prints CLS: 0.16 before the fix, 0.02 after it.
  • Line 6 prints LCP: 2846 ms before the fix, 2825 ms after it.
  • Line 7 subtracts one LCP from the other and prints LCP saved: 21 ms.

An agent could report this as: "The fix removed the layout shift, and made LCP 21 ms faster." The first half sounds big. The second half sounds small, but welcome. Are both true?

Real-life analogyWeighing tea on a wobbly scale

Ravi weighs a bag of mint tea three times: 101 g, 99 g, 100 g. The scale wobbles by a gram or two. Then he weighs a new bag. If it comes to 100.5 g, he can't say it's heavier, because that's inside the wobble. If it comes to 80 g every time, the difference is real.

In real life: The scale's wobble, a gram or two
In DevTools: How much LCP moves between runs of the same page
In real life: Weighing each bag several times
In DevTools: Recording each page several times
In real life: 100.5 g against 100 g
In DevTools: LCP in the middle run: 2827 ms against 2828 ms
In real life: 80 g every time
In DevTools: CLS: 0.02 in every run, against 0.16

So the course had the agent record each page five times. The two pages took turns. Each run had a new server and a new browser, so nothing carried over from the run before:

Five runs each wayPop out in the code editor (opens in a new tab)JavaScript
const runs = {  store: { cls: [0.16, 0.16, 0.16, 0.16, 0.16], lcpMs: [2846, 2824, 2828, 2826, 2831] },  reserved: { cls: [0.02, 0.02, 0.02, 0.02, 0.02], lcpMs: [2825, 2817, 2835, 2827, 2842] },};const middle = (list) => [...list].sort((a, b) => a - b)[Math.floor(list.length / 2)]; for (const [page, { cls, lcpMs }] of Object.entries(runs)) {  const range = `${Math.min(...lcpMs)} to ${Math.max(...lcpMs)} ms`;  console.log(`${page}: CLS ${middle(cls)}, LCP ${middle(lcpMs)} ms, runs from ${range}`);}
  • Lines 2 and 3 hold CLS and LCP from all ten summaries, five for each page.
  • Line 5 makes middle. It sorts a list and takes the value in the middle, so one odd run can't move it. You met it in The Network panel for performance.
  • Line 8 finds the lowest and highest LCP. Line 9 prints one line per page.
  • It prints store: CLS 0.16, LCP 2828 ms, runs from 2824 to 2846 ms and reserved: CLS 0.02, LCP 2827 ms, runs from 2817 to 2842 ms.

CLS is the 80 g bag. It was 0.16 in all five runs before the fix, and 0.02 in all five after. That difference is real.

LCP is the 100.5 g bag. The middle runs differ by 1 ms. The runs before the fix spread over 22 ms, and the runs after it over 25 ms. The two spreads overlap. So the "21 ms faster" came from which two runs happened to be compared. Pick another pair, and the fix looks slower. Exercise 1 shows one.

That fits what you know about the store. The photo is the LCP, and it was painted long before the promotion arrived. So keeping space for the promotion gave the photo nothing to speed up.

Why it matters: ask the agent for several runs each way, taking turns, and compare the spreads. If they overlap, the difference is noise. It's the same rule as timing two routes to the shop on many mornings. Switch between the two metrics here:

Playground: one change, five runs each
CLS, run by runrecorded
The store, before the fix
0.16
0.16
0.16
0.16
0.16
After the fix: space kept for the promotion
0.02
0.02
0.02
0.02
0.02
Try it yourself
CLS

CLS in the middle run: 0.16 before the fix, 0.02 after it. Every run after the fix beats every run before it, so this difference is real.

Recorded through the MCP server. Each run had a new server and a new browser, and the two pages took turns. The numbers are the ones in each run's summary.

Checking a diagnosis against the evidence

The fix worked for CLS. But you skipped a step: you never checked what caused the jump. Suppose you'd asked the agent first: "What makes the text jump?" Through the server, that means asking about the layout shift insight, as in step 5 of the playground. Here is the part of the answer about the biggest shift:

What the model reads about the biggest shiftText
### Layout shift 3:- Impacted elements:  - MAIN - Start time: 4,482 ms- Score: 0.1364- Potential root causes:  - An unsized image (IMG id='hero') (url: http://127.0.0.1:8080/hero.jpg (eventKey: s-2468, ts: 1155325517158)).
  • Line 1 names the shift: the third in the worst group of shifts.
  • Lines 2 and 3 say which element moved: MAIN, the store's main content.
  • Line 5 is when it started: 4,482 ms. Line 6 is its score: 0.1364, the biggest of the three.
  • Lines 7 and 8 give a "potential root cause": the photo, an image with no width or height.

An agent that trusts this text would tell you, in one confident sentence: "The unsized photo caused the layout shift. Add a width and height to it." It even came from DevTools. Should you make that change?

Real-life analogyThe junior who blames the oven

Lunch at Leaf and Kettle was late. A new junior cook is sure why: "The oven is too slow." You don't buy a new oven. You ask for the evidence: the order tickets, each stamped with a time. They show every dish left the oven on time. The hold-up came from the order of the tasks. Just before the rush, someone pushed an extra order to the front.

In real life: "The oven is too slow"
In DevTools: "The unsized photo caused the layout shift"
In real life: The oven, big and always there
In DevTools: The photo, the biggest thing on the page
In real life: The order tickets, each with its time
In DevTools: The events in the trace file
In real life: The extra order pushed to the front
In DevTools: The promotion, put in above the photo by a timer

Your order tickets are the trace file the server saved. Ask it two questions. First: when was the photo painted?

When the photo was paintedPop out in the code editor (opens in a new tab)JavaScript
// From the agent's own trace file, in ms from the start of the load.const shiftAtMs = 4479.5;const photoPaintedMs = 2828; console.log(`The photo was painted ${(shiftAtMs - photoPaintedMs).toFixed(1)} ms before the shift.`);
  • Line 1 says where the times come from: the agent's own trace file, counted from the start of the load.
  • Line 2 is when the biggest shift happened. Line 3 is when the photo was painted. The photo was the LCP.
  • Line 5 prints The photo was painted 1651.5 ms before the shift.

So the photo had been still on the screen for more than a second and a half. Why would it move the page now? Second question: what ran just before the shift?

What ran just before the shiftPop out in the code editor (opens in a new tab)JavaScript
// From the same file: the last timer that fired before the shift.const shiftAtMs = 4479.5;const timerFiredMs = 4472.5; console.log(`The promotion's timer fired ${(shiftAtMs - timerFiredMs).toFixed(1)} ms before the shift.`);
  • Line 2 is the same shift as before.
  • Line 3 is when the last timer before the shift fired. The store sets only one timer: the promotion's, a second after the page is ready.
  • Line 5 prints The promotion's timer fired 7.0 ms before the shift.

You may have noticed two times for the same shift: 4,482 ms in the insight, 4,479.5 ms in the file. The insight counts from the moment the recording started. The course counted from the start of the load, which the summary puts 2.9 ms later. Here are the three moments in order:

From the agent's own trace: the photo was painted at 2828 ms, the promotion's timer fired at 4472.5 ms, and the biggest layout shift, 0.1364, came at 4479.5 ms. The insight blamed the photo.photo paintedpromotion's timerbiggest shift2,500 ms3,500 ms4,500 ms
The insight blamed the photo, which had been still on the screen for over a second and a half. The promotion's timer fired just before the shift.

The promotion caused the shift. It pushed the photo down, so the browser painted the photo again in its new place. Now look at the insight's rule, from DevTools' source: "An unsized image is considered a root cause if its PaintImage can be correlated to a layout shift." In plain words: an image with no size that's painted again right after a shift gets the blame. The rule saw the photo's repaint, and named the photo. It's the same rule that misled you in Performance insights, where the manager's camera noted Asha for stepping back to make room.

And the fix the trace pointed to, space for the promotion, is the one that took CLS from 0.16 to 0.02. A width and height on the photo are still worth adding: they keep the photo's space before it loads. But they don't keep space for the promotion, so they can't stop this shift.

Why it matters: an agent passes on a guess in the same sure voice as a fact. So ask it for the evidence, the way you'd ask the junior cook. Look at what happened just before the effect. Then test the fix with several runs.

Lab traces are one check. Your real visitors are another. A page can measure LCP and CLS for them with two kinds of performance entry. Here is where each one works:

What not to hand over

Last question: what can a helper see while it works? The two helpers in this lesson give very different answers. Picture it first:

Real-life analogyThe shop's keys, or a look through the window

Someone offers to count the queue at Leaf and Kettle. You could let them look through the window. They see what the window shows, and nothing else. Or you could hand them the keys. Then they can walk in, open the till and read the order book. Both can count the queue. Only one can read the till.

In real life: Looking through the window
In DevTools: AI assistance: DevTools chooses what to send
In real life: Handing over the keys
In DevTools: The MCP server: the agent drives the whole browser
In real life: The till and the order book
In DevTools: Cookies, logins and every page in that browser
In real life: A spare shop with play money
In DevTools: A temporary profile and test accounts

AI assistance looks through the window. DevTools' data usage page lists what it sends to Google for each kind of question. For a network request, it sends some of the headers. The page says these are always left out: custom headers, Cookie and Set-Cookie, and Authorization. It also warns that "human reviewers may read, annotate, and process input data", and asks you to keep sensitive and personal information out of your prompts.

The MCP server hands over the keys. Its README warns that it "exposes content of the browser instance to the MCP clients allowing them to inspect, debug, and modify any data in the browser or DevTools." Its tool reference says get_network_request is "Useful for inspecting request headers (including 'Cookie') and response headers (including 'Set-Cookie' and directives)."

The course tried it. It served a page whose server sets a made-up session cookie, the kind a site uses to keep you signed in. The cookie was HttpOnly. MDN says that flag "Forbids JavaScript from accessing the cookie", for example through document.cookie, and that it helps against cross-site scripting: someone else's script getting onto your page. Then the agent asked the server for the page's request:

What get_network_request answeredText
- cookie:session=test-only-5f2a9c- set-cookie:session=test-only-5f2a9c; Path=/; HttpOnly
  • Line 1 is the request's Cookie header: the browser sending the cookie back to the server.
  • Line 2 is the response's Set-Cookie header, HttpOnly and all.

The page's own JavaScript can't read that cookie. The agent read it in full, with one tool call. And whatever a tool answers goes to the model.

The same session cookie, session=test-only-5f2a9c, set HttpOnly. HttpOnly keeps it from the page's own JavaScript, through document.cookie. The agent asked the server's get_network_request tool, and got it back in full.the page's own JavaScriptdocument.cookiesession=test-only-5f2a9cHttpOnly: kept from scriptsthe agent, through the serverget_network_requestsession=test-only-5f2a9cread in full
HttpOnly keeps a cookie away from the page's scripts. It doesn't keep it from an agent that drives the browser.

Not every answer showed it. The trace's summary didn't include the cookie, and neither did the document latency insight. That insight even hid one header's value: it showed Keep-Alive: <redacted>. But hiding values in one answer doesn't hide them everywhere. The cookie was still one tool call away.

So which browser does the agent drive? That depends on how you start the server. A profile is the folder where Chrome keeps your cookies, logins and history. Three of the server's defaults matter:

  • It keeps its own profile. By default, the server keeps a profile in your home folder, from one session to the next. Sign in to a site there, and that login can still be there next time. With --isolated, it uses a temporary profile instead, "automatically cleaned up after the browser is closed."
  • It sends usage statistics. The README says: "Data collection is enabled by default." Google collects things like how often tools succeed and how long they take. --no-usage-statistics turns it off.
  • It looks up field data. "Performance tools may send trace URLs to the Google CrUX API", to fetch numbers from real visitors. It's like an assistant phoning head office with the address of every page they test. --no-performance-crux stops it.

The course ran the server with all three changed: --isolated, --no-usage-statistics and --no-performance-crux. It added --headless, which runs Chrome without a window.

The server can also connect to a Chrome that's already running, instead of starting its own. If that's your everyday Chrome, signed in to your email and your bank, you've handed over every key you have.

The protocol's own specification is careful here too. It says: "Tools represent arbitrary code execution and must be treated with appropriate caution." And: "Users should understand what each tool does before authorizing its use." So when your agent's app asks before a tool call, read the call before you allow it.

Here are the two helpers side by side. Read each row across: one question, then the answer for each helper.

A look through the window, or the keys
QuestionAI assistance in DevToolsAn agent with the MCP server
Where does it work?Inside your own DevTools, beside the page you're looking at.In a Chrome the server starts, or one you connect it to.
What can it reach?The topic you pick, and the details it asks DevTools for.Anything its tools can reach in that browser, network requests included.
Does it see cookies?Not in network requests. Its docs say the Cookie and Set-Cookie headers are always left out.Yes. get_network_request gave back the test cookie in full.
Where do its answers go?To Gemini, at Google. Human reviewers may read what's sent.To your agent's model, whoever runs it.
How do you limit it?It's off until you turn it on. An Enterprise policy can turn it off.Start the server with --isolated, and sign in only to test accounts.

Why it matters: whatever an agent can reach, it can pass on. Give it a profile of its own, test accounts and made-up data. Then everything it reads is something you'd be happy to share.

Trust it, check it, or keep it?

Each card is something you might give an agent, or something it might tell you. Ask three questions. Is it repetitive work that leaves a record you can open? Let it do it. Is it a claim about a cause or a change? Check it first. Is it something real the job doesn't need? Keep it from the agent.

Trust it, check it, or keep it?
  • Record a trace of the page on Slow 4G
  • Record each version of the page five times, taking turns
  • List the insights the trace found
  • "The unsized photo caused the layout shift"
  • "My change made LCP 21 ms faster"
  • Your everyday Chrome profile, signed in to work accounts
  • A page where you're signed in with a real account
Try it yourself
0 of 7 correct

Sort each card by how you'd handle it with an agent.

Choose a category for every card. You can change an answer at any time; Reset clears them all.

Common mix-ups

  • "It used DevTools, so its diagnosis is right." No. It read the same insights you can read, guesses included. The layout shift insight blamed the photo, and the promotion caused the shift.
  • "One trace before and one after shows the change." No. LCP moved more between runs of the same page than between the two pages' middle runs.
  • "The agent only sees the page." No. It drives the browser. It read an HttpOnly cookie that the page's own JavaScript can't.
  • "Redaction protects everything." No. The insight hid one header's value. Another tool, get_network_request, showed the cookie in full.
  • "Nothing leaves my machine." No. Tool answers go to the model. Usage statistics and CrUX lookups are on until you turn them off.
  • "AI assistance sends everything it sees." No. Its docs list what it sends. For network requests, they say Cookie, Set-Cookie and Authorization headers are always left out.

Practice exercises

Answer first, then use the hints.

Exercise 1 · Warm-upAnother pair of runs

This time the agent compares the fourth run of each page. What does the last line print?

Starter codePop out in the code editor (opens in a new tab)JavaScript
// The fourth run of each page, from the agent's summaries.
const store = { cls: 0.16, lcpMs: 2826 };
const reserved = { cls: 0.02, lcpMs: 2827 };

console.log(`CLS: ${store.cls} before the fix, ${reserved.cls} after it.`);
console.log(`LCP: ${store.lcpMs} ms before the fix, ${reserved.lcpMs} ms after it.`);
console.log(`LCP saved: ${store.lcpMs - reserved.lcpMs} ms.`);

Answer, then press Check. Spacing and letter case don’t matter.

    Exercise 2 · Warm-upAn earlier timer

    Suppose the promotion's timer had fired half a second earlier. What would the last line print?

    Starter codePop out in the code editor (opens in a new tab)JavaScript
    // From the same file: the last timer that fired before the shift.
    const shiftAtMs = 4479.5;
    const timerFiredMs = 3972.5;
    
    console.log(`The promotion's timer fired ${(shiftAtMs - timerFiredMs).toFixed(1)} ms before the shift.`);

    Answer, then press Check. Spacing and letter case don’t matter.

      Exercise 3 · PracticeThe performance tools

      In step 1 of the first playground, how many tools have names that start with performance_?

      Answer, then press Check. Spacing and letter case don’t matter.

        Exercise 4 · PracticeAnother suspect

        In step 5, the insight lists two potential root causes for layout shift 2. One is the unsized image. What is the other?

        Answer, then press Check. Spacing and letter case don’t matter.

          Exercise 5 · ChallengeYour own Chrome

          Which flag connects the server to a Chrome that's already running, at an address like http://127.0.0.1:9222?

          Answer, then press Check. Spacing and letter case don’t matter.

            Exercise 6 · ChallengeHide the headers

            Which flag makes the server hide some sensitive network headers before it answers?

            Answer, then press Check. Spacing and letter case don’t matter.

              Check your understanding

              For each question, ask three things. What did the agent read? What does the trace say? What could the agent reach?

              AI agents quiz · 7 questionsScore: first tries count
              1. Question 1 of 7What does the first line of the five-runs example print?

                Read the code, then predictPop out in the code editor (opens in a new tab)JavaScript
                const runs = {
                  store: { cls: [0.16, 0.16, 0.16, 0.16, 0.16], lcpMs: [2846, 2824, 2828, 2826, 2831] },
                  reserved: { cls: [0.02, 0.02, 0.02, 0.02, 0.02], lcpMs: [2825, 2817, 2835, 2827, 2842] },
                };
                const middle = (list) => [...list].sort((a, b) => a - b)[Math.floor(list.length / 2)];
                
                for (const [page, { cls, lcpMs }] of Object.entries(runs)) {
                  const range = `${Math.min(...lcpMs)} to ${Math.max(...lcpMs)} ms`;
                  console.log(`${page}: CLS ${middle(cls)}, LCP ${middle(lcpMs)} ms, runs from ${range}`);
                }

                Choose an answer to see the explanation.

              2. Question 2 of 7What does the timer check print?

                Read the code, then predictPop out in the code editor (opens in a new tab)JavaScript
                // From the same file: the last timer that fired before the shift.
                const shiftAtMs = 4479.5;
                const timerFiredMs = 4472.5;
                
                console.log(`The promotion's timer fired ${(shiftAtMs - timerFiredMs).toFixed(1)} ms before the shift.`);

                Choose an answer to see the explanation.

              3. Question 3 of 7An agent records a trace through the Chrome DevTools MCP server. What does its model read?

                Choose an answer to see the explanation.

              4. Question 4 of 7An agent says your change made LCP faster, from one trace before and one after. What do you do?

                Choose an answer to see the explanation.

              5. Question 5 of 7What can an agent read through the Chrome DevTools MCP server that the page's own JavaScript can't?

                Choose an answer to see the explanation.

              6. Question 6 of 7What does the server's --isolated flag do?

                Choose an answer to see the explanation.

              7. Question 7 of 7DevTools' AI assistance explains an insight. How do you treat the answer?

                Choose an answer to see the explanation.

              Key takeaways

              • An AI agent picks a tool, uses it, and reads what comes back. AI assistance does it inside DevTools.
              • A coding agent does it through the Chrome DevTools MCP server, whose answers are text for the model.
              • The model reads text about the trace, built from DevTools' insights. Their guesses become its guesses.
              • Check each claim in the trace file. Look at what happened just before the effect.
              • Compare several runs each way, taking turns. If the runs overlap, the difference is noise.
              • The MCP server hands over the browser. Cookies, HttpOnly ones too, are one tool call away.
              • Use --isolated and test accounts, and decide on usage statistics and CrUX lookups yourself.

              Remember the one-liner.
              Let the agent record; check what it claims; keep what it doesn't need.

              Sources

              The facts on this page come from these docs, specifications and source files:

              Coming next: How Lighthouse works, also in Measuring in the lab. Navigation, timespan and snapshot modes, and what Lighthouse does when it tests a page.

              CompleteFrontend Clear concepts. Working examples.