PWA Debug Layer
NPM · @ARYANDUNTLEY/PWA-DEBUG
Debug PWAs in your real browser via MCP: service-worker, cache, installability & framework state.
Available components
Last scanned (6 hours ago)
Scores higher than 81% of npm servers
Category Browser Automation
How this component scores in each security and reliability category. Every signal is checked automatically from public evidence about the published package, including repeated runs of it in an isolated sandbox, and we only credit what we can confirm. How we score → Why this is hard to score →
Supply Chain Security81
- No malware found by supply-chain analysis.Pass
- CVE check failed: a known high-severity CVE affects zod 3.25.76, a direct dependency. A fixed version is available. View diagnostics → Fail
- No install/post-install scripts declared.Pass
- 31 of 94 dependencies flagged as unhealthy. View diagnostics → Partial
Provenance & Transparency97
- Source repository is publicly reachable at the declared URL. View diagnostics → Pass
- Cryptographically verified build provenance (signed, bound to aryanduntley/pwa-debug-layer). View diagnostics → Pass
- Clear OSI-approved license (MIT).Pass
- Actively maintained (last published 2 days ago).Pass
- Disclosure check failed: no security disclosure policy was found in the source repository. See how to fix → Fail
Schema Quality & AI Usability66
- AI-judged instruction clarity (excellent).Pass
- Context-footprint check failed: tool/resource definitions use about 19928 tokens (~218/item across 91 items; 91 tools + 0 resources), over budget; trim descriptions and params. See how to fix → Fail
- Usage-examples check failed: none of the tools include examples. See how to fix → Fail
Stability & Change Management100
- No destabilizing schema changes in the last 30 days.Pass
Tool Coverage68
- 100% of tools have a non-trivial description (not blank, and not just the tool's name).Pass
- 3% of tool parameters carry a description.Partial
Tool Safety75
- No prompt-injection markers were found in the server instructions, tool names or descriptions we captured.Pass
- 0 of 2 tool(s) whose name or description implies an irreversible operation declare an MCP destructiveHint annotation; "host_unregister_extension" implies "remove" and declares no destructiveHint at all, which the MCP spec reads as destructive by default. See how to fix → Fail
- An AI judge read all 92 captured unit(s) of tool text and found none that tries to manipulate the model reading it.Pass
Capabilities100
- Implements a supported MCP spec version (2025-11-25); the latest is 2026-07-28.Pass
Every change we have recorded for this component, newest first. Security-relevant changes are always shown. ▲ marks a change for the better, ▼ a change for the worse; unmarked changes are neutral.
- 3 Nov 26 +21
- Known CVEs: unverified → fail ▼ security
- Malware scan: unverified → pass ▲ security
- Dependency health: unverified → 0.84 ▲ functional
- 2 Nov 26 −21
- Known CVEs: fail → unverified ▼ security
- Malware scan: pass → unverified ▼ security
- Dependency health: 0.84 → unverified ▼ functional
- Package version: 1.0.0 → 1.2.0 functional
- Package version: 1.0.0 → 1.1.0 functional
- 1 Nov 26 0
- Stability: pass → unverified ▼ security
- Tool safety: pass → unverified ▼ security
- Capabilities: pass → unverified ▼ functional
- Tool coverage: 100 → unverified ▼ functional
- Package version: 0.2.0 → 1.0.0 functional
- 27 Oct 26 +1
- Stability: 0.97 → pass security
- 25 Oct 26 −1
- Stability: pass → 0.93 functional
- 24 Oct 26 −5
- CVE-2023-54404 affects this package: high ▼ security
- Known CVEs: pass → fail ▼ security
- 17 Oct 26 +2
- Stability: 0.90 → pass security
- 15 Oct 26 +1
No change was recorded against any check on this day. Stability & Change Management went from 83 to 87. That category is still filling its 30-day observation window: 25 days of observed history at the previous scan, 26 at this one. The score rises as the window fills, whether or not the server changes.
Diagnostic detail from the automated scan of this channel: what the scanner observed at each step, so you can see exactly where a check passed or failed. It is informational only and never changes the trust score.
Captured 5 Nov 2026 · Analysed npm/@aryanduntley/pwa-debug@1.2.0
Provenance Verified
A signed build attestation was found and verified, binding this exact artifact to the source repository it claims to come from.
| Result | Verified |
|---|---|
| Ecosystem | npm |
| Reason | Verified |
| Discovered via | Registry attestation endpoint |
| Source repo | aryanduntley/pwa-debug-layer |
| Certificate issuer | https://token.actions.githubusercontent.com |
| Certificate SAN | https://github.com/aryanduntley/pwa-debug-layer/.github/workflows/publish.yml@refs/tags/v1.2.0 |
| Rekor log index | 3647734098 |
| Predicate type | SLSA build provenance https://slsa.dev/provenance/v1 |
| Subject digest | sha512:4e41560ab554f0b4a6ef9a99615c853f4d32fd6219723ff148117abbf417c0e588613186aa9ed44541eedf7828532c48a1e7654bce8f2be34c2e90df5 |
Background: How many MCP packages publish verified provenance →
Vulnerabilities 1 finding
| ID | CVE | Severity | Vector | Fix available |
|---|---|---|---|---|
| GHSA-mrhx-c7xc-m3w5 | CVE-2023-54404 | high | CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H | yes |
Background: What a vulnerability scan can and cannot prove →
Dependencies 94 packages
| Packages resolved | 94 |
|---|---|
| Stale | 31 |
| Tree resolution | Complete |
Background: SBOMs and build attestations, explained →
The tools this component advertises to a client, with an estimated token cost for each. Expand a tool to see its parameters and schema. The per-tool counts are indicative and are not scored directly; the schema's total context footprint is one signal in Schema Quality & AI Usability. A tool's description is untrusted text the model reads on every call, which is what makes this list a security surface and not just an inventory: how tool poisoning works →
cache_inspect ~194
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; CDP cannot do this. CALL host_status FIRST.
| Name | Type | Req | Description |
|---|---|---|---|
| cache_name | string | yes | – |
| extension_id | string | – | – |
| limit | integer | – | – |
| tab_id | integer | – | – |
No output schema declared.
No examples provided.
cache_list ~153
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.
| Name | Type | Req | Description |
|---|---|---|---|
| extension_id | string | – | – |
| tab_id | integer | – | – |
No output schema declared.
No examples provided.
cache_match ~176
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-world read. CALL host_status FIRST.
| Name | Type | Req | Description |
|---|---|---|---|
| extension_id | string | – | – |
| tab_id | integer | – | – |
| url | string | yes | – |
No output schema declared.
No examples provided.
console_tail ~224
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|warn|error|debug|trace); pattern={include?: regex sources[], exclude?: regex sources[]}; since/until=opaque cursor strings; limit=int 1..1000 (default 200). Errors carry kind in next_steps so AI can self-correct: cursor_invalid, cursor_session_mismatch, pattern_invalid, limit_invalid.
| Name | Type | Req | Description |
|---|---|---|---|
| extension_id | string | – | – |
| filter | object | – | – |
No output schema declared.
No examples provided.
error_tail ~283
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 rejected promises (including wallet/connect rejections that bubble) without the app having to log them. NOTE: errors an app fully handles in try/catch do NOT surface here. With no extension_id, targets the single connected NMH (errors if zero or multiple). FilterSpec (all optional): pattern.include/exclude=regex sources matched against each entry's JSON (e.g. include:['rejected','cancelled']); since/until=opaque cursors; limit=int 1..1000 (default 200); level is ignored (page errors have no level field).
| Name | Type | Req | Description |
|---|---|---|---|
| extension_id | string | – | – |
| filter | object | – | – |
No output schema declared.
No examples provided.
evaluate ~311
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: ... } tags; cycles become { __type: 'Cycle' }). When await_promise=true and the expression returns a thenable, races resolution against timeout_ms. error{message,stack?} is populated for syntax errors, sync throws, async rejects, and timeouts — the call still returns ok:true so AI can introspect failure detail. Expression mode only (single expression, no statements; same as DevTools console expression eval). With no extension_id/tab_id, targets the single connected NMH and the active tab. CALL host_status FIRST to see which extensions are connected.
| Name | Type | Req | Description |
|---|---|---|---|
| await_promise | boolean | – | – |
| expression | string | yes | – |
| extension_id | string | – | – |
| tab_id | integer | – | – |
| timeout_ms | integer | – | – |
No output schema declared.
No examples provided.
host_list_registrations ~51
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.
Input schema present but exposes no named parameters.
No output schema declared.
No examples provided.
host_register_extension ~211
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 `[pwa-debug/sw] id=<id>` on every boot. NEVER invent an ID. After this returns requiresReload:true, the user must reload the extension at chrome://extensions for Chrome to re-validate allowed_origins. If next_steps mentions a flatpak caveat, surface it to the user verbatim.
| Name | Type | Req | Description |
|---|---|---|---|
| extension_id | string | yes | – |
No output schema declared.
No examples provided.
host_reset ~77
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.
| Name | Type | Req | Description |
|---|---|---|---|
| confirm | string | yes | – |
No output schema declared.
No examples provided.
host_status ~98
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.
Input schema present but exposes no named parameters.
No output schema declared.
No examples provided.
host_unregister_extension ~98
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.
| Name | Type | Req | Description |
|---|---|---|---|
| extension_id | string | yes | – |
No output schema declared.
No examples provided.
idb_list ~172
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_status FIRST.
| Name | Type | Req | Description |
|---|---|---|---|
| extension_id | string | – | – |
| tab_id | integer | – | – |
No output schema declared.
No examples provided.
idb_query ~199
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. CALL host_status FIRST.
| Name | Type | Req | Description |
|---|---|---|---|
| db | string | yes | – |
| extension_id | string | – | – |
| limit | integer | – | – |
| store | string | yes | – |
| tab_id | integer | – | – |
No output schema declared.
No examples provided.
layout_sweep ~727
Responsive layout audit across a view matrix: pages × viewport widths × color schemes × app states. For every view it emulates the viewport and prefers-color-scheme over CDP on the real top-level page, applies the app state, settles, and measures in the page: sideways page overflow, elements off screen (scroll rows and carousels skipped automatically), tap targets under the minimum (WCAG 2.2 default 24px; set checks.tap_target.min 44/48 for platform guidelines), fixed bars not fully on screen, cut-off text, text or fields touching a visible edge of their box, outline/shadow rings cut off by a scrolling or clipping parent, unresolved copy ({{x}}, undefined, NaN, [object Object], plus checks.text_patterns.extra), page errors, and optionally contrast. Results are grouped into distinct problems, not raw rows. Runs in the background: returns { job_id, planned_views, target } at once; poll layout_sweep_status. TARGETS: phones (pdl_android_attach connection) and browsers started by pdl_launch_browser (extension connection; the host uses the launch's debug port). Other connections are refused. Args: { extension_id?, tab_id?, pages: absolute URLs or paths with base_url, base_url?, viewports?: [{ width, height?=800, dpr?=2, mobile?=true }] (default 360/390/430; phones also check their own size first), color_schemes?: ['light','dark'] default, states?: { discover?: expression → string[] of state keys, apply?: expression with `view` bound ({ state, scheme, width, height, page, ...dimensions }), hash_template?: '#state={state}&lang={lang}', dimensions?: { lang: ['en','es'] }, reload?: reload after applying }, root_selector?, settle?: { wait_ms?=80, wait_for?: expression, freeze_animations?=true }, checks?: each check true/false or its knobs, custom_checks?: [{ name: snake_case, expression }] (up to 20; your own checks, run in the page after the built-ins for every view with `view` (page, width, height, scheme, state, dimensions) and `pdl` helpers (elements(selector?) visible no…
| Name | Type | Req | Description |
|---|---|---|---|
| base_url | string | – | – |
| checks | object | – | – |
| color_schemes | array | – | – |
| custom_checks | array | – | – |
| exempt_selectors | array | – | – |
| extension_id | string | – | – |
| max_views | integer | – | – |
| pages | array | yes | – |
| report_path | string | – | – |
| root_selector | string | – | – |
| screenshots | string | – | – |
| settle | object | – | – |
| states | object | – | – |
| tab_id | integer | – | – |
| view_timeout_ms | integer | – | – |
| viewports | array | – | – |
No output schema declared.
No examples provided.
layout_sweep_cancel ~57
Stop a running layout_sweep after its current view; the viewport, color scheme and starting URL are restored and partial results stay available through layout_sweep_status. Args: { job_id }.
| Name | Type | Req | Description |
|---|---|---|---|
| job_id | string | yes | – |
No output schema declared.
No examples provided.
layout_sweep_status ~139
Progress and results of a layout_sweep job (the latest one when job_id is omitted): status (running | done | cancelled | interrupted | failed), views done / planned, the current view, caveats and warnings, summary { problems, groups, by_check }, and groups: distinct problems most frequent first (page overflow always first), each { check, signature, message, count, pages, where (up to 5 view labels), rect?, screenshot? }, capped at 50 inline. report_path holds every finding and view. Partial results while running. Args: { job_id? }.
| Name | Type | Req | Description |
|---|---|---|---|
| job_id | string | – | – |
No output schema declared.
No examples provided.
network_tail ~274
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, sequenceNumber) plus per-entry cursor. With no extension_id, targets the single connected NMH (errors if zero or multiple). FilterSpec (all optional): pattern={include?: regex sources[], exclude?: regex sources[]} matches against JSON.stringify of each entry; since/until=opaque cursor strings; limit=int 1..1000 (default 200); level applies only to console events and returns empty entries when set on the network buffer; selectors reserved for DOM tail tools. Errors carry kind in next_steps for AI self-correction.
| Name | Type | Req | Description |
|---|---|---|---|
| extension_id | string | – | – |
| filter | object | – | – |
No output schema declared.
No examples provided.
pdl_android_attach ~314
Attach pwa-debug to a page on an Android phone in one call: finds the phone over adb, forwards its browser's DevTools socket, optionally exposes desktop ports to the phone (reverse_ports, so http://localhost:<port> works there), then attaches to an open tab (url_contains / target_id / the only one) or opens a new tab at open_url. Returns a connection id to pass as extension_id to every other pwa-debug tool, so framework, store, PWA, interaction, evaluate, navigation and console/network/error capture all work on the phone. Args: { serial?, socket?, open_url?, url_contains?, target_id?, reload?, reverse_ports? }.
| Name | Type | Req | Description |
|---|---|---|---|
| open_url | string | – | Open a new tab at this URL on the phone and attach it (bridge present before the page loads). |
| reload | boolean | – | Reload the attached tab so the bridge runs before the app code. |
| reverse_ports | array | – | Desktop ports to expose on the phone as localhost:<port> (adb reverse), e.g. [5173] for a dev server. |
| serial | string | – | Device serial from pdl_android_devices; optional when one phone is connected. |
| socket | string | – | DevTools socket name, "chrome", "webview", or an app package. Default: Chrome. |
| target_id | string | – | Attach to this tab id (or prefix). |
| url_contains | string | – | Attach to the open tab whose URL contains this text. |
No output schema declared.
No examples provided.
pdl_android_devices ~105
List Android phones connected over adb, each with its authorization state (device / unauthorized / offline), model, and the DevTools sockets it exposes (Chrome, other Chromium browsers, debuggable app WebViews with their package). Use it before pdl_android_attach, or when attaching fails, to tell the user exactly what to do (install adb, enable USB debugging, accept the authorization prompt, open Chrome). Finds adb on PATH or in the Android SDK. No args.
Input schema present but exposes no named parameters.
No output schema declared.
No examples provided.
pdl_blur ~210
Blur an element. Locator: pass ONE of { selector } | { role, name? } | { text, exact? } | { framework, stable_id }. framework: react|vue|svelte|solid|dom (only meaningful for stable_id; svelte stable_id is a file, not an element — use role/text/selector). Disambiguate multiple matches with nth (0-based) or require_unique. Also extension_id?/tab_id?. CALL host_status FIRST. Runs in page-world via the page-bridge — no CDP, coexists with the user's DevTools.
| Name | Type | Req | Description |
|---|---|---|---|
| exact | boolean | – | – |
| extension_id | string | – | – |
| framework | string | – | – |
| name | string | – | – |
| nth | integer | – | – |
| require_unique | boolean | – | – |
| role | string | – | – |
| selector | string | – | – |
| stable_id | string | – | – |
| tab_id | integer | – | – |
| text | string | – | – |
No output schema declared.
No examples provided.
pdl_browser_status ~141
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_steps[].
Input schema present but exposes no named parameters.
No output schema declared.
No examples provided.
pdl_cdp_attach ~317
Attach pwa-debug to one page over CDP and inject its page-world bridge, so every pwa-debug tool (framework, store, PWA, interaction, evaluate, console/network/error capture, navigation) works on that page exactly as through the extension. Returns a connection id ('cdp:host:port:target') to pass as extension_id to the other tools. Picks the page by target_id, by url_contains, or the single regular page when only one is open. reload:true reloads so the bridge is present before the app starts. Args: { browser_url, target_id?, url_contains?, reload? }. For an Android phone: forward its DevTools socket first with `adb forward tcp:9223 localabstract:chrome_devtools_remote` (run it yourself if you have a shell, otherwise give the user that command), then use browser_url "9223". To load a desktop dev server on the phone, also run `adb reverse tcp:<port> tcp:<port>` so the phone can open http://localhost:<port>.
| Name | Type | Req | Description |
|---|---|---|---|
| browser_url | string | yes | Debug endpoint: a port ('9222'), 'host:port', or an http/ws URL. |
| reload | boolean | – | Reload after attaching so the bridge runs before the app code (needed for hooks that must exist at startup). |
| target_id | string | – | Page target id (or its prefix) from pdl_cdp_targets. |
| url_contains | string | – | Attach to the page whose URL contains this text. |
No output schema declared.
No examples provided.
pdl_cdp_detach ~67
Detach a CDP page connection made by pdl_cdp_attach: removes the injected bridge registration and stops capture for it. Args: { connection_id }.
| Name | Type | Req | Description |
|---|---|---|---|
| connection_id | string | yes | Connection id returned by pdl_cdp_attach ('cdp:...'). |
No output schema declared.
No examples provided.
pdl_cdp_targets ~199
List the pages open on a Chrome DevTools Protocol endpoint (desktop debug port or an Android phone over adb), with the connection id each would get and whether it is already attached. Use it to reach pages the pwa-debug extension cannot run in: Android Chrome tabs, installed PWAs on a phone, or a desktop browser without the extension. Args: { browser_url }. For an Android phone: forward its DevTools socket first with `adb forward tcp:9223 localabstract:chrome_devtools_remote` (run it yourself if you have a shell, otherwise give the user that command), then use browser_url "9223". To load a desktop dev server on the phone, also run `adb reverse tcp:<port> tcp:<port>` so the phone can open http://localhost:<port>.
| Name | Type | Req | Description |
|---|---|---|---|
| browser_url | string | yes | Debug endpoint: a port ('9222'), 'host:port', or an http/ws URL. |
No output schema declared.
No examples provided.
pdl_check ~223
Check a checkbox/radio (idempotent; native click path so onChange fires). Locator: pass ONE of { selector } | { role, name? } | { text, exact? } | { framework, stable_id }. framework: react|vue|svelte|solid|dom (only meaningful for stable_id; svelte stable_id is a file, not an element — use role/text/selector). Disambiguate multiple matches with nth (0-based) or require_unique. Also extension_id?/tab_id?. CALL host_status FIRST. Runs in page-world via the page-bridge — no CDP, coexists with the user's DevTools.
| Name | Type | Req | Description |
|---|---|---|---|
| exact | boolean | – | – |
| extension_id | string | – | – |
| framework | string | – | – |
| name | string | – | – |
| nth | integer | – | – |
| require_unique | boolean | – | – |
| role | string | – | – |
| selector | string | – | – |
| stable_id | string | – | – |
| tab_id | integer | – | – |
| text | string | – | – |
No output schema declared.
No examples provided.
pdl_check_setup ~343
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), bundled extension dist present, an extension ID registered, live NMH connections, AND extension-id / allow-list consistency — it derives the bundled extension's id from its pinned manifest key and probes any managed browser's debug port for loaded extension ids, flagging the case where an extension is loaded but its id is not whitelisted in allowed_origins (so it loads yet never connects). ok=true means no gaps. When gaps exist, next_steps carries the exact remediation (the `claude mcp add chrome-devtools …` snippet, the host install/register command, the host_register_extension <id> fix for a mismatch, or a pdl_install_extension pointer). detail also reports bundledExtensionId + loadedExtensionIds. Also reports phone readiness as information (never a gap): adb installed, connected Android devices, their authorization state and whether Chrome is debuggable, with what to ask the user. Cheap, no side effects. Run this first on a new machine, then chain pdl_install_extension → pdl_launch_browser.
Input schema present but exposes no named parameters.
No output schema declared.
No examples provided.
pdl_click ~224
Click an element (full pointer/mouse event chain so React/Vue delegated onClick fires). Locator: pass ONE of { selector } | { role, name? } | { text, exact? } | { framework, stable_id }. framework: react|vue|svelte|solid|dom (only meaningful for stable_id; svelte stable_id is a file, not an element — use role/text/selector). Disambiguate multiple matches with nth (0-based) or require_unique. Also extension_id?/tab_id?. CALL host_status FIRST. Runs in page-world via the page-bridge — no CDP, coexists with the user's DevTools.
| Name | Type | Req | Description |
|---|---|---|---|
| exact | boolean | – | – |
| extension_id | string | – | – |
| framework | string | – | – |
| name | string | – | – |
| nth | integer | – | – |
| require_unique | boolean | – | – |
| role | string | – | – |
| selector | string | – | – |
| stable_id | string | – | – |
| tab_id | integer | – | – |
| text | string | – | – |
No output schema declared.
No examples provided.
pdl_close_browser ~225
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; ignored for the user's 'existing' profile), or 'detach' (drop the registry record, leave the browser running). With no target it does nothing (returns an error) — closing requires intent. Follow next_steps[].
| Name | Type | Req | Description |
|---|---|---|---|
| all | boolean | – | – |
| browser | string | – | – |
| pid | integer | – | – |
| port | integer | – | – |
| session | string | – | – |
No output schema declared.
No examples provided.
pdl_dblclick ~212
Double-click an element. Locator: pass ONE of { selector } | { role, name? } | { text, exact? } | { framework, stable_id }. framework: react|vue|svelte|solid|dom (only meaningful for stable_id; svelte stable_id is a file, not an element — use role/text/selector). Disambiguate multiple matches with nth (0-based) or require_unique. Also extension_id?/tab_id?. CALL host_status FIRST. Runs in page-world via the page-bridge — no CDP, coexists with the user's DevTools.
| Name | Type | Req | Description |
|---|---|---|---|
| exact | boolean | – | – |
| extension_id | string | – | – |
| framework | string | – | – |
| name | string | – | – |
| nth | integer | – | – |
| require_unique | boolean | – | – |
| role | string | – | – |
| selector | string | – | – |
| stable_id | string | – | – |
| tab_id | integer | – | – |
| text | string | – | – |
No output schema declared.
No examples provided.
pdl_double_tap ~214
Double-tap the located element. Locator: pass ONE of { selector } | { role, name? } | { text, exact? } | { framework, stable_id }. framework: react|vue|svelte|solid|dom (only meaningful for stable_id; svelte stable_id is a file, not an element — use role/text/selector). Disambiguate multiple matches with nth (0-based) or require_unique. Also extension_id?/tab_id?. CALL host_status FIRST. Runs in page-world via the page-bridge — no CDP, coexists with the user's DevTools.
| Name | Type | Req | Description |
|---|---|---|---|
| exact | boolean | – | – |
| extension_id | string | – | – |
| framework | string | – | – |
| name | string | – | – |
| nth | integer | – | – |
| require_unique | boolean | – | – |
| role | string | – | – |
| selector | string | – | – |
| stable_id | string | – | – |
| tab_id | integer | – | – |
| text | string | – | – |
No output schema declared.
No examples provided.
pdl_drag ~272
Drag the located element to a point (toX/toY) or onto targetSelector; pointer drag + optional HTML5 DnD. Locator: pass ONE of { selector } | { role, name? } | { text, exact? } | { framework, stable_id }. framework: react|vue|svelte|solid|dom (only meaningful for stable_id; svelte stable_id is a file, not an element — use role/text/selector). Disambiguate multiple matches with nth (0-based) or require_unique. Also extension_id?/tab_id?. CALL host_status FIRST. Runs in page-world via the page-bridge — no CDP, coexists with the user's DevTools.
| Name | Type | Req | Description |
|---|---|---|---|
| exact | boolean | – | – |
| extension_id | string | – | – |
| framework | string | – | – |
| html5 | boolean | – | – |
| name | string | – | – |
| nth | integer | – | – |
| require_unique | boolean | – | – |
| role | string | – | – |
| selector | string | – | – |
| stable_id | string | – | – |
| steps | number | – | – |
| tab_id | integer | – | – |
| targetSelector | string | – | – |
| text | string | – | – |
| toX | number | – | – |
| toY | number | – | – |
No output schema declared.
No examples provided.
pdl_fill ~233
Set an input/textarea/select value via the native setter + input/change (works with React controlled inputs). Locator: pass ONE of { selector } | { role, name? } | { text, exact? } | { framework, stable_id }. framework: react|vue|svelte|solid|dom (only meaningful for stable_id; svelte stable_id is a file, not an element — use role/text/selector). Disambiguate multiple matches with nth (0-based) or require_unique. Also extension_id?/tab_id?. CALL host_status FIRST. Runs in page-world via the page-bridge — no CDP, coexists with the user's DevTools.
| Name | Type | Req | Description |
|---|---|---|---|
| exact | boolean | – | – |
| extension_id | string | – | – |
| framework | string | – | – |
| name | string | – | – |
| nth | integer | – | – |
| require_unique | boolean | – | – |
| role | string | – | – |
| selector | string | – | – |
| stable_id | string | – | – |
| tab_id | integer | – | – |
| text | string | – | – |
| value | string | yes | – |
No output schema declared.
No examples provided.
pdl_focus ~209
Focus an element. Locator: pass ONE of { selector } | { role, name? } | { text, exact? } | { framework, stable_id }. framework: react|vue|svelte|solid|dom (only meaningful for stable_id; svelte stable_id is a file, not an element — use role/text/selector). Disambiguate multiple matches with nth (0-based) or require_unique. Also extension_id?/tab_id?. CALL host_status FIRST. Runs in page-world via the page-bridge — no CDP, coexists with the user's DevTools.
| Name | Type | Req | Description |
|---|---|---|---|
| exact | boolean | – | – |
| extension_id | string | – | – |
| framework | string | – | – |
| name | string | – | – |
| nth | integer | – | – |
| require_unique | boolean | – | – |
| role | string | – | – |
| selector | string | – | – |
| stable_id | string | – | – |
| tab_id | integer | – | – |
| text | string | – | – |
No output schema declared.
No examples provided.
pdl_help ~221
How to use pwa-debug, as data. Call with no args for the overview: what pwa-debug can do (you can navigate, click, type and read app state yourself, with no screenshots needed), the startup sequence, and the list of how-to topics. Pass topic (e.g. 'control', 'reading_state', 'mobile', 'pwa') for a how-to, or tool (e.g. 'pdl_click') for that tool's full description and parameters, read from the live registry. Cheap, no side effects; call it whenever you are unsure what pwa-debug can do or how to call a tool.
| Name | Type | Req | Description |
|---|---|---|---|
| tool | string | – | A tool name, e.g. "pdl_click", to get its full description and parameters. |
| topic | string | – | How-to topic id: getting_started | control | reading_state | frameworks | stores | pwa | layout | popups | recording | browser | chrome_devtools | mobile | targeting | settings | setup | troubleshoot… |
No output schema declared.
No examples provided.
pdl_hover ~218
Hover an element (pointer/mouse over/enter/move). Locator: pass ONE of { selector } | { role, name? } | { text, exact? } | { framework, stable_id }. framework: react|vue|svelte|solid|dom (only meaningful for stable_id; svelte stable_id is a file, not an element — use role/text/selector). Disambiguate multiple matches with nth (0-based) or require_unique. Also extension_id?/tab_id?. CALL host_status FIRST. Runs in page-world via the page-bridge — no CDP, coexists with the user's DevTools.
| Name | Type | Req | Description |
|---|---|---|---|
| exact | boolean | – | – |
| extension_id | string | – | – |
| framework | string | – | – |
| name | string | – | – |
| nth | integer | – | – |
| require_unique | boolean | – | – |
| role | string | – | – |
| selector | string | – | – |
| stable_id | string | – | – |
| tab_id | integer | – | – |
| text | string | – | – |
No output schema declared.
No examples provided.
pdl_install_extension ~133
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.
| Name | Type | Req | Description |
|---|---|---|---|
| target | string | – | – |
No output schema declared.
No examples provided.
pdl_key_press ~221
Press a single key on an element. Locator: pass ONE of { selector } | { role, name? } | { text, exact? } | { framework, stable_id }. framework: react|vue|svelte|solid|dom (only meaningful for stable_id; svelte stable_id is a file, not an element — use role/text/selector). Disambiguate multiple matches with nth (0-based) or require_unique. Also extension_id?/tab_id?. CALL host_status FIRST. Runs in page-world via the page-bridge — no CDP, coexists with the user's DevTools.
| Name | Type | Req | Description |
|---|---|---|---|
| exact | boolean | – | – |
| extension_id | string | – | – |
| framework | string | – | – |
| key | string | yes | – |
| name | string | – | – |
| nth | integer | – | – |
| require_unique | boolean | – | – |
| role | string | – | – |
| selector | string | – | – |
| stable_id | string | – | – |
| tab_id | integer | – | – |
| text | string | – | – |
No output schema declared.
No examples provided.
pdl_launch_browser ~614
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 browser, with the pwa-debug extension PRELOADED (no reload needed); mode='sandbox-temp' is the same but in a throwaway mkdtemp profile cleaned up on host shutdown. Sandbox modes always work standalone (separate profile → no lock collision) and both pwa-debug + CDP tools are available. Args: browser? (chrome|chromium|edge|brave|vivaldi|opera; defaults to system-default), port? (default 9222), mode?, packaging? (native|snap|flatpak). When the same browser is installed under multiple packagings (e.g. snap AND flatpak chromium), pass packaging to pick one; without it the default preference is native > snap > flatpak and next_steps lists the alternatives so you can re-target. isolateExtensions? (sandbox modes only, default true): true pins the dedicated profile to ONLY the pwa-debug extension (clean room — every other extension is disabled); pass false to let other extensions coexist (pwa-debug still preloads, while extensions already in the persistent profile or Load-unpacked/installed after launch stay enabled) — use this to debug a PWA alongside other extensions or to test your own extension with pwa-debug. existing mode already keeps all your normal-profile extensions. extraArgs? (string[]): extra Chromium startup flags appended after pwa-debug's managed flags — e.g. extraArgs=['--enable-speech-dispatcher'] to enable system TTS/speech-dispatcher, or any other --flag the browser ne…
| Name | Type | Req | Description |
|---|---|---|---|
| browser | string | – | – |
| extraArgs | array | – | – |
| isolateExtensions | boolean | – | – |
| mode | string | – | – |
| packaging | string | – | – |
| port | integer | – | – |
No output schema declared.
No examples provided.
pdl_long_press ~228
Long-press the located element (holds, then releases + contextmenu). Locator: pass ONE of { selector } | { role, name? } | { text, exact? } | { framework, stable_id }. framework: react|vue|svelte|solid|dom (only meaningful for stable_id; svelte stable_id is a file, not an element — use role/text/selector). Disambiguate multiple matches with nth (0-based) or require_unique. Also extension_id?/tab_id?. CALL host_status FIRST. Runs in page-world via the page-bridge — no CDP, coexists with the user's DevTools.
| Name | Type | Req | Description |
|---|---|---|---|
| duration | number | – | – |
| exact | boolean | – | – |
| extension_id | string | – | – |
| framework | string | – | – |
| name | string | – | – |
| nth | integer | – | – |
| require_unique | boolean | – | – |
| role | string | – | – |
| selector | string | – | – |
| stable_id | string | – | – |
| tab_id | integer | – | – |
| text | string | – | – |
No output schema declared.
No examples provided.
pdl_navigate ~238
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 flight at timeout — the navigation still happened). Args: { url: non-empty string, tab_id?, timeout_ms?, extension_id? }. With no extension_id/tab_id, targets the single connected NMH and the active tab. CALL host_status FIRST to confirm a connection.
| Name | Type | Req | Description |
|---|---|---|---|
| extension_id | string | – | – |
| tab_id | integer | – | – |
| timeout_ms | integer | – | – |
| url | string | yes | – |
No output schema declared.
No examples provided.
pdl_new_tab ~215
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: non-empty string, active?, timeout_ms?, extension_id? }. With no extension_id, targets the single connected NMH. CALL host_status FIRST to confirm a connection.
| Name | Type | Req | Description |
|---|---|---|---|
| active | boolean | – | – |
| extension_id | string | – | – |
| timeout_ms | integer | – | – |
| url | string | yes | – |
No output schema declared.
No examples provided.
pdl_pinch ~232
Pinch-zoom on the located element with two touches. Locator: pass ONE of { selector } | { role, name? } | { text, exact? } | { framework, stable_id }. framework: react|vue|svelte|solid|dom (only meaningful for stable_id; svelte stable_id is a file, not an element — use role/text/selector). Disambiguate multiple matches with nth (0-based) or require_unique. Also extension_id?/tab_id?. CALL host_status FIRST. Runs in page-world via the page-bridge — no CDP, coexists with the user's DevTools.
| Name | Type | Req | Description |
|---|---|---|---|
| exact | boolean | – | – |
| extension_id | string | – | – |
| framework | string | – | – |
| name | string | – | – |
| nth | integer | – | – |
| require_unique | boolean | – | – |
| role | string | – | – |
| scale | number | yes | – |
| selector | string | – | – |
| stable_id | string | – | – |
| steps | number | – | – |
| tab_id | integer | – | – |
| text | string | – | – |
No output schema declared.
No examples provided.
pdl_register_chrome_devtools ~242
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 — ALWAYS ask the user for confirmation before calling it. Because it writes a DIRECT MCP registration, a full Claude Code restart is required afterward for the tools to load (next_steps explains the restart, the context-handoff to hand the user before restarting, and the lower-friction plugin alternative that needs only /reload-plugins). Args: port? (override the debug port).
| Name | Type | Req | Description |
|---|---|---|---|
| port | integer | – | – |
No output schema declared.
No examples provided.
pdl_scroll ~248
Scroll the located element by delta (dispatches wheel + scrollBy) or scrollIntoView. Locator: pass ONE of { selector } | { role, name? } | { text, exact? } | { framework, stable_id }. framework: react|vue|svelte|solid|dom (only meaningful for stable_id; svelte stable_id is a file, not an element — use role/text/selector). Disambiguate multiple matches with nth (0-based) or require_unique. Also extension_id?/tab_id?. CALL host_status FIRST. Runs in page-world via the page-bridge — no CDP, coexists with the user's DevTools.
| Name | Type | Req | Description |
|---|---|---|---|
| deltaX | number | – | – |
| deltaY | number | – | – |
| exact | boolean | – | – |
| extension_id | string | – | – |
| framework | string | – | – |
| intoView | boolean | – | – |
| name | string | – | – |
| nth | integer | – | – |
| require_unique | boolean | – | – |
| role | string | – | – |
| selector | string | – | – |
| stable_id | string | – | – |
| tab_id | integer | – | – |
| text | string | – | – |
No output schema declared.
No examples provided.
pdl_select_option ~235
Select a <select> option by value or visible label (one required). Locator: pass ONE of { selector } | { role, name? } | { text, exact? } | { framework, stable_id }. framework: react|vue|svelte|solid|dom (only meaningful for stable_id; svelte stable_id is a file, not an element — use role/text/selector). Disambiguate multiple matches with nth (0-based) or require_unique. Also extension_id?/tab_id?. CALL host_status FIRST. Runs in page-world via the page-bridge — no CDP, coexists with the user's DevTools.
| Name | Type | Req | Description |
|---|---|---|---|
| exact | boolean | – | – |
| extension_id | string | – | – |
| framework | string | – | – |
| label | string | – | – |
| name | string | – | – |
| nth | integer | – | – |
| require_unique | boolean | – | – |
| role | string | – | – |
| selector | string | – | – |
| stable_id | string | – | – |
| tab_id | integer | – | – |
| text | string | – | – |
| value | string | – | – |
No output schema declared.
No examples provided.
pdl_submit ~216
Submit the form owning the located element (requestSubmit). Locator: pass ONE of { selector } | { role, name? } | { text, exact? } | { framework, stable_id }. framework: react|vue|svelte|solid|dom (only meaningful for stable_id; svelte stable_id is a file, not an element — use role/text/selector). Disambiguate multiple matches with nth (0-based) or require_unique. Also extension_id?/tab_id?. CALL host_status FIRST. Runs in page-world via the page-bridge — no CDP, coexists with the user's DevTools.
| Name | Type | Req | Description |
|---|---|---|---|
| exact | boolean | – | – |
| extension_id | string | – | – |
| framework | string | – | – |
| name | string | – | – |
| nth | integer | – | – |
| require_unique | boolean | – | – |
| role | string | – | – |
| selector | string | – | – |
| stable_id | string | – | – |
| tab_id | integer | – | – |
| text | string | – | – |
No output schema declared.
No examples provided.
pdl_swipe ~238
Swipe a touch across the located element in a direction. Locator: pass ONE of { selector } | { role, name? } | { text, exact? } | { framework, stable_id }. framework: react|vue|svelte|solid|dom (only meaningful for stable_id; svelte stable_id is a file, not an element — use role/text/selector). Disambiguate multiple matches with nth (0-based) or require_unique. Also extension_id?/tab_id?. CALL host_status FIRST. Runs in page-world via the page-bridge — no CDP, coexists with the user's DevTools.
| Name | Type | Req | Description |
|---|---|---|---|
| direction | string | yes | – |
| distance | number | – | – |
| exact | boolean | – | – |
| extension_id | string | – | – |
| framework | string | – | – |
| name | string | – | – |
| nth | integer | – | – |
| require_unique | boolean | – | – |
| role | string | – | – |
| selector | string | – | – |
| stable_id | string | – | – |
| steps | number | – | – |
| tab_id | integer | – | – |
| text | string | – | – |
No output schema declared.
No examples provided.
pdl_tap ~218
Tap (touchstart/touchend) the located element. Locator: pass ONE of { selector } | { role, name? } | { text, exact? } | { framework, stable_id }. framework: react|vue|svelte|solid|dom (only meaningful for stable_id; svelte stable_id is a file, not an element — use role/text/selector). Disambiguate multiple matches with nth (0-based) or require_unique. Also extension_id?/tab_id?. CALL host_status FIRST. Runs in page-world via the page-bridge — no CDP, coexists with the user's DevTools.
| Name | Type | Req | Description |
|---|---|---|---|
| exact | boolean | – | – |
| extension_id | string | – | – |
| framework | string | – | – |
| name | string | – | – |
| nth | integer | – | – |
| require_unique | boolean | – | – |
| role | string | – | – |
| selector | string | – | – |
| stable_id | string | – | – |
| tab_id | integer | – | – |
| text | string | – | – |
No output schema declared.
No examples provided.
pdl_type_sequence ~224
Type a string into an editable element char-by-char. Locator: pass ONE of { selector } | { role, name? } | { text, exact? } | { framework, stable_id }. framework: react|vue|svelte|solid|dom (only meaningful for stable_id; svelte stable_id is a file, not an element — use role/text/selector). Disambiguate multiple matches with nth (0-based) or require_unique. Also extension_id?/tab_id?. CALL host_status FIRST. Runs in page-world via the page-bridge — no CDP, coexists with the user's DevTools.
| Name | Type | Req | Description |
|---|---|---|---|
| exact | boolean | – | – |
| extension_id | string | – | – |
| framework | string | – | – |
| name | string | – | – |
| nth | integer | – | – |
| require_unique | boolean | – | – |
| role | string | – | – |
| selector | string | – | – |
| stable_id | string | – | – |
| tab_id | integer | – | – |
| text | string | – | – |
| value | string | yes | – |
No output schema declared.
No examples provided.
How do I install the PWA Debug Layer MCP server?
PWA Debug Layer runs locally as an npm package, launched with npx -y @aryanduntley/pwa-debug. Ready-made configuration for Claude, Cursor, VS Code, Codex and 5 more is on this page, copied from each client's own documentation.
npm · @aryanduntley/pwa-debug
claude mcp add aryanduntley-pwa-debug-layer -- npx -y @aryanduntley/pwa-debug
What is the PWA Debug Layer MCP server?
PWA Debug Layer is an MCP server listed in the public MCP registry as io.github.aryanduntley/pwa-debug-layer. Debug PWAs in your real browser via MCP: service-worker, cache, installability & framework state. This page covers its npm package (@aryanduntley/pwa-debug).
Is the PWA Debug Layer MCP server safe to use?
PWA Debug Layer scores 84 out of 100 on VerifyMCP. We recorded 1 known advisory against it as of 5 November 2026. It declares no install or post-install scripts. Its build provenance is signed and verified. That is a record of what we were able to check automatically, not an endorsement. The category breakdown on this page shows every signal behind the number, including the ones we could not confirm.
What tools does the PWA Debug Layer MCP server expose?
PWA Debug Layer exposes 91 tools: pdl_help, host_status, host_register_extension, host_unregister_extension, host_list_registrations, and 86 more. Their descriptions and schemas cost roughly 19,539 tokens of context every time the server is loaded.
Is the PWA Debug Layer MCP server still maintained?
PWA Debug Layer is still listed as active in the MCP registry. We last reached this channel on 5 November 2026. Those dates come from our own scans of the registry and the channel itself, not from anything the publisher announced.
What licence is the PWA Debug Layer MCP server under?
PWA Debug Layer declares the MIT licence, which is OSI-approved. That covers the source only, and says nothing about the cost of any service it calls.