Stop being your AI's eyes and hands: pwa-debug lets Claude debug your live, logged-in browser
If you've debugged a web app with an AI assistant, you know the loop: The AI asks what the console says. You copy and paste it. It asks what the component's state is. You open React DevTools and describe it. It asks yo
If you've debugged a web app with an AI assistant, you know the loop:
- The AI asks what the console says. You copy and paste it.
- It asks what the component's state is. You open React DevTools and describe it.
- It asks you to click the button and report what happens. You click it.
- It asks for a screenshot. You take one.
You end up as the AI's eyes and hands. That's slow, it loses information, and it doesn't work at all for the bugs that hurt most in PWAs: stale caches, service workers that won't update, install failures, and version skew after a deploy.
I built pwa-debug to remove that loop. It's an open-source (MIT) MCP server plus a browser extension that gives an AI agent like Claude Code direct, structured access to your live, logged-in browser. That covers the DOM, console, network, framework state, store state, service workers, caches, and storage, and the agent can also interact with the page.
npx -y @aryanduntley/pwa-debug@latest
- ๐ฆ npm:
@aryanduntley/pwa-debug - ๐ GitHub: aryanduntley/pwa-debug-layer
The questions it answers
These are the PWA problems people search for most. pwa-debug answers them by reading the runtime directly, without screenshots or OCR:
| You ask | The agent calls |
|---|---|
| Why won't my service worker update? Why are some users on old code? |
sw_status, pwa_update_analyze (waiting-vs-active versions, version-skew verdict) |
| Why is my cache stale? Why are chunks 404ing after a deploy? |
cache_list / cache_inspect / cache_match (entries with age, size, cache-control) |
| Why won't my PWA install? |
pwa_installability (structured { gaps[], fixes[] }, not just "manifest invalid") |
| What's actually in IndexedDB right now? |
idb_list / idb_query, storage_get
|
| Why does this component have the wrong state? |
react_get_state, vue_get_state, store_get_state, redux_get_state
|
| Does anything break at 360px, in dark mode, in Spanish? |
layout_sweep (one call, every page ร width ร scheme ร state) |
Highlights
๐ It runs in your real browser profile
Google's chrome-devtools-mcp is great, but it starts a fresh automated Chrome. That browser has none of your cookies, sessions, or extensions, and none of your real service-worker state. If your app sits behind a login, that's a problem.
pwa-debug attaches to your normal profile by default, so the agent sees what you see: authenticated sessions, your real SW and cache state, and your extensions.
โ๏ธ Framework and store state that CDP can't reach
A script injected into the page's MAIN world reads each framework through its own devtools hooks:
- React fiber tree, props, state, and hooks
- Vue 3 component tree and reactive state
- Svelte components with source locations
- Solid detection and element lookup
- Redux, Zustand, Pinia, Jotai: read, subscribe to change deltas, and (opt-in) dispatch
Every framework also supports find-by-text and find-by-role, so the agent can locate "the Submit button inside <CheckoutForm>" without guessing selectors.
๐ ๏ธ The full PWA diagnostics suite
This is the part the project is named for. It covers service-worker lifecycle and versions, CacheStorage contents and age, installability gaps with per-gap fixes, a live capability matrix (Push, Background Sync, Periodic Sync, Badging, File System Access, Window Controls Overlay), IndexedDB and web storage, and pwa_update_analyze. That last tool combines SW status, cache ages, and recent 404s into a verdict such as "cached HTML is older than the JS it references", which is the classic cause of "why are some users on old code."
pwa_snapshot produces one capped blob of the whole runtime state, which is useful for repros and bug hand-offs.
๐ The agent drives the page itself
pdl_click, pdl_fill, pdl_submit, pdl_key_press, pdl_type_sequence, pdl_navigate, and pdl_new_tab are available, along with touch gestures: tap, double-tap, long-press, swipe, drag, scroll, and pinch. Events are dispatched as native-event sequences, so React's and Vue's delegated event systems fire properly. Targets use one locator format (selector, role, text, or a framework component id).
๐ฑ Android phones over adb
The extension can't run in Android Chrome, so pwa-debug connects to phones over adb and CDP and injects the same page-world code there. One call does the setup:
pdl_android_attach({
open_url: "http://localhost:5173",
reverse_ports: [5173] // phone's localhost:5173 โ your dev server
})
โ { connectionId: "cdp:127.0.0.1:<port>:<tab>" }
After that, every page tool works on the phone tab, including React state, stores, PWA diagnostics, gestures, and console and network capture, and you can watch it happen on the device. I verified this live on a Galaxy S24 Ultra (Android 16, Chrome 156).
๐ Responsive layout sweep
One layout_sweep call goes through a matrix of pages ร viewport widths ร color schemes ร app states (plus extra dimensions such as language). For each view it emulates the viewport over CDP and measures the page for:
- horizontal page overflow and off-screen elements
- tap targets below WCAG 2.2 minimums
- clipped or ellipsized text (useful when testing longer translations)
- fixed bars that aren't fully on screen
- clipped focus and selection rings
- unresolved copy like
{{name}},undefined,NaN,[object Object] - page errors during the view, plus optional WCAG AA contrast
It groups the results into distinct problems, so 150 raw findings that come from two root causes are reported as two groups, with screenshots if you ask for them. You can also pass your own custom_checks expressions. In the largest real test it swept an 83-page prototype across 4,812 views.
๐ Library popups, replay, and source maps
-
WalletConnect, RainbowKit, and SDK modals that live in shadow roots or nested iframes can be recorded, replayed, and correlated with the console errors and failed requests that occurred while they were open (
popup_failures). - rrweb session recording and replay, plus persistent ring buffers that survive navigations and reloads.
-
source_map_resolvemaps minified stack frames back tosrc/file.ts:line:col.
๐ค Built for agents, not humans
Every tool returns { ok, data, error?, next_steps[] }. The next_steps field tells the agent what to call next based on the actual result, so it recovers from setup gaps and errors without you stepping in. pdl_help serves the whole usage guide as data, and filters keep each response to the slice the agent asked for instead of dumping the full DOM.
Works with chrome-devtools-mcp, doesn't replace it
pwa-debug is complementary to chrome-devtools-mcp, and the two tool sets don't overlap. pwa-debug launches a browser with a live debug port, and chrome-devtools-mcp attaches to that same port. Both then drive the same tab over separate channels: CDP for one, extension and page-world injection for the other.
pdl_launch_browser({ browser: "brave", mode: "sandbox-persistent" })
pdl_register_chrome_devtools() // pins chrome-devtools-mcp to the right port for you
Getting started
As a Claude Code plugin (no clone, no build)
/plugin marketplace add aryanduntley/pwa-debug-layer
/plugin install pwa-debug@pwa-debug
/reload-plugins
Or with any MCP client
claude mcp add pwa-debug --scope user -- npx -y @aryanduntley/pwa-debug@latest
Then tell your agent:
Set up pwa-debug and open my app at http://localhost:5173.
The setup is agent-driven. It runs pdl_check_setup, registers the native-messaging host, launches a browser with the extension preloaded, and confirms the round-trip with session_ping. Each step's next_steps tells it what to do next.
Browser support: Chromium-family browsers (Chrome, Chromium, Brave, Edge, Vivaldi, Opera) and Android Chrome over adb. Brave and Chromium give the smoothest experience, because Google Chrome 142+ removed --load-extension and so needs one manual "Load unpacked" step.
How it works
Claude Code โโMCPโโโบ Native host (MCP server, ring buffers, replay store)
โ native messaging
โผ
Extension service worker
โ
โโโโโโโโโโโโโโโดโโโโโโโโโโโโโโโ
โผ โผ
Content script Page-world script (MAIN world)
DOM observe, actions React/Vue hooks, fetch/XHR patch, stores
- The extension handles the page: DOM, content scripts, and page-world hooks injected on every load.
- The native host handles persistence and the MCP server.
- MCP is the contract with the AI.
The whole codebase is TypeScript written in a strict functional style: pure functions, immutable data, no classes, and side effects kept at the edges.
Help wanted ๐
Development happens on Linux, where everything is live-tested. The macOS and Windows code paths (browser detection, profile paths, native-messaging registration) are written and unit-tested but have never run on real hardware. If you're on a Mac or a Windows machine, trying it and opening an issue with the output would help more than anything else right now.
Firefox support is on the roadmap (via WebDriver BiDi). Stars, issues, and PRs are all welcome.
- โญ GitHub: https://github.com/aryanduntley/pwa-debug-layer
- ๐ฆ npm: https://www.npmjs.com/package/@aryanduntley/pwa-debug
If you try it on a PWA bug, I'd like to hear how it went in the comments, whether it worked or not.
Originally published by Dev.to WebDev. Aggregated on AIWithGhost for educational purposes โ full credit and traffic to the original publisher.