Skip to content
Agentmart
Submit a tool

PWA Debug Layer

Debug PWAs in your real browser via MCP: service-worker, cache, installability & framework state.

Connector by aryanduntley · GitHub account verified by the MCP Registry

IndexedMITLocal (stdio)
Write a review

Install

Works with Claude Code, Codex, VS Code, Cursor, Windsurf, Claude Desktop. Read the repository before you give any tool access to your data.

npx agentmarket install connector/aryanduntley-pwa-debug-layer

Asks which of your tools to add it to (Claude Code, Codex, Cursor, VS Code, Windsurf, Gemini CLI, Claude Desktop) and shows the change first. See https://agentmart.dev/cli.

Get the weekly diff

The weekly diff: one email a week with new agent tools, notable updates and grade changes, plus security alerts for tools you follow. Double opt-in, one-click unsubscribe.

We use your email only for this newsletter. Privacy

About

Debug PWAs in your real browser via MCP: service-worker, cache, installability & framework state.

No reviews yet. Installed it? Reviews come from verified installers only. How reviews work

Permissions

What this server can do and what it asks for, from its tool list and install config. Checked Oct 25, 2026.

Tools
37 read · 18 write · 2 delete · 6 not yet classified
Secrets
None asked for
Install scripts
Not scanned yet
Runs as
Node.js package

Tools by what they do 63

Read from the source code; the running server may list different tools.

Read 37 · Look things up without changing anything.

  • cache_inspect
  • cache_list
  • cache_match
  • console_tail
  • error_tail
  • evaluate
  • host_list_registrations
  • host_status
  • idb_list
  • idb_query
  • network_tail
  • pdl_browser_status
Show 25 more tools
  • pdl_check_setup
  • popup_replay
  • popup_tail
  • pwa_snapshot
  • pwa_status
  • recent_events
  • redux_get_state
  • redux_tail
  • session_replay
  • settings_get
  • settings_list_schema
  • solid_detect
  • solid_find_by_role
  • solid_find_by_text
  • source_map_resolve
  • storage_get
  • svelte_components
  • svelte_find_by_role
  • svelte_find_by_text
  • sw_lifecycle_tail
  • sw_status
  • vue_find_by_role
  • vue_find_by_text
  • vue_get_state
  • vue_tree

Write 18 · Create, change, send or run things.

  • host_register_extension
  • pdl_close_browser
  • pdl_install_extension
  • pdl_navigate
  • pdl_register_chrome_devtools
  • popup_record
  • pwa_update_analyze
  • react_find_by_role
  • react_find_by_text
  • react_get_state
  • react_tree
  • redux_subscribe
Show 6 more tools
  • session_record
  • settings_set
  • store_dispatch
  • store_get_state
  • store_subscribe
  • store_tail

Delete 2 · Delete, drop, revoke or wipe things.

  • host_reset
  • host_unregister_extension

Not yet classified 6 · We could not tell from the name and description.

  • pdl_launch_browser
  • pdl_new_tab
  • popup_failures
  • pwa_installability
  • redux_dispatch
  • session_ping

Files it touches

  • Any path the agent passes to its tools

Tools (63)

