Skip to content

Feat - Add a built-in MCP server for AI-assistant control in NotificationX - #156

Open
ShahrearMSf wants to merge 3 commits into
masterfrom
84280-mcp
Open

Feat - Add a built-in MCP server for AI-assistant control in NotificationX#156
ShahrearMSf wants to merge 3 commits into
masterfrom
84280-mcp

Conversation

@ShahrearMSf

Copy link
Copy Markdown
Contributor

Add a built-in MCP server for AI-assistant control (#84280) in NotificationX

Summary

Adds a Model Context Protocol (MCP) server built directly into NotificationX (Free) so approved AI assistants — Claude, ChatGPT, Cursor, and any MCP-compatible client — can manage notifications and read analytics in plain language. It is fully self-contained: no companion plugin, no external service, no terminal.

The feature is off by default, administrator-only, and loads only on a capable PHP runtime, so nothing changes for existing installs until an admin turns it on.

What's included

10 tools (abilities) exposed over MCP

Read Write
list-notifications, get-notification, list-types, list-sources, get-analytics, get-settings toggle-notification, update-notification, duplicate-notification, delete-notification
  • JSON-RPC 2.0 transport at a pretty endpoint …/notificationx/mcp (plus the REST route) handling initialize / ping / tools/list / tools/call.
  • Two ways to connect:
    • OAuth 2.1 + PKCE for one-click clients — dynamic client registration (RFC 7591), .well-known discovery, refresh-token rotation, and a first-party consent page.
    • Pairing token (bearer) for token-based clients.
  • Admin UI — a native MCP tab in NotificationX → Settings: status badge, connector URL, token (show/copy), per-client setup cards, a connected-apps list with per-app revoke, connection health, and a one-click self-test.

How it works

  • Each capability is a small self-describing ability (input/output JSON Schema + permission callback), registered in a lightweight registry that mirrors the WordPress Abilities API contract, so it can hand off to core's Abilities API later with no rewrite.
  • The MCP server bridges those abilities to tools/list / tools/call. Every request authenticates (pairing token or OAuth access token) and then runs as the granting administrator — each ability re-checks native WP capabilities.
  • Everything is server-rendered PHP (settings tab included), so no JS/build step is required.

Security

  • Off by default, gated behind a single enable_mcp setting; when off, the endpoint returns 403 and discovery is inert.
  • Bearer auth impersonates the granting admin and refuses anyone lacking manage_options.
  • Read/write scope gating — a read-only credential cannot call write tools.
  • Per-IP rate limiter on failed auth (fixed-window lockout).
  • Secret redactionget-settings never returns API keys/tokens.
  • Destructive tools require explicit confirmation (e.g. delete-notification needs confirm:true).
  • OAuth tokens are stored hashed at rest; PKCE (S256) is mandatory; codes are single-use; refresh tokens rotate.

Compatibility & rollout

  • Progressive enhancement: the module loads only on PHP ≥ 7.0, so the plugin's existing minimum PHP is unchanged. On older PHP the module simply doesn't load.
  • No schema/migration changes; state lives in a couple of options.
  • No changes to existing behavior when disabled.

Screenshots

Free

1. Enable & status — top of the MCP tab

Free — enable & status

2. Connection & client setup

Free — connection & clients

3. Connected apps

Free — connected apps

4. Connection health

Free — connection health

With Pro active

Top of the MCP tab (Pro installed)

With Pro — top of tab

Testing

Verified on a local site and a live HTTPS site, Free only (and with Pro active for the settings-tab render).

Transport & discovery

  • Unauthenticated → 401 + WWW-Authenticate challenge; initialize returns the correct protocol/server info; tools/list10 tools; both .well-known OAuth documents serve 200.

Tools

  • All read tools return real data; get-settings redacts secrets (***redacted***).
  • All write tools verified end-to-end: update (rename), toggle (correct state + free single-active cap honored), duplicate (creates disabled), delete (confirm-gate: no-confirm blocked, with-confirm deleted).
  • Read/write gating: read-only token is blocked from write tools.
  • Rate limiter locks after the failure threshold; self-test reports the server healthy with all tools available.

OAuth 2.1 + PKCE (one-click path)

  • Full flow verified: dynamic client registration → consent → code exchange → access token calls the MCP endpoint successfully; refresh-token rotation works and the old refresh token is rejected; PKCE tampering is rejected (invalid_grant).
  • On the live HTTPS site, dynamic registration accepted a real client callback and the discovery documents advertise HTTPS endpoints — a hosted client can connect directly.

Management actions

  • Reset token (old token → 401, new → 200), per-app revoke (token 200401, app removed), disconnect-all, and reconnect all verified.

Connected apps

  • Completed a genuine OAuth connection so a client appears in Connected apps with its scope badge and a working revoke.

Free features + analytics accuracy (live site)

  • Created a notification via the builder, edited it, viewed it on the frontend, then confirmed analytics recorded exactly the views generated — and the number matched across the MCP get-analytics tool, the dashboard Total Views, and the list row.

Bugs found & fixed during testing

  1. toggle-notification reported a stale enabled value (read a memoized state) — now re-reads from storage.
  2. The pretty endpoint returned 401 instead of 429 when rate-limited (the auth header was reset after the status) — status is now asserted last.

UI polish

  • Corrected the enable-toggle alignment (label + switch on one row; help text left-aligned) and tightened the settings panel (status badge, copy/reveal, connected-apps list, health, toasts).

Reviewer notes

  • Runtime autoloading uses the optimized classmap, so the regenerated vendor/composer/autoload_classmap.php + autoload_static.php are committed alongside the new classes.
  • No PR/merge automation assumed — targeting review before merge.

Introduce a self-contained Model Context Protocol (MCP) server so AI
assistants such as Claude, ChatGPT and Cursor can manage NotificationX in
natural language. It is off by default, administrator-only, and loads only
on PHP 7.0+ so the plugin's stated 5.6 floor is unchanged.

Abilities (includes/Abilities): a WordPress-Abilities-API-shaped registry
exposing 6 read tools (list/get notifications, analytics, types, sources,
settings) and 4 write tools (toggle, update, duplicate, delete), with the
free single-active-notification cap enforced and secrets redacted.

MCP module (includes/MCP): JSON-RPC 2.0 transport over a pretty
/notificationx/mcp endpoint and the REST route, an abilities-to-tools
bridge, pairing-token auth and a full OAuth 2.1 authorization server with
PKCE (dynamic client registration, discovery, refresh-token rotation),
per-request admin impersonation, a per-IP rate limiter, read/write scope
gating and a loopback self-test. Adds a native NotificationX settings tab
(status, connector URL, tokens, per-client setup, connected apps, health).

Wire the module in via NotificationX::__construct and regenerate the
Composer classmap for the new classes.
Keep the "Enable MCP access" label and switch on a single row instead of
quickbuilder's fixed 200px label column, which left a large gap and floated
the toggle far to the right with the help text indented beneath it. The help
now spans full width, left-aligned with the label. CSS only, scoped to the
enable_mcp field; no behaviour change.
Expose NotificationX form submissions (Popup / Exit Intent) over the MCP
server. list-entries lists entries with notification/source/search filters
and pagination; export-entries returns the feedback CSV. The submitter name
and email are included only when Pro is active, mirroring the admin Feedback
Entries export.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant