Ever wished your AI assistant could actually see and interact with a browser like you do? That's exactly what the Chrome DevTools MCP server delivers: a direct connection between your AI coding assistant and Chrome's powerful developer tools, enabling everything from automated testing to debugging and performance analysis.
Instead of asking your AI to generate Puppeteer scripts or guess at how your web app behaves, it can now control a real Chrome browser, inspect network requests, capture screenshots, and even record performance traces. The Model Context Protocol (MCP) creates this bridge, giving your AI assistant the same view into your web application that you'd get from Chrome DevTools.
But here's where it gets really interesting. Need to debug why a form submission isn't working? Your AI can take a snapshot of the page, examine the form elements, fill them out, and capture any console errors, all while explaining what's happening. Want to understand why your page is loading slowly? It can record a performance trace and break down the Core Web Vitals for you with actionable insights.
Common Workflows
Before we dive into the individual tools, let me show you how they work together to solve real problems you face every day. These workflows demonstrate the power of combining different tools to automate complex testing and debugging scenarios.
End-to-End Testing
Testing a complete user flow from start to finish:
"Test the checkout process with these steps: add a product to cart, proceed to checkout, fill in shipping details, and verify the order confirmation appears."
Your AI will:
- Take a snapshot to find the "Add to Cart" button (
take_snapshot
) - Click it and wait for the cart to update (
click
) - Navigate to checkout (
click
) - Take another snapshot to identify form fields (
take_snapshot
) - Fill in shipping details using realistic test data (
fill_form
) - Submit the form (
click
) - Wait for the confirmation message (
wait_for
) - Take a screenshot of the confirmation for your records (
take_screenshot
)
If anything fails, it'll capture console errors and network requests to help debug (list_console_messages
, list_network_requests
).
Performance Debugging
Understanding why a page loads slowly:
"Analyze the performance of example.com and tell me what's slowing it down."
Your AI will:
- Navigate to the URL (
navigate_page
) - Start a performance trace with page reload (
performance_start_trace
) - Wait for the trace to complete (
performance_stop_trace
) - Analyze Core Web Vitals (LCP, FID, CLS)
- Examine specific insights like ImageDelivery breakdown (
performance_analyze_insight
) - Identify the largest contributors (render-blocking resources, slow server response, large images)
- Suggest concrete optimizations with priority order
Responsive Design Testing
Verifying layouts work across different screen sizes:
"Test how the navigation menu behaves on mobile, tablet, and desktop."
Your AI will:
- Start at desktop size (1920x1080) (
resize_page
) - Take a snapshot and screenshot of the navigation (
take_snapshot
,take_screenshot
) - Resize to tablet (768x1024) (
resize_page
) - Check if the menu collapses to a hamburger icon (
take_snapshot
) - Test that the hamburger menu opens correctly (
click
) - Resize to mobile (375x667) (
resize_page
) - Verify mobile navigation works (
take_snapshot
,click
) - Report any layout issues or broken interactions
API Testing Through UI
Debugging network requests triggered by user interactions:
"Submit the search form and show me what API calls are made."
Your AI will:
- List network requests to establish a baseline (
list_network_requests
) - Take a snapshot to find the search input and button (
take_snapshot
) - Fill in the search term (
fill
) - Click the search button (
click
) - Wait for results to appear (
wait_for
) - List new network requests since the click (
list_network_requests
) - Show you the request details (URL, headers, payload, response) (
get_network_request
)
This is incredibly useful for debugging API integration issues without opening DevTools manually.
Available Tools
Now that you've seen what's possible, let's explore the individual tools. The Chrome DevTools MCP server organizes its 26 tools into six categories, each targeting a specific aspect of browser automation and debugging.
Input Automation (7 tools)
Tools for interacting with page elements:
-
click
- Click or double-click elements on the page -
drag
- Drag one element onto another -
fill
- Type text into inputs or select options from dropdowns -
fill_form
- Fill out multiple form fields at once -
handle_dialog
- Accept or dismiss browser dialogs (alerts, confirms, prompts) -
hover
- Hover over elements to trigger hover states -
upload_file
- Upload files through file input elements
Navigation Automation (7 tools)
Tools for controlling browser tabs and navigation:
-
close_page
- Close browser tabs by index -
list_pages
- Get a list of all open tabs -
navigate_page
- Navigate to a specific URL -
navigate_page_history
- Go back or forward in browser history -
new_page
- Open a new tab with a URL -
select_page
- Switch context to a different tab -
wait_for
- Wait for specific text to appear on the page
Emulation (3 tools)
Tools for simulating different device and network conditions:
-
emulate_cpu
- Throttle CPU to simulate slower devices (1-20x slowdown) -
emulate_network
- Simulate network conditions (Offline, Slow 3G, Fast 3G, Slow 4G, Fast 4G) -
resize_page
- Change viewport dimensions to test responsive designs
Performance (3 tools)
Tools for analyzing page performance:
-
performance_start_trace
- Begin recording a performance trace -
performance_stop_trace
- Stop the active trace recording -
performance_analyze_insight
- Get detailed information about specific performance insights (like LCP breakdown or document latency)
Network (2 tools)
Tools for inspecting network activity:
-
list_network_requests
- Get all network requests since the last navigation, with filtering options -
get_network_request
- Retrieve detailed information about a specific request by its ID
Debugging (4 tools)
Tools for inspecting and debugging pages:
-
evaluate_script
- Execute JavaScript code in the page context and get JSON-serializable results -
list_console_messages
- Retrieve all console logs, warnings, and errors -
take_screenshot
- Capture screenshots of the full page or specific elements (PNG, JPEG, or WebP) -
take_snapshot
- Get a text representation of the page based on the accessibility tree with unique element identifiers
Configuration
Let's get you up and running! The Chrome DevTools MCP server is pretty flexible, so you can configure it to match your workflow. I'll cover the essentials here, but if you want to dive deeper into all the configuration options, check out the official configuration documentation.
Basic Setup
The simplest way to get started is to let the server launch a fresh Chrome instance with default settings:
{
// or "mcpServers", depending your IDE / tool
"servers": {
"chrome-devtools": {
"command": "npx",
"args": ["-y", "chrome-devtools-mcp@latest"]
}
}
}
That's it! This configuration will work great for most use cases.
Connecting to a Running Chrome Instance from Docker
Here's a scenario I've run into a lot: you're developing in a Docker container but want to use a Chrome instance that's running on your host machine. Maybe you want to see the browser window, or you need to use your existing Chrome profile. Whatever the reason, connecting to a running instance is the way to go.
First, start Chrome on your host machine with remote debugging enabled. Chrome needs a user data directory when remote debugging is on, so make sure to specify one:
"C:\Program Files\Google\Chrome\Application\chrome.exe" ^
--remote-debugging-port=9222 ^
--user-data-dir="C:\Temp\chrome-profile-stable"
Now for the Docker-specific part. Docker provides a special DNS name called host.docker.internal
that resolves to your host machine. From inside your container, you can find the IP address:
host host.docker.internal
# Output: host.docker.internal has address 192.168.65.254
Use that IP address in your MCP server configuration:
{
// or "mcpServers", depending your IDE / tool
"servers": {
"chrome-devtools": {
"command": "npx",
"args": [
"-y",
"chrome-devtools-mcp@latest",
"--browserUrl=http://192.168.65.254:9222"
]
}
}
}
You might be wondering: "Why can't I just use host.docker.internal
directly in the browserUrl
?" If you're curious about the details, check out this discussion in the Puppeteer GitHub issues.
For more configuration options like headless mode, custom Chrome paths, network throttling, and debugging settings, take a look at the full configuration guide.
Tool Reference
Input Automation Tools
click
Clicks or double-clicks an element identified by its unique ID from a page snapshot.
Input Schema:
{
uid: string, // Required: The uid of an element on the page from the page content snapshot
dblClick?: boolean // Optional: Set to true for double clicks. Default is false.
}
In Action: Let's say you want to test a login form. First, take a snapshot to get element IDs, then use click to submit the form. Your AI assistant can chain these actions naturally when you ask "Can you test the login flow?" It'll find the form, fill it out, click submit, and report what happened.
drag
Drags one element onto another, useful for testing drag-and-drop interfaces.
Input Schema:
{
from_uid: string, // Required: The uid of the element to drag
to_uid: string // Required: The uid of the element to drop into
}
In Action: Testing a kanban board? Ask your AI to "move the task card from 'In Progress' to 'Done'" and it'll locate both elements from a snapshot and perform the drag operation. It can even verify the move succeeded by checking the DOM afterward.
fill
Types text into input fields, text areas, or selects an option from a dropdown.
Input Schema:
{
uid: string, // Required: The uid of an element on the page from the page content snapshot
value: string // Required: The value to fill in
}
In Action: When you ask "Fill out the contact form with test data," your AI can take a snapshot, identify all the form fields, then use fill on each one. It's smart enough to generate appropriate test data based on field names and types.
fill_form
Fills multiple form fields in a single operation, more efficient than calling fill repeatedly.
Input Schema:
{
elements: Array<{
uid: string; // The uid of the element to fill out
value: string; // Value for the element
}>;
}
In Action: This is perfect for complex forms. Instead of watching your AI fill fields one by one, it batches everything together. Ask "Complete the registration form" and it'll snapshot the page, identify all required fields, then fill them all at once.
handle_dialog
Responds to JavaScript alerts, confirms, or prompts.
Input Schema:
{
action: 'accept' | 'dismiss', // Required: Whether to dismiss or accept the dialog
promptText?: string // Optional: Optional prompt text to enter into the dialog.
}
In Action: Testing a "delete item" flow that shows a confirmation dialog? Your AI will click the delete button, detect the dialog, then use handle_dialog
to confirm or cancel. You can specify which action you want: "Test the delete flow but cancel the confirmation."
hover
Triggers hover states on elements, essential for testing dropdown menus and tooltips.
Input Schema:
{
uid: string; // Required: The uid of an element on the page from the page content snapshot
}
In Action: Want to test if your navigation dropdown works? Ask "Check if the Products menu shows all categories" and your AI will hover over the menu, wait for it to expand, then snapshot and verify the contents.
upload_file
Uploads a file through a file input element.
Input Schema:
{
uid: string, // Required: The uid of the file input element or an element that will open file chooser on the page from the page content snapshot
filePath: string // Required: The local path of the file to upload
}
In Action: Need to test an image upload feature? Tell your AI "Upload test-image.jpg to the profile picture form" and it'll locate the file input, handle the upload, and can even wait to verify the upload succeeded.
Navigation Automation Tools
close_page
Closes a browser tab by its index. You can't close the last remaining tab.
Input Schema:
{
pageIdx: number; // Required: The index of the page to close. Call list_pages to list pages.
}
In Action: After opening multiple tabs for testing, clean them up: "Close all the test tabs we opened." Your AI will list the pages, then close the ones that aren't needed, keeping your main tab active.
list_pages
Returns all open browser tabs with their URLs and indices.
Input Schema: None
In Action: This runs automatically whenever your AI needs to know what tabs are open. When you ask "What pages do I have open?" or "Switch to the documentation tab" it uses this tool behind the scenes.
navigate_page
Navigates the current tab to a URL.
Input Schema:
{
url: string, // Required: URL to navigate the page to
timeout?: number // Optional: Maximum wait time in milliseconds. If set to 0, the default timeout will be used.
}
In Action: The most straightforward tool. "Go to https://example.com" triggers this directly. Your AI can also chain it with other actions: "Navigate to the login page and check if the form is accessible."
navigate_page_history
Moves backward or forward through the browser history.
Input Schema:
{
navigate: 'back' | 'forward', // Required: Whether to navigate back or navigate forward in the selected pages history
timeout?: number // Optional: Maximum wait time in milliseconds. If set to 0, the default timeout will be used.
}
In Action: Testing multi-step flows? "Complete the checkout process, then go back and verify the cart is still correct." Your AI navigates forward through each step, then uses history navigation to go back and verify state.
new_page
Opens a new tab and navigates to a URL.
Input Schema:
{
url: string, // Required: URL to load in a new page.
timeout?: number // Optional: Maximum wait time in milliseconds. If set to 0, the default timeout will be used.
}
In Action: Comparing pages side-by-side? "Open the production and staging sites in separate tabs and compare the homepage layouts." Your AI opens both pages and can switch between them to analyze differences.
select_page
Changes which tab is active for subsequent tool operations.
Input Schema:
{
pageIdx: number; // Required: The index of the page to select. Call list_pages to list pages.
}
In Action: When working with multiple tabs, this switches context. Ask "Check the console errors in the second tab" and your AI uses list_pages to find it, select_page to switch to it, then list_console_messages to check for errors.
wait_for
Waits for specific text to appear on the page, useful after triggering actions that update the UI.
Input Schema:
{
text: string, // Required: Text to appear on the page
timeout?: number // Optional: Maximum wait time in milliseconds. If set to 0, the default timeout will be used.
}
In Action: After submitting a form, you want to verify success: "Submit the form and check for the success message." Your AI clicks submit, then uses wait_for to ensure "Thank you for your submission" appears before continuing.
Emulation Tools
emulate_cpu
Throttles CPU performance to simulate slower devices.
Input Schema:
{
throttlingRate: number; // Required: The CPU throttling rate representing the slowdown factor 1-20x. Set the rate to 1 to disable throttling
}
In Action: Want to see how your app performs on older devices? "Test the page performance on a slow device" triggers CPU throttling, helping identify performance bottlenecks that only appear on less powerful hardware.
emulate_network
Simulates various network conditions.
Input Schema:
{
throttlingOption: "No emulation" |
"Offline" |
"Slow 3G" |
"Fast 3G" |
"Slow 4G" |
"Fast 4G"; // Required: The network throttling option to emulate. Available throttling options are: No emulation, Offline, Slow 3G, Fast 3G, Slow 4G, Fast 4G. Set to "No emulation" to disable. Set to "Offline" to simulate offline network conditions.
}
In Action: Testing offline functionality? "Check if the app works without internet" sets the network to offline mode, then you can ask your AI to verify that your service worker and caching work correctly. You can also test on slow connections: "How does the page load on 3G?"
resize_page
Changes the viewport dimensions to test responsive designs.
Input Schema:
{
width: number, // Required: Page width
height: number // Required: Page height
}
In Action: Checking mobile responsiveness? "Test the layout on iPhone 12 dimensions" resizes to 390x844, takes screenshots, and you can explicitly ask it to check that the mobile menu works correctly. Your AI can test multiple sizes in sequence to catch responsive design issues.
Performance Tools
performance_start_trace
Begins recording a performance trace to analyze page load and runtime performance.
Input Schema:
{
reload: boolean, // Required: Determines if, once tracing has started, the page should be automatically reloaded.
autoStop: boolean // Required: Determines if the trace recording should be automatically stopped.
}
In Action: "Analyze the performance of this page" starts a trace with reload enabled and autoStop true. After recording, your AI gets Core Web Vitals (LCP, FID, CLS) and performance insights automatically.
performance_stop_trace
Stops the active performance trace and returns the analysis.
Input Schema: None
In Action: If you started a trace with autoStop disabled (to capture user interactions), you'd stop it manually: "Stop the performance recording and show me the results." The trace includes detailed timing information and performance insights.
performance_analyze_insight
Gets detailed information about specific performance issues identified in a trace.
Input Schema:
{
insightName: string; // Required: The name of the Insight you want more information on. For example: "DocumentLatency" or "LCPBreakdown"
}
Here's the list of all 18 Chrome DevTools Performance Insights and their names:
Each of these insights focuses on a specific aspect of performance that can help you make your pages faster. When you run a performance trace, Chrome analyzes your page and highlights which of these insights are relevant to your situation:
-
Cache
: Use efficient cache lifetimes -
CLSCulprits
: Layout shift culprits -
DocumentLatency
: Document request latency -
DOMSize
: Optimize DOM size -
DuplicatedJavaScript
: Duplicated JavaScript -
FontDisplay
: Font display -
ForcedReflow
: Forced reflow -
ImageDelivery
: Improve image delivery -
INPBreakdown
: INP breakdown -
LCPBreakdown
: LCP breakdown -
LCPDiscovery
: LCP request discovery -
LegacyJavaScript
: Legacy JavaScript -
ModernHTTP
: Modern HTTP -
NetworkDependencyTree
: Network dependency tree -
RenderBlocking
: Render-blocking requests -
SlowCSSSelector
: CSS selector costs -
ThirdParties
: Third parties -
Viewport
: Optimize viewport for mobile
Want to understand how Chrome generates these insights and what each one means for your page? The Performance Insights documentation has all the details.
In Action: After running a trace that shows "LCP could be improved," ask "What's causing the slow LCP?" Your AI uses this tool to get a breakdown of what contributed to the Largest Contentful Paint time and suggests specific optimizations.
Network Tools
list_network_requests
Retrieves all network requests since the page loaded.
Input Schema:
{
pageSize?: number, // Optional: Maximum number of requests to return. When omitted, returns all requests.
pageIdx?: number, // Optional: Page number to return (0-based). When omitted, returns the first page.
resourceTypes?: string[] // Optional: Filter requests to only return requests of the specified resource types. When omitted or empty, returns all requests.
}
In Action: Debugging API calls? "Show me all the failed network requests" lists requests, filters to status codes 4xx and 5xx, and shows you what went wrong. You can also check "Are there any requests taking longer than 2 seconds?"
get_network_request
Gets a network request by URL. You can get all requests by calling list_network_requests.
Input Schema:
{
reqid: number; // Required: The reqid of a request on the page from the listed network requests
}
In Action: After listing requests, dive into details: "What headers were sent with that POST request?" Your AI uses the request ID to fetch complete information including headers, response body, timing breakdown, and status.
Debugging Tools
evaluate_script
Executes JavaScript code in the page context and returns JSON-serializable results.
Input Schema:
{
function: string, // Required: A JavaScript function to run in the currently selected page
args?: Array<{ // Optional: An optional list of arguments to pass to the function.
uid: string // The uid of an element on the page from the page content snapshot
}>
}
In Action: Need to inspect page state? "What's the current value of the search input?" triggers evaluation of document.querySelector('input[name="search"]').value
. You can also modify state, check global variables, or debug complex interactions.
list_console_messages
Retrieves console logs, warnings, and errors from the page.
Input Schema:
{
pageSize?: number, // Optional: Maximum number of messages to return. When omitted, returns all requests.
pageIdx?: number, // Optional: Page number to return (0-based). When omitted, returns the first page.
types?: string[] // Optional: Filter messages to only return messages of the specified resource types. When omitted or empty, returns all messages.
}
In Action: After page interactions, check what happened: "Did any errors occur?" Your AI lists console messages, filters to errors, and explains what each one means. Great for catching JavaScript errors during testing.
take_screenshot
Captures visual snapshots of the page or specific elements.
Input Schema:
{
format?: 'png' | 'jpeg' | 'webp', // Optional: Type of format to save the screenshot as. Default is "png"
quality?: number, // Optional: Compression quality for JPEG and WebP formats (0-100). Higher values mean better quality but larger file sizes. Ignored for PNG format.
uid?: string, // Optional: The uid of an element on the page from the page content snapshot. If omitted takes a pages screenshot.
fullPage?: boolean, // Optional: If set to true takes a screenshot of the full page instead of the currently visible viewport. Incompatible with uid.
filePath?: string // Optional: The absolute path, or a path relative to the current working directory, to save the screenshot to instead of attaching it to the response.
}
In Action: Visual regression testing made easy: "Take a screenshot of the homepage and compare it to yesterday's version." Your AI captures the image, compares layouts, and highlights any differences. You can also screenshot specific components: "Show me what the modal looks like when open."
take_snapshot
Generates a text representation of the page based on the accessibility tree, with unique IDs for each element. Think of this as getting a bird's eye view of your page's structure: it shows you all the interactive elements, their roles, and how they're organized, similar to how a screen reader would understand the page.
The accessibility tree is a simplified version of the DOM that exposes only the information needed for assistive technologies. By using this tree, the snapshot focuses on semantically meaningful elements while filtering out purely visual markup. This makes it perfect for understanding page structure and finding elements to interact with.
For a deeper dive into how Chrome's accessibility tree works and what information it provides, check out the Full Accessibility Tree in Chrome DevTools blog post.
Input Schema:
{
verbose?: boolean // Optional: Whether to include all possible information available in the full a11y tree. Default is false.
}
In Action: This is your AI's eyes into the page structure. When you ask "What form fields are on this page?" it takes a snapshot to see all interactive elements. The unique IDs from this snapshot are what other tools use to interact with specific elements.
👨💻About the author
My name is Gergely Szerovay, I worked as a data scientist and full-stack developer for many years, and I have been working as frontend tech lead, focusing on Angular based frontend development. As part of my role, I'm constantly following how Angular and the frontend development scene in general is evolving. To share my knowledge, I started the Angular Addicts monthly newsletter and publication in 2022, so that I can send you the best resources I come across each month. Whether you are a seasoned Angular Addict or a beginner, I got you covered. Let me know if you would like to be included as a writer. Let's learn Angular together! Subscribe here 🔥
Angular has evolved very rapidly over the past few years, and in the past year, with the rise of generative AI, our software development workflows have also evolved rapidly. In order to closely follow the evolution of AI-assisted software development, I decided to start building AI tools in public, and publish my progress on AIBoosted.dev. Join my on this learning journey: Subscribe here 🚀
Follow me on Substack (Angular Addicts), Substack (AIBoosted.dev), Medium, Dev.to, Twitter or LinkedIn to learn more about Angular, and how to build AI apps with AI, Typescript, React and Angular!
Top comments (1)
If you want AI‑native vibes, a11y‑tree targeting with take_snapshot, and DevTools perf juice, MCP is your friend. Getting started is dead simple - wire your IDE/tool to run npx -y chrome-devtools-mcp@latest and it spins up a fresh Chrome profile-no --browserUrl unless you’re attaching to an existing instance. Snapshots use the accessibility tree roles/names for stable IDs, which is super robust, but they’ll skip purely visual bits or cross‑origin stuff.