Skip to content

Proposal: ship QM → AI-agent telemetry inside wp-framework (Abilities API + official MCP Adapter), optional Chrome-extension bridge #40

Description

@Adi-ty

What we want to build

A local-dev-only capability that makes the data Query Monitor already collects — slow/duplicate DB queries, HTTP calls, PHP errors, each with the file and line that caused it — queryable by the AI coding agent the developer already has open next to their plugin/theme source (Claude Code, Cursor, Copilot).

The agent gets two things side by side: what happened at runtime (QM's telemetry, with host-resolved file paths) and the code that caused it (the repo it's already in). That pairing is what lets it fix things rather than just describe them.

We do not rebuild Query Monitor — QM already collects the data with file/line backtraces. The work is getting that data out of the single request it lives in and in front of the agent. There are two credible architectures for that pipe; this issue lays both out in detail for a decision.

The common core (both approaches need this)

A dev-only layer that:

  1. Captures QM's collectors at shutdown (after QM's own dispatch), normalises them once, and persists the last N requests to a store — QM itself only ever sees the current request. Covers every request the developer makes, including authenticated admin screens, POST submissions and AJAX.
  2. Translates QM's container paths (/var/www/…) to real host paths the agent can open; emits null for anything unmappable rather than a guessed path.
  3. Stays hard-gated to wp_get_environment_type() === 'local' (plus a {PREFIX}_DEV_MODE constant) and no-ops everywhere else.

Storage: a custom MySQL table ($wpdb + dbDelta), not SQLite. The site already has MySQL; SQLite forced PDO plus a wp-content host-mapping hack in the prototype. A capped table (last N requests) is simpler and portable.

On top of that core, the normalised telemetry is exposed to the agent. How it reaches the agent is where the two approaches differ.


Approach A — inside wp-framework: Abilities API + official MCP Adapter

Ship the whole thing as a DEV_MODE-gated src/Dev/ layer inside wp-framework itself — no separate plugin. The captured telemetry is exposed through the Abilities API (Core 6.9) and bridged to MCP by the official MCP Adapter. No browser extension, no custom transport.

