=== Enable Abilities for MCP === Contributors: fabiomontenegro1987 Donate link: https://paypal.me/fabiomontenegroz Tags: mcp, ai, rest-api, content-management, woocommerce Requires at least: 6.9 Tested up to: 7.0 Requires PHP: 8.0 Stable tag: 2.1.1 License: GPLv2 or later License URI: https://www.gnu.org/licenses/gpl-2.0.html Manage which WordPress Abilities are exposed to MCP servers. Supports WooCommerce, The Events Calendar, and any custom post type. == Description == **Enable Abilities for MCP** gives you full control over which WordPress Abilities are available to AI assistants through the MCP (Model Context Protocol) Adapter. WordPress 6.9 introduced the Abilities API, allowing external tools to discover and execute actions on your site. This plugin extends that functionality by registering a comprehensive set of content management abilities and providing a simple admin interface to toggle each one on or off. = Connect from claude.ai with just a URL = Since version 2.1 the plugin ships an embedded OAuth 2.1 server built for claude.ai custom connectors. Add your site in claude.ai → Settings → Connectors, log in with your WordPress user, approve the consent screen — connected. No Client ID, no Application Password, no local configuration. * Works from the claude.ai web app, mobile apps, and Claude Desktop * Each team member authenticates with their **own WordPress account and role** — a subscriber can never do what only an editor should * Every ability execution lands in the activity log under the real user's name * Works on single sites, subdirectory installs, and multisite networks (network-activate so the main site serves the OAuth discovery documents for every subsite) Prefer tokens? Application Passwords (per-user) and a single-admin Bearer token connect Claude Desktop / Claude Code, OpenAI Codex CLI, and Google Antigravity — the Connection tab generates ready-to-paste configuration for each client, and fills in your credentials automatically. = Features = * **71 abilities** organized in 16 categories: Core, Read, Write, SEO (Rank Math), SEO (SEOPress), SEO (Yoast), Utility, Multilanguage, Custom Post Types, WooCommerce, The Events Calendar, Code Snippets, JetEngine Options Pages, Elementor, LearnDash, and AI Agent Readiness (llms.txt) * **WooCommerce integration** — dedicated abilities to manage products, orders, and customers using the native WooCommerce API (HPOS-compatible, formally declared) * **The Events Calendar integration** — list, get, create, and update events with venue, organizer, and date filters * **claude.ai OAuth custom connector** — connect from claude.ai (web, mobile, or desktop) with zero local setup: an embedded OAuth 2.1 server with Client ID Metadata Document (CIMD) support lets each user log in with their own WordPress account and role * **Admin dashboard** with toggle switches for each ability * **Per-ability control** — expose only what you need * **Secure by design** — proper capability checks, input sanitization, and per-post permission validation * **WPCS compliant** — fully passes WordPress Coding Standards (phpcs) * **MCP-ready** — all abilities include `show_in_rest` and `mcp.public` metadata = Available Abilities = **Read (safe, query-only):** * Get posts with filters (status, category, tag, search) * Get single post details (content, SEO meta, featured image) * Get categories, tags, pages, comments, media, and users **Write (create & modify):** * Create, update, and delete posts * Create categories and tags * Create pages * Moderate comments * Reply to comments as the authenticated user * Upload images from external URLs to the media library (with optional auto-assign as featured image) **SEO — Rank Math:** * Get full Rank Math metadata for any post/page (title, description, keywords, robots, Open Graph, SEO score) * Update Rank Math metadata: SEO title, description, focus keyword, canonical URL, robots, Open Graph, primary category, pillar content **SEO — SEOPress:** * Get full SEOPress metadata for any post/page (title, description, focus keyword, robots, canonical, Open Graph, Twitter Card) * Update SEOPress metadata: SEO title, description, focus keyword, canonical URL, robots directives, Open Graph, Twitter Card **SEO — Yoast SEO:** * Get full Yoast SEO metadata for any post/page (title, description, focus keyphrase, canonical, robots, Open Graph, Twitter Card) * Update Yoast SEO metadata: SEO title, description, focus keyphrase, canonical URL, robots (noindex, nofollow, advanced), Open Graph, Twitter Card * Get Yoast sitemap index — fetch and parse the sitemap index, returning all registered sitemap URLs with last modification date **Custom Post Types:** * List all registered custom post types with configuration and taxonomies * Get items from any CPT with filtering, search, and taxonomy queries * Get full details of a CPT item including all meta fields (WooCommerce, ACF, JetEngine, etc.) * Create, update, and delete CPT items with taxonomy and meta field support * Get CPT taxonomies with their terms * Assign taxonomy terms to CPT items **WooCommerce:** * List products with price, SKU, stock status, categories, and type * Get full product detail including gallery, attributes, and variations * Update product price, sale price, stock quantity, and status * List orders with customer, total, status, and date (HPOS-compatible) * Get full order detail: line items, billing/shipping, totals, and notes * Update order status with optional note * List customers with email, name, total spent, and order count **The Events Calendar:** * List events with start/end date, venue, organizer, and date range filter * Get full event detail with resolved venue address and organizer contact * Create new events with title, description, dates, venue, and organizer * Update existing events **Utility:** * Search and replace text in post content * Site statistics overview (includes custom post type counts) * Update any post meta field by exact key (with protected internal key blocklist) = Requirements = * WordPress 6.9 or later (Abilities API) * MCP Adapter plugin installed and configured * PHP 8.0 or later == Installation == 1. In your WordPress dashboard, go to **Plugins > Add New** and search for **Enable Abilities for MCP**. 2. Click **Install Now**, then **Activate**. 3. Go to **Settings > WP Abilities** to manage which abilities are active. 4. Install and configure the [MCP Adapter](https://github.com/WordPress/mcp-adapter/releases) plugin to connect with AI assistants. == Frequently Asked Questions == = Do I need anything else for this plugin to work? = Yes. This plugin requires WordPress 6.9+ (which includes the Abilities API) and the MCP Adapter plugin to connect abilities with AI assistants like Claude. = Are all abilities enabled by default? = Yes. On first activation, all abilities are enabled. You can disable any of them from **Settings > WP Abilities**. = Is it safe to enable write abilities? = Write abilities respect WordPress capabilities. For example, creating a post requires the `publish_posts` capability, and editing checks per-post permissions. The MCP user must have the appropriate WordPress role. = Does it work on Multisite? = Yes. The plugin can be network-activated. Each site in the network has its own ability configuration, its own OAuth toggle, and its own connector URL — enabling MCP on one subsite never exposes the others. For **subdirectory networks** (site.com/blog-a, site.com/blog-b) the claude.ai OAuth connector needs the plugin network-activated (or active on the main site): OAuth clients resolve discovery documents against the domain root, which belongs to the main site, so the plugin bridges those requests to the owning subsite automatically. = Does it work with WooCommerce? = Yes. The Custom Post Types section automatically detects WooCommerce products, orders, coupons, and any other registered post type. You can list, create, update, and delete items with full access to WooCommerce meta fields like `_price`, `_sku`, `_stock_status`, `_regular_price`, and more. = Can I add custom abilities? = This plugin registers abilities using the standard `wp_register_ability()` API. You can register additional abilities in your own plugin using the `wp_abilities_api_init` hook. = The claude.ai custom connector fails with "Couldn't register with the sign-in service" — why? = In almost every reported case the OAuth flow is fine and the request never reaches WordPress: a security layer in front of your site is blocking Anthropic's backend, which connects with a non-browser User-Agent (`python-httpx`). Common culprits are hosting WAFs (cPGuard, Imunify360, ModSecurity rules like "generic HTTP client User-Agent") and Cloudflare's Bot Fight Mode or AI-crawler blocking. To diagnose, run `curl -A "python-httpx/0.28.1" https://your-site.com/.well-known/oauth-authorization-server` from an external machine — a 403 confirms the block. Ask your host to allow that User-Agent (or Anthropic's IP range 160.79.104.0/23) for `/.well-known/oauth-*`, `/oauth/*`, and `/wp-json/mcp/*`, or disable the relevant bot protection for the site. = The OAuth discovery documents return a 301 redirect or 404 — is that a problem? = Yes — strict OAuth clients require a direct `200` on `/.well-known/oauth-authorization-server` and `/.well-known/oauth-protected-resource`. This plugin already prevents WordPress's trailing-slash canonical redirect on those paths and serves the RFC 9728 path-suffixed variants. If they still return 404, your web server is intercepting `.well-known/` before WordPress runs (common with Let's Encrypt auto-SSL configs) — see **Tools → Site Health** for the "MCP OAuth discovery documents" check and ask your host to route those two paths to WordPress. == Screenshots == 1. Admin settings page showing all abilities organized by category with toggle switches. == Changelog == = 2.1.1 = * Fix: OAuth discovery-document handling (canonical-redirect prevention and RFC 9728 path-suffixed URLs) now works for sites installed in a subdirectory — the matchers derive the site's base path from `home_url()` instead of assuming a root install. * New: multisite discovery bridge — on subdirectory networks, OAuth clients resolve `/.well-known/oauth-*` against the domain root, which belongs to the main site. With the plugin network-activated, the main site now resolves those requests to the owning subsite and relays its discovery document, making the claude.ai connector work for every subsite (validated against claude.ai on a production-style subdirectory multisite). * Docs: Multisite FAQ updated with the subdirectory-network requirements. = 2.1.0 = * New: claude.ai OAuth Custom Connector — embedded OAuth 2.1 server (wp-media/mcp-oauth) with Client ID Metadata Document (CIMD) support. Add your site as a custom connector in claude.ai (web, mobile, or desktop) with just a URL — no Client ID, no Application Password: each user logs in with their own WordPress account and approves a consent screen. Opt-in from Settings > WP Abilities > Connection. * New: Connection tab redesigned — authentication methods now read top-down as options (claude.ai OAuth, Application Passwords, Single Admin Bearer Token) and the client configuration examples (Claude Desktop / Claude Code, OpenAI Codex CLI, Google Antigravity) moved to a shared, highlighted "Connect your AI client" section with the MCP endpoint URL, since they apply to both token methods. * New: generating Application Password credentials now auto-fills every client example with your real `Basic` authorization header — copy-paste ready, no manual editing. * Fix: OAuth discovery documents (`/.well-known/oauth-*`) and `/oauth/*` endpoints no longer receive WordPress's trailing-slash 301 canonical redirect, which strict OAuth clients (claude.ai) reject as a failed metadata fetch. * Fix: RFC 9728 path-suffixed discovery URLs (`/.well-known/oauth-protected-resource/`) are now served, matching the lookup order of Anthropic's OAuth client. * Docs: FAQ entries on diagnosing hosting WAFs (cPGuard/Imunify/ModSecurity) and Cloudflare bot protections that block Anthropic's `python-httpx` client. * i18n: POT and Spanish (es_ES) translation updated with the new strings. = 2.0.25 = * New: Connection tab now includes ready-to-copy configuration for three AI clients — Claude Desktop / Claude Code (`claude_desktop_config.json`), OpenAI Codex CLI (`~/.codex/config.toml`, TOML `[mcp_servers.*]`), and Google Antigravity (`mcp_config.json`, direct `serverUrl` + `headers` connection with no npx required). * New: Review request notice — appears only for administrators, only after 25+ logged ability executions (threshold filterable via `ewpa_review_notice_threshold`), and only on the Dashboard, Plugins, and plugin settings screens. Snoozable for 30 days or permanently dismissible; the shown count comes from the real activity log. * i18n: POT and Spanish (es_ES) translation updated with the new strings. = 2.0.24 = * New: `ewpa/clear-cache` (Utility) — purges the page cache for a single post (`post_id`, requires edit_post) or the whole site (no param, requires manage_options). Auto-detects WP Rocket, LiteSpeed Cache, W3 Total Cache, WP Super Cache, and WP Fastest Cache; falls back to the WordPress object cache when none is active. Fixes the stale-audit loop: write abilities that modify post meta directly (`update-seopress`, `update-post-meta`, `elementor-update-element`) do not fire `save_post`, so cache plugins kept serving the old rendered HTML — SEO audits and visitors saw stale titles and meta descriptions after AI fixes. Auto-enabled on upgrade. Reported from a production site running LiteSpeed. * Updated: Total abilities: 71 in 16 categories = 2.0.23 = * i18n: Regenerated the POT template (524 strings, was stuck at v2.0.13) and completed the Spanish (es_ES) translation — 101 new strings covering the JetEngine Options Pages, Elementor, LearnDash, SEOPress content analysis, and llms.txt sections. Recompiled .mo files. = 2.0.22 = * New: AI — Agent Readiness section (2 abilities). `ewpa/get-llms-txt` fetches the site llms.txt (the AI-crawler guidance file audited by Lighthouse "Agentic Browsing"), detects which component serves it (SEOPress Pro, physical file, this plugin, third-party, or none), and validates it against the llmstxt.org spec with actionable issues (missing H1, no blockquote summary, no Markdown links, raw HTML entities, oversize). `ewpa/update-llms-txt` (opt-in, manage_options) writes the content with automatic routing: SEOPress Pro option when active (its dynamic placeholders keep working), or a virtual /llms.txt served by this plugin via do_parse_request; refuses when a physical file or third-party plugin already provides it. Content validated before saving. * Updated: Total abilities: 70 in 16 categories = 2.0.21 = * New: LearnDash section (6 abilities) — `ewpa/ld-get-courses`, `ewpa/ld-get-course`, `ewpa/ld-get-user-progress`, `ewpa/ld-get-quiz-results` (read, enabled by default); `ewpa/ld-enroll-user`, `ewpa/ld-unenroll-user` (write, disabled by default). Requires LearnDash. Guard: `class_exists('SFWD_LMS')`. `learndash_get_course_users_access_from_meta()` wrapped in `function_exists()` for broad compatibility. * Fix: Per-post permission callbacks (get-post, get-page, get-cpt-item, update-post, delete-post, and 7 more) now return a descriptive `WP_Error` instead of bare `false`. Previously, a missing `post_id` parameter or a nonexistent post ID surfaced as a generic "Permission denied" with no detail — even for administrators — because `current_user_can()` with a per-post capability resolves to `do_not_allow` when the post does not exist. MCP clients now receive actionable messages: missing parameter, invalid post ID, or an actual capability denial. Reported in the support forum (bearer-token thread). * Updated: Total abilities: 68 in 15 categories = 2.0.20 = * New: `ewpa/get-seopress-content-analysis` — reads the SEOPress content analysis for a post or page: every check (meta title, meta description, headings, internal links, structured data, image alt texts, content depth, etc.) with its impact level (good/low/medium/high) and plain-text recommendation, plus a summary count and the target keywords. Optional `refresh=true` runs a fresh SEOPress analysis of the rendered page first (internally dispatching `GET seopress/v1/posts/{id}/content-analysis`), so agents can update content and immediately re-check the recommendations. Requires SEOPress 7.5+ (fresh analysis honors SEOPress 30 req/min per-user rate limit). Read-only; auto-enabled on upgrade. * Updated: Total abilities: 62 in 14 categories = 2.0.19 = * New: Elementor section (3 abilities) — `ewpa/elementor-get-structure` returns a compact, read-only tree of an Elementor page/template (element ids, types, text preview); `ewpa/elementor-update-element` edits the settings of an element by id (static content or styles), supporting single edits and a batch `edits[]` mode applied in one read/write pass; `ewpa/elementor-bind-dynamic-field` binds a widget setting to a dynamic tag (native Post Title, or a JetEngine/meta field). All three are opt-in (disabled by default). Requires Elementor (Elementor Pro for post-title, JetEngine for meta fields). All edits validate the tree, save with correct slashing, and clear the Elementor cache. * Updated: Total abilities: 61 in 14 categories = 2.0.18 = * Security: `ewpa/get-post`, `ewpa/get-page`, and `ewpa/get-cpt-item` now enforce a per-post visibility check (`current_user_can( 'read_post', $id )`) in their `permission_callback`, instead of only the site-wide `read` capability. Previously a low-privilege user authenticating via Application Passwords could read drafts, private, or password-protected content of other authors by ID (IDOR). Not exploitable via the Bearer token (which authenticates as an administrator). Reported by Hardik (hnanda21). = 2.0.17 = * Fix: `ewpa_filter_core_abilities()` wrapper closure now uses `$input = null` and calls `$original()` vs `$original($input)` conditionally — fixes `ArgumentCountError` on `core/get-user-info` and `core/get-environment-info` (PHP 8.4), which have no `input_schema` and are invoked with zero arguments by `WP_Ability::invoke_callback()`. `core/get-site-info` was unaffected because it declares an input schema. Same root cause as the v2.0.14 fix for `ewpa_register_ability_with_log()`. = 2.0.16 = * Fix: `ewpa_authenticate_api_key()` now uses a static re-entry guard (`$resolving`) to prevent infinite recursion when `user_can()` inside `ewpa_validate_api_key()` triggers `map_meta_cap`. Plugins like Yoast SEO hook `map_meta_cap` and call `wp_get_current_user()` from within it, re-entering the `determine_current_user` filter and causing unbounded recursion (PHP fatal / HTTP 500). Reproduced with Yoast SEO + WPML String Translation active. = 2.0.15 = * Enhancement: `ewpa/je-update-options-page-field` now supports repeater fields — pass an array of row objects where each key matches a sub-field name. `ewpa/je-get-options-page` now returns `repeater_fields` (name, title, type) for repeater fields so the AI can inspect the expected row structure before writing. = 2.0.14 = * New: JetEngine Options Pages section (3 abilities) — `ewpa/je-list-options-pages` lists all registered options pages with their field schema; `ewpa/je-get-options-page` returns field values for a given slug; `ewpa/je-update-options-page-field` writes a single field value with blocklist protection (html, tab, accordion, endpoint types are blocked). Both list and get abilities are enabled by default; update is off by default. Requires JetEngine with the Options Pages module enabled. * Fix: `ewpa_register_ability_with_log()` wrapper closure now uses `$input = null` (optional parameter) so abilities without an `input_schema` are not broken by PHP 8.4's `ArgumentCountError` when `WP_Ability::invoke_callback()` calls them with zero arguments. * Updated: Total abilities: 58 in 13 categories = 2.0.13 = * Fix: `ewpa/update-post`, `ewpa/create-post`, `ewpa/create-page`, `ewpa/create-cpt-item`, `ewpa/update-cpt-item`, `ewpa/search-replace`, and `ewpa/tec-update-event` now use `wp_slash()` instead of `wp_kses_post()` on post content before passing to `wp_insert_post()` / `wp_update_post()`. This prevents double-unslashing that corrupted JSON Unicode escapes (e.g. `<` → `u003c`) in Gutenberg block attributes such as Yoast FAQ questions. KSES is now applied by WordPress via the `content_save_pre` filter, which correctly respects `unfiltered_html` capability — allowing admins to save `