Read from the source code; the running server may differ. Descriptions are the server’s own words; hidden characters are shown as [U+…].

  • cache_inspect

    Inspect one CacheStorage cache's entries. Returns CacheInspectResult { supported, found, name, entries: [{ url, method, status, contentType, contentLength, dateHeader, ageSeconds, cacheControl }], entryCount, truncated }. ageSeconds (now − the response's Date header) is the staleness signal — old app-shell HTML or API responses are the usual 'why won't my update show' / 'why is my data stale' cause. Get the cache name from cache_list first. Bodies are NOT read (size is from content-length). Args: { extension_id?, tab_id?, cache_name (required), limit?: default 200, max 1000 }. Page-world read;…

  • cache_list

    List the debugged PWA's CacheStorage caches. Returns CacheListResult { supported, caches: [{ name, entryCount }] } read from the page's caches.* API. Use to see what the service worker has cached, then cache_inspect(cache_name) for per-entry detail (age/size/type) or cache_match(url) to find which cache serves a URL — the core of diagnosing stale-cache bugs. Reads your real profile's caches; CDP/chrome-devtools-mcp does not surface this. Args: { extension_id?, tab_id? }. Runs in page-world via the page-bridge. CALL host_status FIRST.

  • cache_match

    Find which CacheStorage cache would serve a URL. Returns CacheMatchResult { supported, url, matched, cacheName, entry } — iterates the caches in order and reports the first hit plus the matched entry (status, content-type, ageSeconds, cache-control). Answers 'is /app.js cached, by which cache, and how old is it'. CAVEAT: a hit means a cached copy EXISTS; the SW's fetch handler decides whether it's actually served (cache-first / network-first / SWR) and that strategy is not observable from here — reported as a heuristic, not a guarantee. Args: { extension_id?, tab_id?, url (required) }. Page-wo…

  • console_tail

    Tail the host-side console ring buffer for a target extension with cursor pagination + FilterSpec. Returns { entries: ConsoleEntry[]; cursor: Cursor|null; hasMore: bool }. Each ConsoleEntry has page-world fields (ts, frameUrl, frameKey, level, args, optional stack) intersected with host fields (receivedAt, sessionId, extensionId, sequenceNumber) plus a per-entry cursor. Top-level cursor = newest entry's cursor for forward pagination via filter.since. With no extension_id, targets the single connected NMH (errors if zero or multiple). FilterSpec (all optional): level=ConsoleLevel[] (log|info|wa…

  • error_tail

    Tail the host-side page_error ring buffer for a target extension with cursor pagination + FilterSpec. page_error events are UNCAUGHT failures captured app-/framework-agnostically: a window 'error' (ErrorEvent / window.onerror, subkind='error') or an 'unhandledrejection' (a rejected promise the app did not catch, subkind='unhandledrejection'). Returns { entries: PageErrorEntry[]; cursor: Cursor|null; hasMore: bool }. Each entry: { kind:'page_error', subkind, message, name?, stack?, source?(url:line:col), ts, frameUrl, frameKey, ...host fields, cursor }. Use this to see thrown errors and rejecte…

  • evaluate

    Evaluate a JavaScript expression in the page (MAIN world) via the page-bridge — NO CDP, so this coexists with the user's open DevTools (chrome-devtools-mcp's evaluate_script cannot). Sees framework globals (window.React, __REACT_DEVTOOLS_GLOBAL_HOOK__, store hooks, etc.) that the SW and content script cannot reach. Args: { extension_id?, tab_id?, expression: non-empty string, timeout_ms?: <=3500ms (default 3000), await_promise? }. Returns { extensionId, tabId, value?, truncated?, durationMs, error? } where value is serialized with a 16KB cap (DOM nodes/functions/promises/errors become { __type…

  • host_list_registrations

    Lists registered extension IDs from the host state file. Cheap read-only view; does NOT verify that manifests are on disk or that any extension is connected. For the full setup picture, prefer host_status.

  • host_register_extension

    Registers an extension ID as an allowed origin for the pwa-debug native messaging host. Detects every Chromium-family install on this machine — Linux native packages, Linux snap (Chromium), Linux flatpak (any vendor), macOS Application Support, Windows HKCU registry — and writes the host manifest into each one. Also drops an install-time launcher script (POSIX sh / Windows .bat) that embeds an absolute node path so the host spawns correctly under sandboxed/stripped PATH (snap, flatpak). Idempotent. ID DISCOVERY: read the pwa-debug service-worker console via chrome-devtools-mcp — the SW logs `[…

  • host_reset

    DESTRUCTIVE: removes ALL registered extension IDs and deletes every per-browser host manifest (POSIX files + Windows HKCU keys + the shared %APPDATA% manifest JSON). Requires confirm:"reset" to invoke (a typed safety guard). Inform the user before calling. Use only when starting setup from scratch.

  • host_status

    Reports the install/liveness state of the pwa-debug native messaging host: registered extension IDs, expected manifest paths per detected browser install (with on-disk verification for POSIX kinds), launcher script path + presence, and the host binary path. Cheap, idempotent, no side effects. CALL THIS BEFORE ANY OTHER pwa-debug TOOL to confirm setup. The structured response includes a next_steps[] array tailored to the actual state — follow it.

  • host_unregister_extension

    Removes an extension ID from the pwa-debug host manifest allowed_origins. If at least one ID remains, manifests are rewritten with the new union; if the last ID is removed, manifests are deleted entirely (Windows HKCU keys cleared too). Use to recycle stale IDs after a manifest key change in dev. Idempotent: removing an already-absent ID returns removed:false with no side effects.

  • idb_list

    List the debugged PWA's IndexedDB databases and their schema. Returns IdbListResult { supported, databases: [{ name, version, stores: [{ name, keyPath, autoIncrement, indexes: [{ name, keyPath, unique, multiEntry }] }], error? }] } read from the page's indexedDB API. Use to discover where the app keeps structured/offline data, then idb_query(db, store) to read records — the recurring 'inspect IndexedDB live' need that CDP/chrome-devtools-mcp does not surface for your real profile. Read-only: opening a database never creates one. Args: { extension_id?, tab_id? }. Page-world read. CALL host_stat…

  • idb_query

    Read a capped slice of records from one IndexedDB object store. Returns IdbQueryResult { supported, found, db, store, records: [{ key, value, truncated? }], returned, truncated } read read-only from the page's indexedDB. Get the db + store names from idb_list first. Use to inspect the app's offline/cached structured data — the 'what's actually in IndexedDB' need CDP/chrome-devtools-mcp does not surface for your real profile. Read-only (no writes); values over 16KB are truncated. Args: { extension_id?, tab_id?, db (required), store (required), limit?: default 100, max 1000 }. Page-world read. C…

  • network_tail

    Tail the host-side network ring buffer (fetch + xhr + websocket events) for a target extension with cursor pagination + FilterSpec. Returns { entries: NetworkEntry[]; cursor: Cursor|null; hasMore: bool }. Each NetworkEntry is discriminated by kind: 'fetch' | 'xhr' (request/response/error phases correlated by captureId, with method, url, headers, status, body, durationMs; xhr adds responseType) | 'websocket' (subkind=open|frame|close|error, connectionId, url, direction=send|receive, frameType=text|binary, data, code, reason). All carry host fields (receivedAt, sessionId, extensionId, sequenceNu…

  • pdl_browser_status

    Live state of the browsers pdl_launch_browser has started or attached to: each managed launch (browser, profile mode, port, pid, browserUrl) with a fresh debug-port liveness re-probe, plus the pwa-debug extension connections (extensionId + lastSeenAt heartbeat age). Cheap, no side effects. Use it to confirm a launch is still alive, find the browserUrl to hand to chrome-devtools-mcp, or see whether the extension SW is connected. Launch records persist across host restarts (launches.json); the liveness re-probe distinguishes still-running browsers from ones that have since closed. Follow next_st…

Show 48 more tools
  • pdl_check_setup

    Diagnose pwa-debug + chrome-devtools-mcp setup and return { ok, gaps[], recommendations[], detail }. Checks: chrome-devtools-mcp registration (read from the `claude` CLI via `claude mcp get`) AND that its configured --browserUrl matches the active managed debug port (or launch.defaultPort) — flagging both not-registered and registered-at-the-wrong-port; native-messaging host manifest installed for a detected browser, the per-profile manifest present under each ACTIVE sandbox launch's <user-data-dir>/NativeMessagingHosts/ (a custom --user-data-dir searches there, not the install location), bund…

  • pdl_close_browser

    Cleanly close a browser that pdl_launch_browser started — the symmetric counterpart to launch. Operates STRICTLY off the managed-launch registry, so it can NEVER touch your own/normal browser: a launch we only ATTACHED to (didn't spawn) is detached from the registry, never killed. Shutdown prefers a clean CDP Browser.close (no 'restore tabs' crash prompt), falling back to SIGTERM then SIGKILL of the spawned process. Args: target by browser, port, or pid (or all:true for every managed launch); session? = 'persist' (default — keep the profile dir), 'discard' (also delete the sandbox profile dir;…

  • pdl_install_extension

    Copy the bundled pwa-debug extension to a folder for manual unpacked install in a Chromium browser. Args: target? (destination dir; defaults to ~/Downloads/pwa-debug-extension). Returns { source, dest } and step-by-step chrome://extensions Developer-mode 'Load unpacked' instructions in next_steps, plus the host_register_extension follow-up. Errors with build guidance if the bundled extension isn't present. Note: pdl_launch_browser sandbox-persistent/sandbox-temp preload the extension automatically, so this tool is only needed for installing into the user's normal (existing-mode) profile.

  • pdl_launch_browser

    Launch or attach to a Chromium-family browser with a live remote-debugging port, for use alongside chrome-devtools-mcp. Modes: mode='existing' (default) targets the user's normal profile and degrades gracefully — (a) port already live → attach; (b) running without a debug port → opens a NEW WINDOW in the existing session (never kills it), attached:false + degradation message; (c) not running → spawns fresh with --remote-debugging-port + --user-data-dir=<your profile>. mode='sandbox-persistent' spawns a dedicated, persistent dev profile at ~/.pwa-debug/profiles/<browser>/ beside your normal bro…

  • pdl_navigate

    Navigate a browser tab to a URL, driven through the pwa-debug extension's service worker (chrome.tabs.update) — NO CDP, so it works on the user's real, logged-in profile without chrome-devtools-mcp being registered or attached. Targets the active tab in the last-focused window unless tab_id is given. The URL may omit the scheme (https:// is assumed); a javascript: URL is rejected. Waits for the page to reach document 'complete' (up to timeout_ms, default 10000, max 12000) before returning { extensionId, tabId, url, status, windowId? }, where status is 'complete' or 'loading' (load still in fli…

  • pdl_new_tab

    Open a NEW browser tab at a URL via the pwa-debug extension's service worker (chrome.tabs.create) — NO CDP, works on the user's real profile with no chrome-devtools-mcp dependency. The URL may omit the scheme (https:// is assumed); a javascript: URL is rejected. active? controls foreground vs. background (defaults to foreground). Waits for the page to reach document 'complete' (up to timeout_ms, default 10000, max 12000) before returning { extensionId, tabId, url, status, windowId?, created:true }. Pass the returned tabId to other pwa-debug tools to target this tab specifically. Args: { url: n…

  • pdl_register_chrome_devtools

    Register the separate, optional chrome-devtools-mcp server with Claude Code on the user's behalf, pinned to the active debug port. Runs `claude mcp add chrome-devtools --scope user -- npx -y chrome-devtools-mcp@latest --browserUrl http://127.0.0.1:<port>` via the `claude` CLI. Port resolves to: the `port` arg if given, else the active managed launch port (from pdl_launch_browser), else the host launch.defaultPort. Idempotent: no-op when already registered at the correct port; removes + re-adds when registered at the wrong port. IMPORTANT: this MUTATES the user's global (user-scope) MCP config …

  • popup_failures

    Surface auth/connect FAILURES from library popups for a target extension. Correlates each tracked popup's in-widget failure (PopupState.failure/alerts captured by popup_tail's producer) with the console errors and failed network requests (fetch/xhr phase 'error' | status>=400 | status===0, websocket 'error') that fired during that popup's open window, matched by frameKey. Returns { reports: PopupFailureReport[] }, each: { popupId, library, detection, frameKey, role=primary|nested, parentPopupId, reason?, alerts?, window{from,to,open}, console[{level,text,ts,sequenceNumber}], network[{kind,url?…

  • popup_record

    Bounded, intent-driven recording of the library_popup event stream for a target extension. action='start' subscribes to the extension's capture intake and buffers EVERY library_popup event (primary + nested, in arrival order) in memory — immune to ring-buffer eviction — until action='stop', which persists the stream to <config>/pwa-debug/popup-recordings/<label>/events.jsonl (+ meta.json) and returns { path, count }. action='status' reports the active recording { active, label, startedAt, count }. Forward-only: only events between start and stop are recorded — start with intent, perform the in…

  • popup_replay

    Read back a popup recording captured by popup_record. With no label, lists available recordings on disk ({label, count, startedAt, stoppedAt}). With a label, projects the recorded library_popup events three ways via `mode`: 'primary' (default — only role!=='nested' events, one stream per logical widget; paginated by offset/limit), 'flat' (the raw full-fidelity sequence including nested components; paginated), or 'tree' (a hierarchy of popup nodes — primary roots with nested children attached by parentPopupId, each node summarizing popupId/role/library/detection/host/phases). Reads from <config…

  • popup_tail

    Tail the host-side library_popup ring buffer (injected library widgets/popups: WalletConnect, RainbowKit, ConnectKit, Privy, and generic shadow/portal overlays) for a target extension with cursor pagination + FilterSpec. Returns { entries: PopupEntry[]; cursor: Cursor|null; hasMore: bool }. Each PopupEntry carries page-world fields (ts, frameUrl, frameKey, popupId, phase=appeared|updated|disappeared, detection=shadow|portal, library tag ('unknown' when no signature matched), host{tagName, id?, classes?, selector}, role=primary|nested, parentPopupId, and on appeared/updated a state snapshot {vi…

  • pwa_installability

    Diagnose whether the debugged PWA is installable, with actionable gaps instead of 'manifest invalid'. Fetches + parses the web app manifest and checks: manifest present/valid, name/short_name, start_url, app display mode, icons (192 AND 512 AND a maskable purpose), secure context (HTTPS/localhost), and a registered service worker. Returns InstallabilityResult { installable, manifestUrl, manifestFound, secureContext, hasServiceWorker, manifest, gaps: [{ code, severity('error' blocks / 'warning' recommended), message, fix }] }. Each gap names exactly what's wrong and how to fix it. Reads your re…

  • pwa_snapshot

    Capture ONE capped runtime-state blob of the debugged PWA for deterministic bug-repro context. Returns RuntimeSnapshot { url, title, capturedAt, sw (service-worker status), store (auto-detected Redux/Pinia/Jotai/Zustand state, value-capped, or null), webStorage: { local, session }, idb (IndexedDB db/store schema — not records), cacheNames (CacheStorage names + counts) }. Composes the existing sw_status / store_get_state / storage_get / idb_list / cache_list reads into one moment-in-time record you can reason over or hand off to reproduce a bug. Read-only; no new capture surface. For deeper det…

  • pwa_status

    Snapshot the debugged PWA's runtime status + capability matrix. Returns PwaStatusSnapshot { displayMode, standalone (installed?), controlledBySW + controllerScriptURL, permissions: { notifications, push, periodicBackgroundSync }, capabilities: { serviceWorker, pushManager, backgroundSync, periodicBackgroundSync, badging, fileSystemAccess, windowControlsOverlay, webShare, notifications } }. capabilities = live feature-detection in THIS browser (answers 'why does push work on Android but not here'); permissions = current Permissions-API grants. One cheap call assembles what DevTools makes you ga…

  • pwa_update_analyze

    Diagnose service-worker update propagation + version skew for the debugged PWA. Composes sw_status (waiting worker + controller), CacheStorage entry ages (cached HTML vs JS), and recent network 404s into UpdateAnalysisResult { supported, hasWaitingUpdate, controller, findings: [{ code, severity, message }], cachedHtml, cachedJs, chunk404s, summary }. Detects: waiting_update_active_client (an installed SW is waiting while this client stays on the old worker — 'my update won't show'), html_older_js (stale cached HTML referencing chunk hashes the newer JS dropped), and chunk_404 (live chunk misse…

  • react_find_by_role

    Find React components whose rendered host node has a given ARIA role, optionally narrowed by an accessible-name regex. Args: { extension_id?, tab_id?, role: required ARIA role string (e.g. 'button','link','heading','navigation','region','textbox','checkbox','img' — matched against an explicit role attribute or a simplified implicit-role mapping; exact, lowercase), name?: regex source string matched against the element’s accessible name (aria-label > first aria-labelledby ref > text content; compiled with new RegExp, no flags), root_index?: limit to one React root (default: all), max_matches?: …

  • react_find_by_text

    Find React components whose rendered host-node text matches a regex. Args: { extension_id?, tab_id?, pattern: required regex source string (compiled with new RegExp — no flags, case-sensitive), exact?: bool=false (true = regex must match the FULL trimmed text of the component’s host node; false = substring match), root_index?: limit to one React root (default: all), max_matches?: cap (default 20, max 500) }. Returns { extensionId, tabId, matches: { stableId, displayName, key?, matchedText }[], truncated, rootCount }. Feed any stableId into react_get_state for that component’s props/state/hooks…

  • react_get_state

    Return the props, state, and hooks of a single React component identified by stable_id (obtained from a prior react_tree call). Args: { extension_id?, tab_id?, stable_id: required non-empty string, root_index?: number=0 (must match the root used to compute the id), include_props?: bool=true, include_hooks?: bool=true }. Returns { extensionId, tabId, stableId, displayName, key?, props?, state?, hooks?: SerializedHook[], truncated? }. SerializedHook entries: { type: 'state'|'memo'|'effect'|'ref'|'custom'; index; value?; deps?; truncated? }. Tool-level error when stable_id no longer resolves — in…

  • react_tree

    Return the React component tree of the active (or specified) tab as a structured ReactTreeNode[]. Each node has { stableId, displayName, key?, hasState, hasHooks, children } where stableId is a re-render-stable component identity (path + displayName + key/index). Args: { extension_id?, tab_id?, root_index?: pick one root (default: all), depth_limit?: max depth (default 8, cap 64), max_nodes?: total cap (default 200, cap 5000) }. Returns { extensionId, tabId, roots, truncated, rootCount }. truncated:true means the walk hit depth_limit or max_nodes; re-call with a tighter root_index or higher ca…

  • recent_events

    Returns recent CapturedEvents from the extension's SW-side ring buffer (in-memory, lost on SW restart; M11 will add cross-restart persistence). Each event has shape: { kind, ts, frameUrl, frameKey, ...kind-specific-fields } where kind is 'console' (currently) or 'fetch'/'xhr'/'websocket' (M9 Task 8). Filters: kinds=['console','fetch'] restricts to listed kinds; since_ms is a strict greater-than ts cutoff; limit caps the result to the most-recent N (default 50, hard max = SW bufferSize). Returns { extensionId, events, stats: { totalReceived, perKind, bufferSize } }. With no extension_id, target…

  • redux_dispatch

    DEPRECATED — prefer store_dispatch (unified, framework auto-detect). Dispatch an action into the active Redux store (the only WRITE surface in the store-introspection family). DISABLED BY DEFAULT — opt in via settings.set { key: 'capture.stores.allowDispatch', value: true }. Args: { extension_id?, tab_id?, action: { type: non-empty string; payload? } }. Returns { extensionId, tabId, dispatched: true, action, scopeUrl } on success. Tool-level errors (no store detected; user reducer threw) follow the same { error: { message } } convention as redux_get_state. CALL host_status FIRST to see connect…

  • redux_get_state

    DEPRECATED — prefer store_get_state (unified, framework auto-detect). Return the current Redux store state from the active tab, optionally pruned to a dotted/bracket path. Args: { extension_id?, tab_id?, path?: 'counter.value' | 'todos[0].text' | "users['by-id']" }. Returns { extensionId, tabId, state, path?, truncated?, scopeUrl }. Runs in page-world via the page-bridge — no CDP, coexists with the user's DevTools. CALL host_status FIRST to see connections.

  • redux_subscribe

    DEPRECATED — prefer store_subscribe (unified, framework auto-detect). Start or stop a Redux store subscription on the active tab. While active, each store.subscribe callback whose path-narrowed snapshot differs from the prior snapshot emits a store_change event that flows through the standard capture pipeline; read accumulated events via redux_tail. Args: { extension_id?, tab_id?, action: 'start' | 'stop', path?: 'counter' | 'todos.items' }. Returns { extensionId, tabId, active, path?, scopeUrl }. Single subscription per page-world; calling action='start' again replaces any prior subscription.…

  • redux_tail

    DEPRECATED — prefer store_tail (unified, framework-tagged entries). Tail the host-side store_change ring buffer (populated while redux_subscribe is active) for a target extension. Returns { entries: StoreChangeEntry[]; cursor: Cursor|null; hasMore: bool }. Each StoreChangeEntry carries page-world fields (ts, frameUrl, frameKey, storeId, path?, diff{added, changed, removed}, snapshot, truncated?) plus host fields (receivedAt, sessionId, extensionId, sequenceNumber, cursor). FilterSpec (all optional): pattern={include?: regex sources[], exclude?: regex sources[]}; since/until=opaque cursor strin…

  • session_ping

    Sends a ping through the full MCP → IPC → NMH → SW → CS → page-world chain and returns round-trip metadata: { hostVersion, extensionVersion, attachedTabId, extensionId, latencyMs, pageWorld, pageWorldError?, pageWorldErrorMessage?, pageWorldSelfHealed? }. pageWorld is { url, title, readyState } read live from the active tab's MAIN-world page-world script. pageWorld is null with pageWorldError set (a typed code: cs_not_attached_refresh_tab | page_blocks_scripts | page_world_blocked | restricted_url | no_active_tab | cs_inject_failed) when the SW round-trip succeeded but the page-bridge half fai…

  • session_record

    Start or stop a rrweb session recording on the active tab. Each recorded event flows through the capture pipeline as a 'replay' CaptureKind and accumulates in the host replay ring buffer (readable via session_replay). Args: { extension_id?, tab_id?, action: 'start' | 'stop', session_id?: stable id for grouping (auto-generated when missing), duration_cap_ms?: int > 0 (auto-stop deadline) }. Single recording per page-world; action='start' replaces any prior recording. CALL host_status FIRST to see connections.

  • session_replay

    Tail the host-side replay ring buffer (populated while session_record is active) for a target extension. Returns { entries: ReplayEntry[]; cursor; hasMore }. Each ReplayEntry carries page-world fields (ts, frameUrl, frameKey, sessionId, rrwebType, data, timestamp) plus host fields (receivedAt, sessionId, extensionId, sequenceNumber, cursor). FilterSpec: pattern (regex over JSON.stringify), since/until cursors, limit. level is ignored. Call session_record(action='start') first to start producing events.

  • settings_get

    Reads host settings. Omit key to receive every value. Provide key to receive { value, entry } where entry is the wire-safe schema metadata (type, default, scope, description, enumValues). Unknown keys return an error referencing settings_list_schema.

  • settings_list_schema

    Returns every user-tunable host setting as data: key, runtime type tag (number | boolean | string[] | enum[]), default, consuming scope (host | extension | both), human description, and enumValues (for enum[] entries). Order is stable schema-declaration order. Use this BEFORE settings_set so the value shape matches the entry type — settings_set rejects invalid shapes with a schema-contextualized error.

  • settings_set

    Writes a single host setting. Validates the value against the per-key schema validator (rejects with a schema-contextualized error). On accept: atomic-persists to ~/.config/pwa-debug/settings.json and notifies in-process subscribers. Unknown keys and invalid values are rejected with a next_step pointing at settings_list_schema.

  • solid_detect

    Detect SolidJS on the active (or specified) tab. Args: { extension_id?, tab_id? }. Returns { extensionId, tabId, present, devtoolsHook, hydration, delegatedEventCount }. Solid has no virtual DOM and no persisted component tree, so unlike React/Vue there is NO solid_components or solid_get_state — detection is best-effort (the @solid-devtools hook window.__SOLID_DEVTOOLS__, the _$HY hydration global, and a heuristic count of elements carrying Solid's $$-delegated-event props). devtoolsHook:true means @solid-devtools is installed (deeper data may be reachable). Use solid_find_by_text / solid_fin…

  • solid_find_by_role

    Find DOM ELEMENTS with a given ARIA role on a Solid page, optionally narrowed by an accessible-name regex. Args: { extension_id?, tab_id?, role: ARIA role string (exact, lowercase), name?: regex source (new RegExp, no flags), max_matches?: cap (default 20, max 500) }. Returns { extensionId, tabId, matches: { locator, tag, role, name? }[], truncated }. NOTE: Solid has no component identity, so matches are ELEMENTS (locator = best-effort CSS-ish selector), NOT components — documented Solid degradation. Runs in page-world via the page-bridge. CALL host_status FIRST.

  • solid_find_by_text

    Find DOM ELEMENTS whose rendered text matches a regex on a Solid page. Args: { extension_id?, tab_id?, pattern: regex source (new RegExp, no flags, case-sensitive), exact?: bool=false, max_matches?: cap (default 20, max 500) }. Returns { extensionId, tabId, matches: { locator, tag, matchedText }[], truncated }. NOTE: Solid has no component identity, so matches are ELEMENTS (locator = best-effort CSS-ish selector), NOT components — this is the documented Solid degradation. Runs in page-world via the page-bridge. CALL host_status FIRST.

  • source_map_resolve

    Resolve a single generated stack frame (script_url + line + column) to its original-source location using the script's source map. Args: { extension_id?, tab_id?, script_url: non-empty string, line: int >= 1, column: int >= 0 }. Returns { extensionId, tabId, original?: { source, line, column, name? }, scopeUrl }. original is undefined when no map is available or no mapping exists at the requested coordinates. Disabled via capture.sourceMap.enabled=false. M13 ships query-time resolution; M13.5 will add capture-time auto-annotation when needed.

  • storage_get

    Snapshot the debugged PWA's web storage. Returns StorageGetResult { supported, area, entries: [{ key, value, truncated? }], entryCount, truncated } read from the page's localStorage or sessionStorage. Use to inspect auth tokens, feature flags, cached app state, and 'why is the app in this state' bugs that live in storage. Reads your REAL profile's storage; CDP/chrome-devtools-mcp does not surface this. Values over 8KB are truncated. For structured/large data the app keeps in IndexedDB, use idb_list + idb_query instead. Args: { extension_id?, tab_id?, area?: 'local' (default) | 'session', limit…

  • store_dispatch

    Dispatch an action into the active tab's store (the only WRITE surface in the store-introspection family). DISABLED BY DEFAULT — opt in via settings.set { key: 'capture.stores.allowDispatch', value: true }. Auto-detects the framework or pass framework to force one. Args: { extension_id?, tab_id?, framework?, action: { type: non-empty string; payload? } }. Returns { extensionId, tabId, dispatched: true, framework, action, scopeUrl }. Note: stores without a Redux-style dispatch (e.g. some Zustand/Jotai setups) return a tool-level error from the page-world handler. CALL host_status FIRST to see c…

  • store_get_state

    Return the current state of the active tab's JS store, optionally pruned to a dotted/bracket path. Auto-detects the store framework (Redux today; Zustand/Pinia/Jotai as adapters land), or pass framework to force one. Args: { extension_id?, tab_id?, path?: 'counter.value' | 'todos[0].text', framework?: 'redux' }. Returns { extensionId, tabId, framework, state, path?, truncated?, scopeUrl }. Runs in page-world via the page-bridge — no CDP, coexists with the user's DevTools. CALL host_status FIRST to see connections.

  • store_subscribe

    Start or stop a store subscription on the active tab. While active, each store change whose path-narrowed snapshot differs from the prior snapshot emits a store_change event (tagged with the detecting framework) that flows through the standard capture pipeline; read accumulated events via store_tail. Auto-detects the framework or pass framework to force one. Args: { extension_id?, tab_id?, framework?, action: 'start' | 'stop', path?: 'counter' | 'todos.items' }. Returns { extensionId, tabId, active, framework?, path?, scopeUrl }. Single subscription per page-world; calling action='start' again…

  • store_tail

    Tail the host-side store_change ring buffer (populated while store_subscribe is active) for a target extension. Framework-agnostic: each entry's framework field names the store library that produced it. Returns { entries: StoreChangeEntry[]; cursor: Cursor|null; hasMore: bool }. Each StoreChangeEntry carries page-world fields (ts, frameUrl, frameKey, storeId, framework?, path?, diff{added, changed, removed}, snapshot, truncated?) plus host fields (receivedAt, sessionId, extensionId, sequenceNumber, cursor). FilterSpec (all optional): pattern={include?: regex sources[], exclude?: regex sources[…

  • svelte_components

    List the Svelte components rendered on the active (or specified) tab. Args: { extension_id?, tab_id? }. Returns { extensionId, tabId, present, dev, metaElementCount, components: { stableId, file, firstLoc?, elementCount }[] }. Svelte compiles components to closures with no instance tree, so discovery uses the dev-only __svelte_meta source tags: each component == one .svelte file (the file path is its stableId), and elementCount is how many rendered elements belong to it. dev:false means a production build (no __svelte_meta) → components is empty. There is NO svelte_get_state (Svelte exposes no…

  • svelte_find_by_role

    Find Svelte components whose rendered DOM node has a given ARIA role, optionally narrowed by an accessible-name regex. Args: { extension_id?, tab_id?, role: ARIA role string (exact, lowercase; explicit role attr or simplified implicit mapping), name?: regex source for the accessible name (new RegExp, no flags), max_matches?: cap (default 20, max 500) }. Returns { extensionId, tabId, matches: { stableId, file, role, name? }[], truncated }. Matches map to the owning component .svelte file (stableId === file), de-duped per file. DEV-mode only (relies on __svelte_meta). No state read exists for Sv…

  • svelte_find_by_text

    Find Svelte components whose rendered DOM text matches a regex. Args: { extension_id?, tab_id?, pattern: regex source (new RegExp, no flags, case-sensitive), exact?: bool=false (full-text vs substring), max_matches?: cap (default 20, max 500) }. Returns { extensionId, tabId, matches: { stableId, file, matchedText }[], truncated }. Matches map to the owning component .svelte file (stableId === file), de-duped per file. DEV-mode only (relies on __svelte_meta); empty on production builds. No state read exists for Svelte. Runs in page-world via the page-bridge. CALL host_status FIRST.

  • sw_lifecycle_tail

    Tail the DEBUGGED PWA's service-worker lifecycle event stream (kind 'sw_state') with cursor pagination + FilterSpec. Returns { entries: SwStateEntry[]; cursor: Cursor|null; hasMore: bool }. Each SwStateEntry: { subkind, scope?, scriptURL?, state?, slot? } + host fields (receivedAt, sessionId, extensionId, sequenceNumber, cursor). subkind: 'updatefound' = a new worker started installing; 'statechange' = a worker advanced lifecycle state (installing→installed→activating→activated→redundant); 'controllerchange' = the page's controlling worker changed. This is the EVENT STREAM (transitions over ti…

  • sw_status

    Inspect the DEBUGGED PWA's service worker(s). Returns SwStatusSnapshot { supported, controller, registrations[], hasWaitingUpdate } read from the page's navigator.serviceWorker — the installing/waiting/active worker for each registration (with script URLs + lifecycle state), which worker controls the page, and whether an update is stuck WAITING (the #1 'why isn't my update showing' signal). Reads the app's real SW state in your actual browser profile — something CDP / chrome-devtools-mcp does not surface. Args: { extension_id?, tab_id? }. Runs in page-world via the page-bridge (no CDP). For th…

  • vue_find_by_role

    Find Vue 3 components whose rendered DOM node has a given ARIA role, optionally narrowed by an accessible-name regex. Args: { extension_id?, tab_id?, role: required ARIA role string (e.g. 'button','link','heading','navigation','region','textbox','checkbox','img' — matched against an explicit role attribute or a simplified implicit-role mapping; exact, lowercase), name?: regex source string matched against the element's accessible name (aria-label > first aria-labelledby ref > text content; compiled with new RegExp, no flags), root_index?: limit to one Vue root (default: all), max_matches?: cap…

  • vue_find_by_text

    Find Vue 3 components whose rendered DOM text matches a regex. Args: { extension_id?, tab_id?, pattern: required regex source string (compiled with new RegExp — no flags, case-sensitive), exact?: bool=false (true = regex must match the FULL trimmed text of a rendered element; false = substring match), root_index?: limit to one Vue root (default: all), max_matches?: cap (default 20, max 500) }. Returns { extensionId, tabId, matches: { stableId, displayName, key?, matchedText }[], truncated, rootCount }. Matching elements are mapped to their owning component (one entry per component). Feed any s…

  • vue_get_state

    Return the props, setup() bindings, and options-API data of a single Vue 3 component identified by stable_id (obtained from a prior vue_tree call). Args: { extension_id?, tab_id?, stable_id: required non-empty string, include_props?: bool=true, include_state?: bool=true }. Returns { extensionId, tabId, stableId, displayName, key?, props?, setupState?, data?, truncated? }. setupState holds <script setup>/setup() bindings (refs auto-unwrapped); data holds options-API reactive state. Empty surfaces are omitted. Tool-level error when stable_id no longer resolves — re-call vue_tree to refresh ids. …

  • vue_tree

    Return the Vue 3 component tree of the active (or specified) tab as a structured VueTreeNode[]. Each node has { stableId, displayName, key?, hasProps, hasState, children } where stableId is a re-render-stable component identity (path + name + key/index). Args: { extension_id?, tab_id?, root_index?: pick one root (default: all), depth_limit?: max depth (default 8, cap 64), max_nodes?: total cap (default 200, cap 5000) }. Returns { extensionId, tabId, roots, truncated, rootCount }. truncated:true means the walk hit depth_limit or max_nodes; re-call with a tighter root_index or higher caps. Pass …

Signals

Stars
0
Forks
0
Open issues
0
Weekly downloads
20
Last commit
Jun 26, 2026 (4 months ago)
Last release
Jun 26, 2026 (4 months ago)
Last package publish
Jun 26, 2026 (4 months ago)

From GitHub (fetched Oct 24, 2026) and the package registry.

Details

Registry name
io.github.aryanduntley/pwa-debug-layer
Version
0.2.0
Auth
No auth
License
MIT
First seen
Oct 24, 2026
Last checked
Oct 25, 2026

More from aryanduntley

Cite this page

PWA Debug Layer MCP server. Agent Mart. https://agentmart.dev/connectors/aryanduntley-pwa-debug-layer (last updated 2026-10-25)