Why this fits wp-framework with zero new dependencies (the library's non-negotiable):

  • The Abilities API is WordPress Core 6.9 (shipped Dec 2025) — wp_register_ability() on wp_abilities_api_init. Registering abilities needs nothing in require.
  • A Composer library hooks WordPress the moment the consuming skeleton autoloads and boots it — the same way the skeleton already wires up any framework class. So "the framework captures QM / registers abilities" means "the skeleton that depends on it boots the Dev layer under DEV_MODE."
  • Reading QM is dependency-free — guard with class_exists( 'QM_Collectors' ); QM is the developer's own plugin, never a Composer dep.
  • The table is created via dbDelta on a schema-version check (libraries have no activation hook — this is the standard pattern).
  • The MCP Adapter stays out of require — it's a separate install (plugin or Composer package, WP ≥ 6.9). The framework's abilities flagged 'meta' => array( 'mcp' => array( 'public' => true ) ) are auto-exposed by the adapter's default server with no glue code. List the adapter under Composer suggest so it's discoverable without becoming a dependency.

Abilities exposed:

Ability Purpose
list-requests Recent captures with headline metrics
get-telemetry Full normalised QM data (queries+stacks, HTTP, errors, assets, timing) with host paths
compare-requests Before/after metric deltas — proof a fix worked
profile-url Capture fresh telemetry via HTTP loopback, multi-sample medians

Transport: STDIO for local dev (Claude Code via WP-CLI), or HTTP via a WordPress application password. Correlation ("which capture is the page I'm on?") via list-requests (latest / by URL), optionally aided by an X-<Tool>-Request-Id response header.

Dev browses local site (QM + framework Dev layer active)
        │  every request captured at shutdown → MySQL store
        ▼
wp-framework abilities ──► MCP Adapter ──MCP (STDIO/HTTP)──► IDE / Claude Code ──opens──► source

The loop: profile-urlget-telemetry → agent reads the offending source file → writes the fix → re-profiles → compare-requests confirms it.

One detail: abilities are invoked in a separate request from the page being profiled (the agent calls them over STDIO/HTTP), so the data must be persisted for the ability to read it back. Capture happens on the developer's browser requests; abilities read from the store. WP-CLI requests don't run QM collectors — but they don't need to here, they only read.

Setup for a developer: skeleton already has wp-framework → set DEV_MODE → install Query Monitor → install the MCP Adapter → one mcp.json entry.

Pros

  • Lives in the framework every skeleton already pulls in — no separate plugin to build, distribute or version, and zero new runtime dependencies.
  • Official tooling end-to-end (matches the "prefer official tooling" rule); nothing custom on the transport.
  • Already prototyped.

Cons

  • Selection is manual: the agent picks what was captured (via the header + list-requests); it isn't auto-pointed at "the page I'm looking at now".
  • profile-url re-runs a URL as a fresh GET, so it can't reproduce a logged-in admin screen, a stateful cart→checkout, a POST or an AJAX action. Those are covered only by passive capture, then manual selection.

Approach B — Chrome extension + local MCP bridge (browser-driven)

Keep the same dev-only capture core, but instead of the official adapter, add a Chrome extension and a small local Node MCP bridge, following the proven extension → localhost WebSocket → local MCP server → IDE pattern.

  • Capture core additionally serves the normalised telemetry over an authenticated REST route (register_rest_route — core, still zero-dep). This core can still live in wp-framework.
  • Chrome extension (MV3) watches the page the dev is on, reads its X-<Tool>-Request-Id header, pulls that capture from the REST route (application-password auth), and shows a popup: connection status + a "send this page to my IDE" button (or auto-send on every page).
  • Local Node bridge receives the audit over a localhost WebSocket and exposes it to the IDE as MCP tools (get_latest_audit, get_request, compare_requests). The IDE connects to it like any MCP server.
Dev browses local site (QM + capture core active)
        │  response carries  X-<Tool>-Request-Id
        ▼
Chrome extension ──REST fetch (app pwd)──► capture core ──reads──► QM data
        │  WebSocket (localhost, nothing leaves the machine)
        ▼
Local Node MCP bridge ◄── MCP (stdio/http) ── IDE / Claude Code ──opens──► source

Pros

  • Captures the exact page the dev actually clicks through — including authenticated, POST and AJAX flows the agent cannot reproduce on its own — and selects it automatically.
  • One-click "this page is slow, go fix it" from the browser, where the dev already is. Lowest friction in daily use.
  • A natural home for browser-side signals QM can't see (real frontend timing, JS console errors), if we ever want them.

Cons

  • Three custom components to build and own long-term: an MV3 extension, a Node MCP bridge, and the WebSocket protocol between them — plus the REST route.
  • The Node bridge is a second, parallel MCP server that duplicates what the official MCP Adapter already does in Approach A.
  • More setup per developer: install extension, run bridge, mint app password, wire two MCP entries.
  • Partially overlaps Chrome DevTools MCP (Google, official) for the frontend-signals angle.

Side by side

A — framework + MCP Adapter B — Extension + bridge
Transport official MCP Adapter custom Node bridge over WebSocket
Custom components none (abilities only) extension + bridge + REST route
New runtime deps zero (Abilities API is core) zero in PHP, but a Node bridge to ship
Capture of authed/POST/AJAX passive (captured, manual select) passive and auto-selected on view
"Fix the exact page I'm on" manual (header + list-requests) one click
Dev setup framework + DEV_MODE + adapter + 1 MCP entry + extension + bridge + auth
Frontend signals defer to Chrome DevTools MCP possible to add in-extension
Maintenance owned by rtCamp minimal extension + bridge ongoing
Status prototyped, working designed, not built
"Prefer official tooling" rule satisfied partially diverges

Distribution

A dev-environment capability, shipped inside the framework every skeleton already pulls in and switched on with {PREFIX}_DEV_MODE — a dev-only mode, not a separate plugin. Query Monitor and (for A) the MCP Adapter are the developer's own local installs; MCP is the wire between the running site and the agent. The Dev layer is hard-gated to local + DEV_MODE and no-ops otherwise — the raw data (real SQL, paths, errors) must never be reachable in production or over a public network.

Open questions

  • A vs B, or a phased path (A first, B later if the browse-driven ergonomic proves necessary).
  • Storage shape and retention (capped table, last N requests).
  • Whether profile-url's fresh-GET limitation (can't reproduce authed/POST/AJAX) is acceptable for v1, or B's passive auto-capture is needed sooner.
  • Final tool name (drives the header name and the ability category/namespace).

What already exists (so we don't reinvent)

  • Abilities API (Core 6.9) + MCP Adapter (WP ≥ 6.9, plugin or Composer package) — the official, on-machine browser→agent transport; abilities flagged meta.mcp.public are auto-exposed by the adapter's default server. Approach A is built directly on this.
  • Browser MCP / browser-tools-mcp — prove the extension → localhost WebSocket → local MCP server → IDE pattern (the shape Approach B follows).
  • Chrome DevTools MCP (Google) — already gives agents the frontend half (page load, Core Web Vitals, JS, network). The server-side half — DB queries, hooks, PHP errors with backtraces into PHP files — is exactly what this adds.

Metadata

Metadata

Assignees

No one assigned

    Labels

    Scope: AI PrivacyAI_Data_Sanitizer + ai_context() contractScope: Dev MonitorDev_Monitor panel + collectors (in src/Dev/, dev-mode-only)

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions