chrome-devtools-mcp
Original:🇺🇸 English
Translated
Browser automation and testing using chrome-devtools MCP server. Use when automating web browsers, taking screenshots, inspecting console logs, monitoring network requests, testing responsive layouts, collecting performance metrics, or debugging web applications. Critical for visual testing workflows and browser-based automation tasks.
8installs
Sourcegsmlg-dev/code-agent
Added on
NPX Install
npx skill4agent add gsmlg-dev/code-agent chrome-devtools-mcpTags
Translated version includes tags in frontmatterSKILL.md Content
View Translation Comparison →Core Concepts
Browser lifecycle: Browser starts automatically on first tool call using a persistent Chrome profile. Configure via CLI args in the MCP server configuration: .
npx chrome-devtools-mcp@latest --helpPage selection: Tools operate on the currently selected page. Use to see available pages, then to switch context.
list_pagesselect_pageElement interaction: Use to get page structure with element s. Each element has a unique for interaction. If an element isn't found, take a fresh snapshot - the element may have been removed or the page changed.
take_snapshotuiduidWorkflow Patterns
Before interacting with a page
- Navigate: or
navigate_pagenew_page - Wait: to ensure content is loaded if you know what you look for.
wait_for - Snapshot: to understand page structure
take_snapshot - Interact: Use element s from snapshot for
uid,click, etc.fill
Efficient data retrieval
- Use parameter for large outputs (screenshots, snapshots, traces)
filePath - Use pagination (,
pageIdx) and filtering (pageSize) to minimize datatypes - Set on input actions unless you need updated page state
includeSnapshot: false
Tool selection
- Automation/interaction: (text-based, faster, better for automation)
take_snapshot - Visual inspection: (when user needs to see visual state)
take_screenshot - Additional details: for data not in accessibility tree
evaluate_script
Parallel execution
You can send multiple tool calls in parallel, but maintain correct order: navigate → wait → snapshot → interact.
Troubleshooting
If is insufficient, guide users to use Chrome DevTools UI:
chrome-devtools-mcpIf there are errors launching or Chrome, refer to https://github.com/ChromeDevTools/chrome-devtools-mcp/blob/main/docs/troubleshooting.md.
chrome-devtools-mcpChrome DevTools MCP Usage
Critical Constraint: Screenshot Size Limit
API submissions have a hard limit of 8000px on any dimension. Full-page screenshots frequently exceed this.
Solution: Always use viewport-only screenshots by default
javascript
// ✓ CORRECT: Viewport-only (default)
take_screenshot({ fullPage: false })
// ✓ CORRECT: Save to filesystem for large pages
take_screenshot({ fullPage: false, path: './screenshots/page.png' })
// ✗ AVOID: Full-page without size check
take_screenshot({ fullPage: true })When full-page screenshots are needed
Check page height first:
javascript
const height = execute_script('return document.documentElement.scrollHeight')
if (height > 8000) {
take_screenshot({ fullPage: false, path: './screenshot.png' })
} else {
take_screenshot({ fullPage: true })
}Alternative: Capture multiple viewport screenshots by scrolling:
javascript
take_screenshot({ fullPage: false, path: './top.png' })
execute_script('window.scrollBy(0, window.innerHeight)')
take_screenshot({ fullPage: false, path: './middle.png' })Navigation and Timing
Always wait after navigation to allow JS execution and rendering:
javascript
navigate('https://example.com')
wait(1000) // Minimum recommended wait
// For dynamic apps, wait for specific elements
navigate('https://example.com')
execute_script(`
return new Promise(resolve => {
const check = () => {
if (document.querySelector('#app')) resolve();
else setTimeout(check, 100);
};
check();
});
`)Error Detection Patterns
Console Errors
javascript
const errors = execute_script(`
return performance.getEntriesByType('navigation')[0].type === 'reload'
? []
: (window.__consoleErrors || []);
`)Network Failures
javascript
const failed = execute_script(`
return performance.getEntriesByType('resource')
.filter(r => r.transferSize === 0 && r.duration > 0)
.map(r => ({ url: r.name, duration: r.duration }));
`)Layout Issues
javascript
const issues = execute_script(`
const issues = [];
// Horizontal overflow
if (document.documentElement.scrollWidth > window.innerWidth) {
issues.push({ type: 'horizontal-overflow', width: document.documentElement.scrollWidth });
}
// Elements outside viewport
document.querySelectorAll('*').forEach(el => {
const rect = el.getBoundingClientRect();
if (rect.right > window.innerWidth) {
issues.push({ type: 'overflow-right', element: el.tagName });
}
});
return issues;
`)Responsive Testing
Test multiple viewports efficiently:
javascript
const viewports = [
{ width: 375, height: 667, name: 'mobile' },
{ width: 768, height: 1024, name: 'tablet' },
{ width: 1440, height: 900, name: 'desktop' }
];
for (const vp of viewports) {
set_viewport(vp.width, vp.height);
wait(500); // Allow reflow
take_screenshot({
fullPage: false,
path: `./screenshots/${vp.name}.png`
});
}Component State Testing
Test interactive states without user interaction:
javascript
// Hover state
execute_script(`
const el = document.querySelector('#button');
el.dispatchEvent(new MouseEvent('mouseenter', { bubbles: true }));
`);
take_screenshot({ fullPage: false, path: './hover.png' });
// Focus state
execute_script(`
document.querySelector('#input').focus();
`);
take_screenshot({ fullPage: false, path: './focus.png' });
// Active/pressed state
execute_script(`
const el = document.querySelector('#button');
el.dispatchEvent(new MouseEvent('mousedown', { bubbles: true }));
`);
take_screenshot({ fullPage: false, path: './active.png' });Performance Metrics
Collect web vitals and performance data:
javascript
const metrics = execute_script(`
return {
fcp: performance.getEntriesByName('first-contentful-paint')[0]?.startTime,
lcp: performance.getEntriesByType('largest-contentful-paint')[0]?.startTime,
cls: performance.getEntriesByType('layout-shift')
.reduce((sum, entry) => sum + (entry.hadRecentInput ? 0 : entry.value), 0),
resources: performance.getEntriesByType('resource').length,
totalSize: performance.getEntriesByType('resource')
.reduce((sum, r) => sum + r.transferSize, 0)
};
`);Accessibility Checks
Basic accessibility validation:
javascript
const a11y = execute_script(`
const issues = [];
// Missing alt text
document.querySelectorAll('img:not([alt])').forEach(img => {
issues.push({ type: 'missing-alt', src: img.src });
});
// Missing form labels
document.querySelectorAll('input:not([aria-label]):not([id])').forEach(input => {
if (!input.closest('label')) {
issues.push({ type: 'missing-label', name: input.name });
}
});
// Empty links
document.querySelectorAll('a').forEach(link => {
if (!link.textContent.trim() && !link.getAttribute('aria-label')) {
issues.push({ type: 'empty-link', href: link.href });
}
});
return issues;
`);Standard Audit Workflow
Use this pattern for systematic page audits:
javascript
function audit_page(url, name) {
// Navigate
navigate(url);
wait(1000);
// Screenshot (viewport only to avoid size errors)
const screenshot_path = `./screenshots/${name}.png`;
take_screenshot({ fullPage: false, path: screenshot_path });
// Page metrics
const height = execute_script('return document.documentElement.scrollHeight');
const viewport = execute_script('return window.innerHeight');
// Error detection
const console_errors = execute_script('return window.__errors || []');
const network_failures = execute_script(`
return performance.getEntriesByType('resource')
.filter(r => r.transferSize === 0 && r.duration > 0)
.map(r => r.name);
`);
const layout_issues = execute_script(`
return document.documentElement.scrollWidth > window.innerWidth
? ['horizontal-overflow']
: [];
`);
return {
name,
url,
screenshot_path,
page_height: height,
viewport_height: viewport,
needs_scroll: height > viewport,
console_errors,
network_failures,
layout_issues,
has_issues: console_errors.length > 0 ||
network_failures.length > 0 ||
layout_issues.length > 0
};
}Error Handling
Screenshot Dimension Error
If you encounter dimension errors despite using , the page may be using unusual viewport settings. Save to file instead:
fullPage: falsejavascript
try {
take_screenshot({ fullPage: false });
} catch (error) {
if (error.includes('8000 pixels')) {
take_screenshot({ fullPage: false, path: './fallback.png' });
}
}Navigation Timeout
javascript
try {
navigate(url);
wait(5000);
} catch (error) {
// Log failure and continue
console.log(`Failed to load: ${url}`);
}Missing Elements
javascript
const exists = execute_script(`return !!document.querySelector('#target')`);
if (!exists) {
take_screenshot({ fullPage: false, path: './missing-element.png' });
// Handle accordingly
}Key Reminders
- Always default to to avoid dimension errors
fullPage: false - Save screenshots to filesystem when possible instead of API submission
- Wait after navigation to allow JS execution (minimum 1000ms)
- Use execute_script for DOM queries instead of brittle selector-based approaches
- Test multiple viewports for responsive applications
- Capture evidence early - take screenshots before elements change state
- Check page dimensions before deciding on screenshot strategy