=== Enable Abilities for MCP - Changelog (2.0.25 and earlier) === Changes from 2.1.0 onwards are listed in readme.txt. = 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 `