Debugging Web Applications
A comprehensive guide to debugging web applications using devtool-mcp's proxy and frontend diagnostics.
Overview
This use case covers the complete workflow for debugging frontend issues:
- Set up the debugging environment
- Capture and analyze HTTP traffic
- Identify JavaScript errors
- Debug layout and styling issues
- Document and share findings
Setting Up
Start Your Development Server
// Detect project and available scripts
detect {path: "."}
→ {type: "node", scripts: ["dev", "build", "test"]}
// Start dev server
run {script_name: "dev"}
→ {process_id: "dev", state: "running"}
// Verify it's ready
proc {action: "output", process_id: "dev", grep: "ready", tail: 5}
→ "Ready on http://localhost:3000"
Create Debugging Proxy
proxy {action: "start", id: "debug", target_url: "http://localhost:3000", port: 8080}
→ {id: "debug", listen_addr: ":8080", status: "running"}
Now browse to http://localhost:8080 instead of port 3000.
Debugging Workflows
Investigating a User-Reported Bug
Scenario: User reports "the submit button doesn't work on the checkout page."
Step 1: Navigate to the Problem Page
Have the user (or navigate yourself) to the checkout page via the proxy.
Step 2: Check for JavaScript Errors
proxylog {proxy_id: "debug", types: ["error"]}
→ {
entries: [{
message: "Cannot read property 'submit' of undefined",
source: "/static/js/checkout.js",
line: 234
}]
}
Step 3: Check API Calls
proxylog {proxy_id: "debug", types: ["http"], url_pattern: "/api/checkout"}
→ {
entries: [{
method: "POST",
url: "/api/checkout",
status: 401,
response_body: "{\"error\": \"Session expired\"}"
}]
}
Step 4: Inspect the Button
proxy {action: "exec", id: "debug", code: "window.__devtool.inspect('#submit-btn')"}
→ {
visibility: {visible: true},
accessibility: {role: "button", accessibleName: "Submit Order"},
// Element exists and is visible
}
Conclusion: The API returns 401 (session expired), causing the JavaScript error. The button itself is fine.
Debugging Layout Issues
Scenario: Content is getting cut off on mobile.
Step 1: Find Overflow Issues
proxy {action: "exec", id: "debug", code: "window.__devtool.findOverflows()"}
→ [
{selector: ".product-grid", overflow: "horizontal", excess: 200}
]
Step 2: Get Details
proxy {action: "exec", id: "debug", code: "window.__devtool.getOverflow('.product-grid')"}
→ {
scrollWidth: 520,
clientWidth: 320,
hasHorizontalOverflow: true
}
Step 3: Highlight the Issue
proxy {action: "exec", id: "debug", code: "window.__devtool.highlight('.product-grid', {color: 'rgba(255,0,0,0.3)', duration: 0})"}
Step 4: Take Screenshot
proxy {action: "exec", id: "debug", code: "window.__devtool.screenshot('overflow-issue')"}
Step 5: Examine the CSS
proxy {action: "exec", id: "debug", code: "window.__devtool.getComputed('.product-grid', ['display', 'grid-template-columns', 'gap'])"}
→ {
display: "grid",
"grid-template-columns": "repeat(3, 200px)", // Fixed width - problem!
gap: "16px"
}
Solution: Change repeat(3, 200px) to repeat(auto-fit, minmax(200px, 1fr)).
Debugging Z-Index Problems
Scenario: Dropdown menu appears behind a modal.
Step 1: Find Stacking Contexts
proxy {action: "exec", id: "debug", code: "window.__devtool.findStackingContexts()"}
→ [
{selector: ".modal", zIndex: 1000, reason: "position: fixed"},
{selector: ".dropdown", zIndex: 100, parentContext: ".header"}
]
Step 2: Check Dropdown's Context
proxy {action: "exec", id: "debug", code: "window.__devtool.getStacking('.dropdown')"}
→ {
zIndex: 100,
createsContext: true,
parentContext: ".header", // Constrained by header!
reason: "z-index with position: absolute"
}
Step 3: Check Header's Z-Index
proxy {action: "exec", id: "debug", code: "window.__devtool.getStacking('.header')"}
→ {
zIndex: 50, // Lower than modal's 1000
createsContext: true
}
Problem: The dropdown's z-index (100) is relative to .header (50), which is lower than the modal (1000).
Solution: Either increase header's z-index above 1000, or move the dropdown outside the header's stacking context.
Performance Investigation
Scenario: Page loads slowly.
Step 1: Check Performance Metrics
proxylog {proxy_id: "debug", types: ["performance"]}
→ {
entries: [{
navigation: {dom_content_loaded: 2500, load_event: 4500},
paint: {first_paint: 1800, first_contentful_paint: 2200}
}]
}
Step 2: Analyze Resources
proxy {action: "exec", id: "debug", code: "window.__devtool.captureNetwork()"}
→ {
summary: {
totalResources: 45,
totalTransferSize: 2500000,
byType: {
script: {count: 12, size: 1800000} // 1.8MB of JS!
}
},
entries: [
{name: "vendor.js", duration: 2100, size: 1200000}
]
}
Step 3: Check for Slow API Calls
proxylog {proxy_id: "debug", types: ["http"], url_pattern: "/api"}
→ Find any API calls with high duration_ms
Step 4: Get Page Session Overview
currentpage {proxy_id: "debug", action: "get", session_id: "page-1"}
→ Full breakdown of resources, errors, and timing
Interactive Debugging
Let User Show You the Problem
proxy {action: "exec", id: "debug", code: "window.__devtool.selectElement()"}
Ask the user to click on the problematic element. The response includes the selector and element details.
Ask User Questions
proxy {action: "exec", id: "debug", code: "window.__devtool.ask('Does this look correct?', ['Yes', 'No', 'Partially'])"}
Wait for Dynamic Content
proxy {action: "exec", id: "debug", code: "window.__devtool.waitForElement('.results-loaded', 10000)"}
Documentation Workflow
Capture State for Bug Report
// Screenshot
proxy {action: "exec", id: "debug", code: "window.__devtool.screenshot('bug-state')"}
// DOM snapshot
proxy {action: "exec", id: "debug", code: "window.__devtool.captureDOM()"}
// Element details
proxy {action: "exec", id: "debug", code: "window.__devtool.inspect('.problem-element')"}
// Recent errors
proxylog {proxy_id: "debug", types: ["error"], since: "5m"}
// Recent traffic
proxylog {proxy_id: "debug", types: ["http"], url_pattern: "/api", since: "5m"}
Create Visual Guide
// Highlight relevant elements
proxy {action: "exec", id: "debug", code: "window.__devtool.highlight('.step-1', {label: 'Step 1', duration: 0})"}
proxy {action: "exec", id: "debug", code: "window.__devtool.highlight('.step-2', {label: 'Step 2', duration: 0})"}
// Screenshot with highlights
proxy {action: "exec", id: "debug", code: "window.__devtool.screenshot('annotated-steps')"}
// Clean up
proxy {action: "exec", id: "debug", code: "window.__devtool.clearAllOverlays()"}
Complete Debugging Session Example
User: "The product page is broken"
AI: Let me investigate. First, I'll set up debugging.
[run {script_name: "dev"}]
[proxy {action: "start", id: "debug", target_url: "http://localhost:3000"}]
AI: Please navigate to http://localhost:8080/products/123
[User navigates]
AI: Let me check for errors.
[proxylog {proxy_id: "debug", types: ["error"]}]
→ "TypeError: products.map is not a function"
AI: Found a JavaScript error. Let me check the API.
[proxylog {proxy_id: "debug", types: ["http"], url_pattern: "/api/products"}]
→ {status: 200, response_body: "{\"product\": {...}}"}
AI: The API returned a single product object, not an array.
The code expects an array (products.map).
Let me show you exactly where:
[proxy {action: "exec", id: "debug", code: "window.__devtool.highlight('.products-list', {color: 'red'})"}]
AI: The products-list container is highlighted. The component expects
an array but receives a single object.
Fix: Change the API endpoint to return [{...}] or update the
component to handle single products.
Best Practices
- Start with errors - Check
proxylogfor errors first - Check API responses - Many frontend bugs are caused by unexpected API data
- Use page sessions -
currentpagegives a unified view - Screenshot liberally - Visual evidence helps communication
- Clean up highlights - Use
clearAllOverlays()when done - Document findings - Capture state for sharing
See Also
- Reverse Proxy - Proxy configuration
- Frontend Diagnostics - All available primitives
- Performance Monitoring - Deep performance analysis