name: debug-browser
description: Debug or reproduce a component issue in a live browser using the Chrome DevTools MCP server. Use to get a live repro, inspect the DOM, watch console errors, analyze network requests, take screenshots, or evaluate scripts against a running grid. Covers launching dev servers and navigating to pages/stories. (For performance traces and hot-path profiling, use the debug-perf skill.)
argument-hint:
Browser Debugging via Chrome DevTools MCP
Guide for debugging grid components in a live browser using the Chrome DevTools MCP server. This enables inspecting DOM, evaluating scripts, capturing screenshots, monitoring console/network, and more — all from within the AI coding assistant.
Performance-specific debugging? See the
debug-perfskill for hot path analysis, render scheduler profiling, and performance budget verification. That skill includes a section on live browser profiling via MCP.
Prerequisites
The workspace has Chrome DevTools MCP pre-configured in .vscode/mcp.json:
{
"servers": {
"chrome-devtools": {
"command": "npx",
"args": ["-y", "chrome-devtools-mcp@latest"],
},
},
}
The MCP server launches its own Chromium instance. This is separate from any VS Code debug launch configuration — they are independent workflows:
- MCP browser: For AI-driven inspection, script evaluation, screenshots
- VS Code debugger: For breakpoints, step-through, variable inspection
Troubleshooting MCP server launch / connection failures
If an MCP tool call returns an error such as "server not running", "failed to launch", or "connection refused", work through this checklist before continuing:
- Confirm
.vscode/mcp.jsonmatches the configuration block above (correctcommand,args, no typos in the server namechrome-devtools). - Open the MCP: List Servers command from the VS Code command palette and verify
chrome-devtoolsis listed and shows status "Started". Use MCP: Restart Server if it is in an error state. - Open the server's output log via MCP: Show Output for
chrome-devtoolsand read the most recent error — common causes are missingnpx/Node, a corporate proxy blocking thechrome-devtools-mcp@latestdownload, or a previous Chromium process still holding the debug port. - If a stale Chromium is suspected, kill any running
chrome-devtools-mcp/ orphan Chromium processes and retry. - As a last resort, manually run
npx -y chrome-devtools-mcp@latestin a terminal and report the exact stderr output back to the user; do not silently retry the tool call.
Step 1: Reach the Dev Server (assume it's already running)
Do NOT start a dev server first. In a debugging session the user almost always already has the relevant dev server running. Starting another one wastes time and can fail on a port-in-use clash. Check first, start only as a last resort.
about:blankis NOT "no server".list_pages(or a freshly attached MCP browser) reportsabout:blankbecause that's the default empty tab — it tells you nothing about whether the dev server is up. Never infer "the server isn't running, I must start one" from anabout:blankpage. The only way to test the server is tonavigate_pageto the real URL and see whether it loads. Always navigate first.
Work through these in order and stop at the first that succeeds:
Just navigate via Chrome MCP. Point
navigate_pagestraight at the target URL (do not be misled by anabout:blanktab — navigate to confirm). If the page loads, you're done — no terminal command needed.navigate_page → url: http://localhost:4400/grid/plugins/editing/If it errors with a connection refused /
ERR_CONNECTION_REFUSED, the server for that port isn't up — fall through to step 2.Probe the port from the terminal (cheap, non-blocking) to confirm whether something is listening before launching anything:
curl -sSf -o /dev/null http://localhost:4400/ && echo "up" || echo "down"If it prints
up, retry thenavigate_pagefrom step 1 (the server is there). Ifdown, proceed to step 3.Only now start the dev server for the port you need, then navigate once it's listening:
# Docs site (port 4400) bun nx serve docs # Vanilla demo (port 4000) bun nx serve demo-vanilla # Angular demo (port 4200) bun nx serve demo-angular # React demo (port 4300) bun nx serve demo-react # Vue demo (port 4100) bun nx serve demo-vue # All demos at once bun run demo
Server reference:
| App | Port | Command | Probe |
|---|---|---|---|
| Docs Site | 4400 | bun nx serve docs |
curl -sSf -o /dev/null localhost:4400/ |
| Demo Vanilla | 4000 | bun nx serve demo-vanilla |
curl -sSf -o /dev/null localhost:4000/ |
| Demo Angular | 4200 | bun nx serve demo-angular |
curl -sSf -o /dev/null localhost:4200/ |
| Demo React | 4300 | bun nx serve demo-react |
curl -sSf -o /dev/null localhost:4300/ |
| Demo Vue | 4100 | bun nx serve demo-vue |
curl -sSf -o /dev/null localhost:4100/ |
Step 2: Navigate to the Target
Navigate to a demo app
navigate_page → url: http://localhost:4200/
Navigate to a specific docs page
The docs site runs at http://localhost:4400. Navigate to specific pages by path:
navigate_page → url: http://localhost:4400/grid/plugins/editing/
navigate_page → url: http://localhost:4400/grid/guides/getting-started/
Reload a page (after code changes with HMR)
navigate_page → type: reload, ignoreCache: true
List open pages
list_pages
Use select_page to switch between tabs if multiple are open.
Step 3: Inspect and Debug
DOM Inspection
Take a DOM snapshot to see the full HTML structure:
take_snapshot → selector: tbw-grid
This returns a structured DOM tree with element attributes, classes, and text content. Very useful for understanding the current grid state.
Evaluate a script to query the DOM programmatically:
// Example: Count visible rows, editing cells, check column count
() => {
const grid = document.querySelector('tbw-grid');
return {
rows: grid?._bodyEl?.querySelectorAll('.data-grid-row').length,
editingCells: grid?.querySelectorAll('.cell.editing').length,
columns: grid?._visibleColumns?.length,
};
};
Console Monitoring
List console messages to catch warnings/errors:
list_console_messages
Get a specific console message by index for full details:
get_console_message → index: 0
Useful for catching grid validation warnings (e.g., "EditingPlugin required", config validation errors).
Network Monitoring
List network requests to debug API calls or asset loading:
list_network_requests
Get details of a specific request (headers, response body):
get_network_request → index: 0
Screenshots
Take a screenshot for visual verification:
take_screenshot
Take a screenshot of a specific element:
take_screenshot → selector: tbw-grid
Useful for visual regression debugging — compare before/after states.
Interaction
Click on an element:
click → selector: .cell[data-col="3"][data-row="0"]
Fill an input:
fill → selector: .cell.editing input → value: new text
Press a key:
press_key → key: Enter
press_key → key: Escape
press_key → key: Tab
Hover over an element (e.g., to trigger tooltips):
hover → selector: .cell[data-col="0"][data-row="0"]
Wait for Conditions
Wait for an element to appear:
wait_for → selector: .cell.editing
wait_for → selector: tbw-grid .data-grid-row
Wait for network idle (after navigation or data loading):
wait_for → type: networkIdle
Step 4: Script Evaluation Patterns
The evaluate_script tool is the most powerful debugging tool. It runs arbitrary JavaScript in the page context.
Grid Internals Access
The grid's internal state is accessible through the DOM element:
() => {
const grid = document.querySelector('tbw-grid');
return {
// Configuration
effectiveConfig: Object.keys(grid.effectiveConfig || {}),
columnCount: grid._visibleColumns?.length,
rowCount: grid._rows?.length,
// Virtualization window
virtStart: grid._virtualization?.start,
virtEnd: grid._virtualization?.end,
rowHeight: grid._virtualization?.rowHeight,
// Edit state
activeEditRow: grid._activeEditRows,
isGridEditMode: grid._isGridEditMode,
editingCells: grid.querySelectorAll('.cell.editing').length,
// Plugin state
plugins: grid._pluginManager?.getPlugins().map((p) => p.constructor.name),
};
};
Framework Adapter Inspection
// Angular adapter state
() => {
const grid = document.querySelector('tbw-grid');
const adapter = grid?.__frameworkAdapter;
return {
adapterType: adapter?.constructor.name,
viewRefs: adapter?.viewRefs?.length,
componentRefs: adapter?.componentRefs?.length,
editorViewRefs: adapter?.editorViewRefs?.length,
editorComponentRefs: adapter?.editorComponentRefs?.length,
};
};
Monkey-Patching for Tracing
Inject tracing code to track specific operations:
// Track how many times releaseCell is called and what triggers it
() => {
const grid = document.querySelector('tbw-grid');
const adapter = grid?.__frameworkAdapter;
if (!adapter?.releaseCell) return { error: 'No adapter or releaseCell' };
const original = adapter.releaseCell.bind(adapter);
window.__releaseLogs = [];
adapter.releaseCell = (cellEl) => {
window.__releaseLogs.push({
col: cellEl.getAttribute('data-col'),
row: cellEl.getAttribute('data-row'),
isEditing: cellEl.classList.contains('editing'),
stack: new Error().stack?.split('\n').slice(1, 4).join('\n'),
});
return original(cellEl);
};
return { patched: true };
};
Then trigger the action and read results:
() => ({
logCount: window.__releaseLogs?.length || 0,
logs: window.__releaseLogs?.slice(0, 10) || [],
});
Async Operations
For operations that need to wait for render cycles:
() => {
const grid = document.querySelector('tbw-grid');
// Trigger something
grid.rows = [...grid.rows];
// Wait for render to complete
return new Promise((resolve) => {
requestAnimationFrame(() => {
requestAnimationFrame(() => {
resolve({ rendered: true, rowCount: grid._rows?.length });
});
});
});
};
Step 5: Common Debugging Workflows
"Component renders incorrectly"
navigate_pageto the page showing the issuetake_screenshotfor visual contexttake_snapshotontbw-gridfor DOM structureevaluate_scriptto read grid state (config, columns, rows)- Identify the discrepancy between expected and actual state
"Editor is destroyed during editing"
- Navigate to the page with the editor
evaluate_script: Monkey-patchadapter.releaseCellto log destructions- Enter edit mode via
click+press_key Enter - Trigger the suspected cause (e.g., rows replacement)
evaluate_script: Read the logs to see if/how destruction happened
"Event not firing or has wrong payload"
evaluate_script: Add event listener with logging() => { window.__eventLogs = []; const grid = document.querySelector('tbw-grid'); grid.on('cell-commit', (detail) => { window.__eventLogs.push({ type: 'cell-commit', detail }); }); return { listening: true }; };- Trigger the action
evaluate_script: Readwindow.__eventLogs
"Styles not applied correctly"
take_screenshotfor visual stateevaluate_script: Read computed styles() => { const cell = document.querySelector('.cell[data-col="0"]'); const styles = getComputedStyle(cell); return { width: styles.width, color: styles.color, background: styles.backgroundColor, display: styles.display, }; };take_snapshotwith selector for the specific element's DOM
"Docs page fails to render"
navigate_pagetohttp://localhost:4400/grid/plugins/<plugin>/list_console_messagesto catch any errorstake_screenshotfor visual statetake_snapshotto see what DOM was actually rendered
MCP Tool Reference
Tools are grouped by category. Pick a category first based on what you are trying to learn, then choose a tool within it.
Navigation & tab management
Use when you need to load a URL, switch between tabs, or open/close pages.
| Tool | Purpose |
|---|---|
navigate_page |
Go to URL, reload, go back/forward |
list_pages |
List all open browser tabs |
select_page |
Switch to a different tab |
new_page |
Open a new tab |
close_page |
Close a tab |
Visual debugging (screenshots & DOM snapshots)
Use when you need to see what the page looks like or what its DOM structure is.
| Tool | Purpose |
|---|---|
take_screenshot |
Capture full page or element screenshot |
take_snapshot |
Get DOM tree structure |
Script evaluation
Use when you need to read internal state, monkey-patch a method, or run arbitrary JS in the page context.
| Tool | Purpose |
|---|---|
evaluate_script |
Run JavaScript in page context |
User interaction simulation
Use when you need to simulate a user action (click, type, drag, key press) to reproduce or trigger a bug.
| Tool | Purpose |
|---|---|
click |
Click an element by selector |
fill |
Type text into an input |
fill_form |
Fill multiple form fields at once |
press_key |
Send keyboard events |
hover |
Hover over an element |
drag |
Drag from one element to another |
upload_file |
Upload a file to a file input |
handle_dialog |
Accept/dismiss browser dialogs |
Synchronization & viewport
Use when you need to wait for the page to settle, or to change the viewport / device emulation.
| Tool | Purpose |
|---|---|
wait_for |
Wait for element, URL, timeout, or network |
emulate |
Set viewport size or device emulation |
resize_page |
Resize the browser viewport |
Console monitoring
Use when you need to catch warnings, errors, or console.log output emitted by the page.
| Tool | Purpose |
|---|---|
list_console_messages |
Get all console messages |
get_console_message |
Get specific console message details |
Network monitoring
Use when you need to debug API calls, asset loading, or response payloads.
| Tool | Purpose |
|---|---|
list_network_requests |
List all network requests |
get_network_request |
Get specific request/response details |
Performance tracing
Use when you need to profile rendering, scripting, or layout work. See the debug-perf skill for full workflow.
| Tool | Purpose |
|---|---|
performance_start_trace |
Start a performance trace (see debug-perf) |
performance_stop_trace |
Stop trace and get results |
performance_analyze_insight |
Get AI-analyzed performance insights |