Browser DevTools MCP VSCode Extension
Skill by ara.so — Devtools Skills collection.
Overview
Browser DevTools MCP for VS Code & Cursor is a Playwright-powered browser automation extension that integrates the browser-devtools-mcp server into your IDE via the Model Context Protocol (MCP). It enables AI assistants like GitHub Copilot and Cursor AI to interact with real web browsers for testing, debugging, and automation tasks.
Key capabilities:
- Browser automation (navigation, clicks, form filling)
- Screenshots and visual testing
- Accessibility audits and ARIA tree inspection
- Core Web Vitals measurement (LCP, INP, CLS, TTFB, FCP)
- Network inspection and request mocking
- React DevTools integration
- OpenTelemetry distributed tracing
- Figma design comparison
- Non-blocking debugging (tracepoints, logpoints, watch expressions)
- Batch execution via JavaScript
Installation
From Open VSX Registry
# VS Code
code --install-extension serkan-ozal.browser-devtools-mcp-vscode
# Cursor
cursor --install-extension serkan-ozal.browser-devtools-mcp-vscode
From VSIX File
# VS Code
code --install-extension browser-devtools-mcp-vscode-x.x.x.vsix
# Cursor
cursor --install-extension browser-devtools-mcp-vscode-x.x.x.vsix
First-Time Setup
On first activation, the extension downloads Playwright browsers (default: Chromium). To customize:
- Open Command Palette (
Ctrl+Shift+P/Cmd+Shift+P) - Run: Browser DevTools MCP: Install Playwright Browsers...
- Select browsers: Chromium (default), Firefox, and/or WebKit
Alternatively, configure in Settings:
browserDevtoolsMcp.install.chromium(default:true)browserDevtoolsMcp.install.firefox(default:false)browserDevtoolsMcp.install.webkit(default:false)
Configuration
Quick Settings Panel
Open the Browser DevTools MCP panel in the Explorer sidebar for common settings.
Full Settings
Open VS Code Settings (Ctrl+, / Cmd+,) and search for "Browser DevTools MCP" or run:
Browser DevTools MCP: Open Settings
Key Settings
General
{
"browserDevtoolsMcp.enable": true,
"browserDevtoolsMcp.telemetry.enable": true,
"browserDevtoolsMcp.platform": "browser" // "browser" or "node"
}
Browser Configuration
{
"browserDevtoolsMcp.browser.headless": true,
"browserDevtoolsMcp.browser.persistent": false,
"browserDevtoolsMcp.browser.userDataDir": "",
"browserDevtoolsMcp.browser.useSystemBrowser": false,
"browserDevtoolsMcp.browser.executablePath": "",
"browserDevtoolsMcp.browser.locale": "en-US"
}
CDP (Chrome DevTools Protocol) Mode
{
"browserDevtoolsMcp.browser.cdp.enable": false,
"browserDevtoolsMcp.browser.cdp.endpointUrl": "" // e.g., "http://localhost:9222"
}
Network & Security
{
"browserDevtoolsMcp.network.proxy": "", // e.g., "http://proxy.example.com:8080"
"browserDevtoolsMcp.network.bypassProxy": "",
"browserDevtoolsMcp.network.ignoreHTTPSErrors": false
}
Debugging
{
"browserDevtoolsMcp.debug.enable": false,
"browserDevtoolsMcp.debug.screenshots": false,
"browserDevtoolsMcp.debug.traces": false,
"browserDevtoolsMcp.debug.videos": false,
"browserDevtoolsMcp.debug.outputDir": ""
}
OpenTelemetry
{
"browserDevtoolsMcp.otel.enable": false,
"browserDevtoolsMcp.otel.exporterUrl": "", // e.g., "http://localhost:4318/v1/traces"
"browserDevtoolsMcp.otel.serviceName": "browser-devtools-mcp",
"browserDevtoolsMcp.otel.propagationHeaderName": "traceparent"
}
Using with AI Assistants
In Cursor
The extension automatically registers the MCP server via Cursor's native MCP API. AI assistants can directly invoke browser automation tools.
Example prompts:
"Navigate to example.com and take a screenshot"
"Run accessibility audit on the current page"
"Measure Core Web Vitals for https://example.com"
"Click the submit button and capture network requests"
"Mock the /api/users endpoint to return test data"
In VS Code (1.96+)
The extension registers via vscode.lm.registerMcpServerDefinitionProvider. GitHub Copilot can use the MCP tools.
MCP Tools Reference
Navigation & Interaction
navigate
// Navigate to URL
{
"url": "https://example.com",
"waitUntil": "networkidle" // "load" | "domcontentloaded" | "networkidle"
}
click
// Click element by selector
{
"selector": "button.submit",
"timeout": 30000
}
fill
// Fill form input
{
"selector": "input[name='email']",
"value": "test@example.com"
}
type
// Type text with keyboard simulation
{
"selector": "textarea",
"text": "Hello world",
"delay": 100 // ms between keystrokes
}
select
// Select dropdown option
{
"selector": "select[name='country']",
"values": ["US"]
}
Inspection & Testing
screenshot
// Capture screenshot
{
"fullPage": true,
"selector": null, // or specific element selector
"path": "/tmp/screenshot.png" // optional
}
accessibility_snapshot
// Get accessibility tree
{
"selector": "main" // optional, default entire page
}
accessibility_audit
// Run accessibility audit
{
"includeWarnings": true,
"selector": null
}
web_vitals
// Measure Core Web Vitals
{
"url": "https://example.com"
}
Network
network_requests
// Get network request log
{
"filter": {
"url": "/api/*",
"method": "GET",
"status": 200
}
}
mock_route
// Mock API endpoint
{
"pattern": "**/api/users",
"response": {
"status": 200,
"body": { "users": [{"id": 1, "name": "Test User"}] },
"headers": { "Content-Type": "application/json" }
}
}
unmock_route
// Remove mock
{
"pattern": "**/api/users"
}
React DevTools
react_inspect_element
// Inspect React component
{
"selector": "div.App"
}
react_get_component_tree
// Get full component tree
{}
Debugging
set_tracepoint
// Non-blocking breakpoint with logging
{
"source": "app.js",
"line": 42,
"condition": "user.id === 123",
"logMessage": "User: {user.name}"
}
set_logpoint
// Inject console.log
{
"source": "utils.js",
"line": 15,
"message": "Value: {myVariable}"
}
watch_expression
// Watch expression value
{
"expression": "this.state.count",
"source": "Counter.jsx"
}
Batch Execution
execute
// Execute multiple operations
{
"code": `
// Navigate
await callTool('navigate', { url: 'https://example.com' });
// Fill form
await page.fill('input[name="search"]', 'test query');
// Click and wait
await page.click('button[type="submit"]');
await page.waitForLoadState('networkidle');
// Capture screenshot
const screenshot = await callTool('screenshot', { fullPage: true });
return { screenshot };
`
}
Common Patterns
Automated Testing Workflow
// AI prompt: "Test the login flow and capture results"
// 1. Navigate to login page
await callTool('navigate', { url: 'https://app.example.com/login' });
// 2. Fill credentials (use environment variables)
await callTool('fill', {
selector: 'input[name="username"]',
value: process.env.TEST_USERNAME
});
await callTool('fill', {
selector: 'input[name="password"]',
value: process.env.TEST_PASSWORD
});
// 3. Submit form
await callTool('click', { selector: 'button[type="submit"]' });
// 4. Wait for navigation
await callTool('wait', { selector: '.dashboard', timeout: 5000 });
// 5. Capture evidence
const screenshot = await callTool('screenshot', { fullPage: true });
// 6. Run accessibility audit
const audit = await callTool('accessibility_audit', {});
Performance Testing
// AI prompt: "Measure performance of the homepage"
const vitals = await callTool('web_vitals', {
url: 'https://example.com'
});
// vitals contains: LCP, INP, CLS, TTFB, FCP
console.log(`LCP: ${vitals.LCP}ms`);
console.log(`CLS: ${vitals.CLS}`);
API Mocking for Frontend Tests
// AI prompt: "Mock the user API and test the profile page"
// 1. Set up mock
await callTool('mock_route', {
pattern: '**/api/user/profile',
response: {
status: 200,
body: {
id: 1,
name: 'Test User',
email: 'test@example.com'
}
}
});
// 2. Navigate and test
await callTool('navigate', { url: 'https://app.example.com/profile' });
// 3. Verify UI
await callTool('screenshot', { selector: '.profile-card' });
// 4. Clean up
await callTool('unmock_route', { pattern: '**/api/user/profile' });
Debugging with Tracepoints
// AI prompt: "Debug the checkout flow without stopping execution"
// Set tracepoint at critical function
await callTool('set_tracepoint', {
source: 'checkout.js',
line: 78,
condition: 'cart.total > 1000',
logMessage: 'High value cart: {cart.total}, items: {cart.items.length}'
});
// Watch state changes
await callTool('watch_expression', {
expression: 'this.state.checkoutStep',
source: 'CheckoutComponent.jsx'
});
// Execute checkout flow
await callTool('navigate', { url: 'https://shop.example.com/checkout' });
// ... interact with page
React Component Inspection
// AI prompt: "Inspect the React component structure of the dashboard"
// Get full component tree
const tree = await callTool('react_get_component_tree', {});
// Inspect specific component
const component = await callTool('react_inspect_element', {
selector: 'div[data-testid="dashboard"]'
});
// component contains: props, state, hooks, children
Network Monitoring & Analysis
// AI prompt: "Monitor API calls during user signup"
// Navigate to signup
await callTool('navigate', { url: 'https://app.example.com/signup' });
// Fill and submit form
await callTool('fill', { selector: 'input[name="email"]', value: 'test@example.com' });
await callTool('click', { selector: 'button.signup' });
// Get network requests
const requests = await callTool('network_requests', {
filter: {
url: '/api/*',
method: 'POST'
}
});
// Analyze response times and status codes
requests.forEach(req => {
console.log(`${req.method} ${req.url}: ${req.status} (${req.duration}ms)`);
});
Batch Execution for Complex Workflows
// AI prompt: "Run complete E2E test with multiple assertions"
const result = await callTool('execute', {
code: `
// Navigate to app
await callTool('navigate', { url: 'https://app.example.com' });
// Check accessibility
const audit = await callTool('accessibility_audit', { includeWarnings: false });
if (audit.violations.length > 0) {
throw new Error('Accessibility violations found');
}
// Measure performance
const vitals = await callTool('web_vitals', { url: 'https://app.example.com' });
if (vitals.LCP > 2500) {
console.warn('LCP exceeds threshold');
}
// Interact with UI
await page.click('button.start-tour');
await page.waitForSelector('.tour-step-1');
// Capture state
const screenshot = await callTool('screenshot', { fullPage: true });
return {
audit: audit.violations.length === 0,
performance: vitals,
screenshot
};
`
});
Troubleshooting
Playwright Browser Download Fails
Symptom: Extension shows error during activation about browser download.
Solutions:
Use system browser:
{ "browserDevtoolsMcp.browser.useSystemBrowser": true }Set custom executable path:
{ "browserDevtoolsMcp.browser.executablePath": "/Applications/Google Chrome.app/Contents/MacOS/Google Chrome" }Skip download via environment variable:
export PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD=1 code # or cursorConfigure proxy if behind firewall:
{ "browserDevtoolsMcp.network.proxy": "http://proxy.company.com:8080" }Manually install browsers:
npx playwright install chromium
MCP Server Not Registering
Symptom: AI assistant cannot access browser automation tools.
Solutions:
Verify extension is enabled:
{ "browserDevtoolsMcp.enable": true }Check MCP server status in Command Palette:
- Run: Browser DevTools MCP: Show Server Status
Restart MCP session:
- Run: Browser DevTools MCP: Restart Server
Check VS Code/Cursor version:
- VS Code: Requires 1.96+
- Cursor: Native MCP support required
Headless Mode Issues
Symptom: Browser automation fails in headless mode but works in headed mode.
Solution: Disable headless for debugging:
{
"browserDevtoolsMcp.browser.headless": false
}
Certificate Errors
Symptom: HTTPS errors preventing navigation.
Solution:
{
"browserDevtoolsMcp.network.ignoreHTTPSErrors": true
}
Warning: Only use for development/testing environments.
Performance Issues
Symptom: Slow browser operations or timeouts.
Solutions:
Enable persistent context:
{ "browserDevtoolsMcp.browser.persistent": true, "browserDevtoolsMcp.browser.userDataDir": "/path/to/userdata" }Adjust timeouts in tool calls:
await callTool('click', { selector: 'button', timeout: 60000 // 60 seconds });Disable screenshots/videos:
{ "browserDevtoolsMcp.debug.screenshots": false, "browserDevtoolsMcp.debug.videos": false }
Telemetry Opt-Out
To disable telemetry:
Via settings:
{ "browserDevtoolsMcp.telemetry.enable": false }Via environment variable:
export TELEMETRY_ENABLE=falseVia config file: Edit
~/.browser-devtools-mcp/config.json:{ "telemetryEnabled": false }
Advanced Configuration
OpenTelemetry Distributed Tracing
Enable tracing to monitor browser automation operations:
{
"browserDevtoolsMcp.otel.enable": true,
"browserDevtoolsMcp.otel.exporterUrl": "http://localhost:4318/v1/traces",
"browserDevtoolsMcp.otel.serviceName": "browser-automation",
"browserDevtoolsMcp.otel.propagationHeaderName": "traceparent"
}
Persistent Browser Context
Maintain browser state across sessions:
{
"browserDevtoolsMcp.browser.persistent": true,
"browserDevtoolsMcp.browser.userDataDir": "${workspaceFolder}/.browser-data"
}
CDP Attach Mode (Chromium Only)
Attach to existing browser instance:
{
"browserDevtoolsMcp.browser.cdp.enable": true,
"browserDevtoolsMcp.browser.cdp.endpointUrl": "http://localhost:9222"
}
Start Chrome with remote debugging:
google-chrome --remote-debugging-port=9222
Custom Locale
Test internationalization:
{
"browserDevtoolsMcp.browser.locale": "tr-TR"
}
Best Practices
Use environment variables for secrets:
await callTool('fill', { selector: 'input[name="apiKey"]', value: process.env.API_KEY });Enable debugging output for troubleshooting:
{ "browserDevtoolsMcp.debug.enable": true, "browserDevtoolsMcp.debug.outputDir": "${workspaceFolder}/debug-output" }Use batch execution for performance:
- Group related operations in
executetool - Access Playwright
pageobject directly for complex interactions
- Group related operations in
Mock external dependencies:
- Use
mock_routeto isolate frontend tests - Mock slow/unreliable APIs for consistent testing
- Use
Leverage accessibility snapshots:
- Test keyboard navigation and screen reader compatibility
- Validate ARIA attributes and semantic HTML
Monitor performance continuously:
- Set up web vitals baselines
- Alert on regressions in LCP, CLS, or INP