feat(v3.13.0): settings live DB reads, remove Directus/Appwrite, admin stats

Settings fixes:
- MAX_SITES_PER_USER, USER_RATE_LIMIT_PER_MIN/HR now read from DB settings
  table (DB > ENV > default), so dashboard/settings changes apply without
  restart. Sync cache refreshed on every save or delete.
- /api/me reports the live DB value for max_sites_per_user.

Admin improvements:
- Admin users bypass per-user rate limiting entirely (role=admin or ADMIN_EMAILS).
- Admin Overview now shows platform stats: registered users, new users (7d),
  total user sites, available tools.

Plugin cleanup:
- Appwrite and Directus plugins removed from the active registry (8 plugins
  now: WordPress, WooCommerce, WordPress Specialist, Gitea, n8n, Supabase,
  OpenPanel, Coolify). Plugin code is retained for future re-enabling.
- Settings page plugin visibility list updated to match.

Mobile onboarding:
- Stepper steps on narrow viewports stack vertically with correct full border
  and rounded corners on each step.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-05-20 23:33:20 +02:00
parent f203ca88de
commit 43fd2201a0
223 changed files with 36183 additions and 4115 deletions

View File

@@ -4,11 +4,11 @@ Tags: mcp, ai, rest-api, seo, media
Requires at least: 5.0
Tested up to: 6.9
Requires PHP: 7.4
Stable tag: 2.9.0
Stable tag: 2.18.1
License: GPLv2 or later
License URI: https://www.gnu.org/licenses/gpl-2.0.html
Companion plugin for MCP Hub. REST API routes for SEO meta and raw-binary media uploads that bypass upload_max_filesize.
Companion plugin for MCP Hub. REST API routes for SEO meta, raw-binary media uploads that bypass upload_max_filesize, and the WordPress Specialist admin namespace.
== Description ==
@@ -51,6 +51,78 @@ Companion plugin for MCP Hub. REST API routes for SEO meta and raw-binary media
* `POST /wp-json/airano-mcp/v1/regenerate-thumbnails` — Regenerate attachment sub-sizes via `wp_generate_attachment_metadata` for a list of IDs or in paged batch mode (added in 2.8.0)
* `POST /wp-json/airano-mcp/v1/upload-and-attach` — Raw-body upload + metadata + attach-to-post + set-featured in a single REST call (added in 2.9.0). Query params: `attach_to_post`, `set_featured`, `title`, `alt_text`, `caption`, `description`. Permission: `upload_files` + `edit_post` on the target post.
*WordPress Specialist admin namespace (`airano-mcp/v1/admin/*`, added in 2.11.02.18.0)*
All admin routes require `manage_options`. They power the `wordpress_specialist` MCP plugin (companion-backed advanced WordPress management — no Docker socket required).
Read routes (2.11.02.14.0):
* `GET /wp-json/airano-mcp/v1/admin/plugins` — Every plugin known to WP with active/network-active/version/author/update flags (2.11.0)
* `GET /wp-json/airano-mcp/v1/admin/themes` — Installed themes with stylesheet/template/parent/block-theme/active flags (2.11.0)
* `GET /wp-json/airano-mcp/v1/admin/users?role=&search=&page=&per_page=` — Paginated user list (cap 200/call) (2.11.0)
* `GET /wp-json/airano-mcp/v1/admin/options/{name}` — Single option fetch with deny-list of credential-shaped keys (`*_secret`, `*_password`, `*_api_key`, `auth_*_key`, etc.) (2.11.0)
* `GET /wp-json/airano-mcp/v1/admin/cron` — Full cron table with epoch + ISO 8601 next_run, schedule slug, interval, args (2.11.0)
* `GET /wp-json/airano-mcp/v1/admin/maintenance` — `.maintenance` sentinel inspection (`enabled` / `started_at` / `stale`) (2.11.0)
* `GET /wp-json/airano-mcp/v1/admin/system-info` — WP/PHP/MySQL/server versions + memory + paths in one envelope (2.12.0)
* `GET /wp-json/airano-mcp/v1/admin/phpinfo` — Sorted extension list + curated ini whitelist + disabled functions + opcache state (2.12.0)
* `GET /wp-json/airano-mcp/v1/admin/disk-usage` — Bytes for uploads/plugins/themes + filesystem `disk_total/free/used` (bounded 200k files/5s per tree) (2.12.0)
* `GET /wp-json/airano-mcp/v1/admin/elementor/status` — Elementor presence + version + Pro flag + supported post types; `installed:false` cleanly when absent (2.13.0)
* `GET /wp-json/airano-mcp/v1/admin/elementor/{post_id}` — Parsed `_elementor_data` (slash-stripped, JSON-decoded) (2.13.0)
* `GET /wp-json/airano-mcp/v1/admin/elementor/templates` — Saved Elementor templates (id/title/type/modified) (2.13.0)
* `GET /wp-json/airano-mcp/v1/admin/themes/files/{slug}?glob=&max_files=` — Theme directory walk (path/size/mime/sha256/modified per file; capped at 1000 files/call) (2.14.0)
* `GET /wp-json/airano-mcp/v1/admin/themes/files/{slug}/{path}` — Single theme file as base64 + sha256 + mime (5 MB cap) (2.14.0)
Page-editing write routes (2.13.0). All require `manage_options` AND `edit_post` on the target post (S-12); block + classic content is sanitised via `wp_kses_post` unless the caller has `unfiltered_html` (S-13); Elementor JSON node count capped at 5,000 per call (S-14).
* `POST /wp-json/airano-mcp/v1/admin/blocks/replace` — Replace post block tree; companion runs `serialize_blocks()` server-side (max 200 blocks)
* `POST /wp-json/airano-mcp/v1/admin/blocks/insert` — Insert one block at index N
* `POST /wp-json/airano-mcp/v1/admin/blocks/remove` — Remove block at index N (returns the removed block for rollback)
* `POST /wp-json/airano-mcp/v1/admin/elementor/{post_id}` — Replace `_elementor_data`; fires `elementor/document/after_save`
* `POST /wp-json/airano-mcp/v1/admin/elementor/{post_id}/regen-css` — Trigger per-post Elementor CSS regeneration
* `POST /wp-json/airano-mcp/v1/admin/elementor/templates/apply` — Copy a saved template into a target post
* `POST /wp-json/airano-mcp/v1/admin/classic/{post_id}/replace` — Pure post_content swap for classic-editor sites
Theme dev write routes (2.14.0). Per-route capability checks layer on top of `manage_options`: `install_themes` for install, `switch_themes` for activate, `delete_themes` for theme delete, `edit_themes` for file CRUD. PHP file writes additionally require `!DISALLOW_FILE_EDIT` (S-17). theme_slug must match `wp_get_themes()` (S-15). File paths canonicalise via `realpath()` and must stay under `wp-content/themes/{slug}` (S-16). Caps: 5 MB/file, 50 MB/install zip (S-18). Optimistic concurrency on `expected_sha256` returns `sha_mismatch` (409) on drift (S-19).
* `POST /wp-json/airano-mcp/v1/admin/themes/install` — Install theme from `zip_url` or inline `zip_base64`; supports `activate` + `overwrite`. Companion runs WP core's `Theme_Upgrader`.
* `POST /wp-json/airano-mcp/v1/admin/themes/{slug}/activate` — Switch the active theme.
* `DELETE /wp-json/airano-mcp/v1/admin/themes/{slug}` — Delete an installed theme (refuses if active or active parent).
* `PUT /wp-json/airano-mcp/v1/admin/themes/files/{slug}/{path}` — Write a theme file (base64 body, optional `expected_sha256`, optional `create_dirs`).
* `DELETE /wp-json/airano-mcp/v1/admin/themes/files/{slug}/{path}` — Delete a theme file (refuses to delete `style.css` of the active theme).
Plugin write management routes (2.15.0). Per-route capability checks layer on `manage_options`: `install_plugins` for install / update, `activate_plugins` for activate / deactivate, `delete_plugins` for delete. New security rules: S-20 refuses to deactivate / delete the Airano MCP Bridge companion itself (would brick the MCP connection); S-21 refuses to deactivate / delete plugins marked `Required: yes` in their header. Reuses S-15 (slug whitelist via `get_plugins()`) + S-18 (50 MB cap on install zip).
* `POST /wp-json/airano-mcp/v1/admin/plugins/install` — Install via wp.org `slug`, OR via `zip_url`, OR via inline `zip_base64` (mutually exclusive). Supports `activate` + `overwrite`. Companion runs WP core's `Plugin_Upgrader`.
* `POST /wp-json/airano-mcp/v1/admin/plugins/{slug}/activate` — Activate an installed plugin. Optional `network_wide` for multisite.
* `POST /wp-json/airano-mcp/v1/admin/plugins/{slug}/deactivate` — Deactivate. Refuses companion (S-20) and `Required` plugins (S-21).
* `POST /wp-json/airano-mcp/v1/admin/plugins/{slug}/update` — Update to latest wp.org version. Returns `up_to_date:true` cleanly when no update available.
* `DELETE /wp-json/airano-mcp/v1/admin/plugins/{slug}` — Delete. Refuses active plugins, the companion, and `Required` plugins.
Site config routes (2.16.0). All gated on `manage_options`. Writes route every option through WP's own `sanitize_option_*` hooks; permalink writes additionally call `flush_rewrite_rules()`.
* `GET /wp-json/airano-mcp/v1/admin/site/identity` — title / tagline / site_icon / custom_logo / charset / WP version / admin_email / language / siteurl
* `POST /wp-json/airano-mcp/v1/admin/site/identity` — Update title / tagline / site_icon_id / custom_logo_id (any subset). Attachment ids are validated against the media library.
* `GET /wp-json/airano-mcp/v1/admin/site/reading` — show_on_front / page_on_front / page_for_posts / posts_per_page / posts_per_rss / blog_public
* `POST /wp-json/airano-mcp/v1/admin/site/reading` — Update reading settings. page_on_front / page_for_posts must reference published Pages.
* `GET /wp-json/airano-mcp/v1/admin/permalinks` — permalink_structure / category_base / tag_base
* `POST /wp-json/airano-mcp/v1/admin/permalinks` — Update permalink structure (empty string = plain). Calls `flush_rewrite_rules()` after the write so the new structure takes effect immediately.
Site layout routes (2.17.0). All gated on `manage_options`. Per-request capability checks layer on top per the new security rules: S-22 dispatches per nav-menu item type (`post_type` checks `read_post`; `taxonomy` allows public taxonomies and otherwise requires `assign_terms`; `custom` URL items skip the object check); S-23 sanitises widget HTML via `wp_kses_post` unless caller has `unfiltered_html`; S-24 customizer apply requires the `customize` cap (not just `manage_options`).
* `GET /wp-json/airano-mcp/v1/admin/menus` — Every nav menu with id / name / slug / theme locations / item count.
* `GET /wp-json/airano-mcp/v1/admin/menus/{menu_id}` — One menu's items: `{id, title, type, object, object_id, parent, order, url, target, classes, xfn}`.
* `PUT /wp-json/airano-mcp/v1/admin/menus/{menu_id}` — Full-replace items + optional rename. Slug stays frozen so theme_location mapping survives. Two-pass: every item validated against S-22 before any mutation.
* `GET /wp-json/airano-mcp/v1/admin/widgets/areas` — Every registered sidebar with id / name / theme_location / widget_count / kind (`block` or `legacy`).
* `GET /wp-json/airano-mcp/v1/admin/widgets/{area_id}` — One area's widgets. Block-kind returns `{id, type:'block', blocks: [...parsed], raw: '<!-- wp:... -->'}`; legacy-kind returns `{id, type, settings: {...}}` keyed by the widget's option store.
* `PUT /wp-json/airano-mcp/v1/admin/widgets/{area_id}` — Full replace. Block areas accept `{raw}` (or `{blocks}` server-serialised); legacy areas accept `text` widget settings only this round (other legacy types remain read-only). Caller-side `kind` is ignored — area kind is determined by the area itself.
* `POST /wp-json/airano-mcp/v1/admin/customizer/changeset` — `{action: get|apply|discard}`. `get` returns the pending changeset; `apply` publishes it; `discard` trashes it. Empty changeset returns `{status: 'empty'}` 200.
Database inspection routes (2.18.0). All gated on `manage_options`. New security rule S-25: db/search wraps `WP_Query` with `s=$query` (NEVER raw SQL); query is sanitised via `sanitize_text_field` and length-capped at 200 chars; `WP_Query`'s own `posts_clauses` filter (the same gate the WP search page uses) keeps non-readable posts out of the result set.
* `GET /wp-json/airano-mcp/v1/admin/db/size` — Aggregate database size. Single `information_schema.TABLES` aggregation scoped to the WP table prefix; returns `{database_bytes, table_count, row_count_estimate, database_name, table_prefix}`. No SQL exposure: caller doesn't pick the query.
* `GET /wp-json/airano-mcp/v1/admin/db/tables` — Per-table breakdown. Same source as `db/size`, one row per WP table; returns `{database_name, table_prefix, tables: [{name, engine, rows, data_bytes, index_bytes, total_bytes, collation}]}` ordered by total_bytes descending.
* `POST /wp-json/airano-mcp/v1/admin/db/search` — Full-text search wrapper around `WP_Query`. Body `{query, post_type?, status?, limit?}` (limit default 20, max 100). Returns `{query, limit, count, hits: [{id, post_type, status, title, snippet, url, modified}]}`.
**Designed for [MCP Hub](https://github.com/airano-ir/mcphub)** — the AI-native management hub for WordPress and self-hosted services.
== Installation ==
@@ -92,6 +164,33 @@ Yes. All REST API endpoints work with any application that can make authenticate
== Changelog ==
= 2.18.1 =
* WordPress.org review fixes: drop the unused `wp-admin/includes/media.php` include from `/upload-chunk` and `/upload-and-attach` (only helpers from `file.php` and `image.php` are actually used). Split the `/upload-and-attach` permission_callback into its own method (`require_upload_and_attach_capability`) that explicitly checks `current_user_can('edit_post', $attach_to_post)` at the route gate when `attach_to_post` is supplied, and rejects `set_featured` without a target. The per-target check is no longer hidden inside the callback body.
= 2.18.0 =
* F.19.3.2-.3 — Database inspection (read) + bulk fan-out (write). Three new admin routes: `GET /admin/db/size` (single `information_schema.TABLES` aggregation: database_bytes + table_count + row_count_estimate, no SQL exposure), `GET /admin/db/tables` (per-table breakdown: name, engine, rows, data_bytes, index_bytes, total_bytes, collation), `POST /admin/db/search` body `{query, post_type?, status?, limit?}` wraps `WP_Query` with `s=$query` (NEVER raw SQL), `query` sanitised via `sanitize_text_field` and capped at 200 chars server-side, `limit` capped at 100. New security rule S-25 — db/search wraps `WP_Query`, never raw SQL; `posts_clauses` filter (same as the WP search page) keeps non-readable posts (private/draft from other authors) out of the result set. The bulk write tools (`wp_bulk_post_update`, `wp_bulk_term_update` on MCP Hub side) ride stock REST `wp/v2/posts/{id}` and `wp/v2/{taxonomy}/{id}` — no companion routes needed for those, but the cap of 50 items per call is documented under S-26 in the MCP Hub spec. All admin routes gated on `manage_options`. This release is bundled with the wordpress_advanced sunset on the MCP Hub side: the deprecated WP-CLI/Docker-socket plugin is removed in F.19.3.2-.3 (2026-05-04); `wordpress_specialist` is now the only WP-management surface.
= 2.17.0 =
* F.19.6.B — Site layout (menus + widgets + customizer). Adds seven new admin routes: `GET /admin/menus`, `GET /admin/menus/{id}`, `PUT /admin/menus/{id}` (full-replace items + optional rename, slug stays frozen), `GET /admin/widgets/areas`, `GET /admin/widgets/{area_id}`, `PUT /admin/widgets/{area_id}` (block areas accept any block raw HTML; legacy areas accept `text` widget settings only — other legacy types remain read-only this round), and `POST /admin/customizer/changeset` (single-tool wrapper around the changeset queue with `{action: get|apply|discard}`). New security rules: S-22 dispatches per nav-menu item type — `post_type` checks `current_user_can('read_post', id)`, `taxonomy` allows public taxonomies and otherwise requires the taxonomy's `assign_terms` cap (deliberately NOT `manage_categories` which is a write cap), `custom` URL items skip the object validation; refusals surface as `forbidden_object_id` 403/404. S-23 sanitises widget HTML via `wp_kses_post` unless the caller has `unfiltered_html` (mirrors S-13 from F.19.5). S-24 customizer apply requires the `customize` cap (same bar as `/wp-admin/customize.php`); `manage_options` alone is not enough. All routes gated on `manage_options`. Pre-validates every menu item before mutating so a partial S-22 failure leaves the menu untouched. Customizer apply is racy with concurrent edits via the customizer UI — intentional, mirrors WP behaviour.
= 2.16.0 =
* F.19.6.A — Site config surface. Six new admin routes covering Settings → General + Reading + Permalinks: `GET/POST /admin/site/identity` (title / tagline / site_icon / custom_logo), `GET/POST /admin/site/reading` (show_on_front / page_on_front / page_for_posts / posts_per_page / posts_per_rss / blog_public), `GET/POST /admin/permalinks` (permalink_structure / category_base / tag_base). All gated on `manage_options`. POST routes validate attachment ids against the media library, reject non-published page ids for page_on_front / page_for_posts, and route every option write through WP's own `sanitize_option_*` hooks. After a permalink_structure write `flush_rewrite_rules()` runs so the new structure takes effect immediately. First consumer of the `settings` tier introduced by F.19.2.0.
= 2.15.0 =
* F.19.2.1 — Plugin write management. Adds five new admin routes — `POST /admin/plugins/install` (accepts `{slug}` for wp.org install OR `{zip_url|zip_base64}` for arbitrary zip), `POST /admin/plugins/{slug}/activate`, `POST /admin/plugins/{slug}/deactivate`, `POST /admin/plugins/{slug}/update`, `DELETE /admin/plugins/{slug}`. Per-route capability checks: `install_plugins` (install / update), `activate_plugins` (activate / deactivate), `delete_plugins` (delete). New security rules: S-20 refuses to deactivate or delete the Airano MCP Bridge companion itself (would brick the MCP connection — operators must use WP-Admin → Plugins instead); S-21 refuses to deactivate / delete plugins marked `Required: yes` in their header (must-use plugins shipped by some managed hosts). Reuses S-15 (slug whitelist via `get_plugins()`) + S-18 (50 MB cap on install zip). Refuses to delete an active plugin (caller must deactivate first). Update route returns `up_to_date:true` cleanly when no update is available.
= 2.14.0 =
* F.19.7 — Theme dev surface (install + activate + delete + file CRUD). Adds three theme management routes — `POST /admin/themes/install` (accepts either `zip_url` or `zip_base64`; runs WP core's `Theme_Upgrader`), `POST /admin/themes/{slug}/activate`, `DELETE /admin/themes/{slug}` — and four file CRUD routes — `GET /admin/themes/files/{slug}` (glob walk capped at 1000 files), `GET /admin/themes/files/{slug}/{path}` (file read as base64), `PUT /admin/themes/files/{slug}/{path}` (file write with optional `expected_sha256` for optimistic concurrency), `DELETE /admin/themes/files/{slug}/{path}`. New security rules: `theme_slug` must match `wp_get_themes()` (S-15); paths canonicalise via `realpath()` and must stay under `wp-content/themes/{slug}`, blocking `..`, symlinks, absolute paths, null bytes (S-16); PHP file writes additionally require `!DISALLOW_FILE_EDIT` (S-17); per-call caps 5 MB/file, 1000 files/list, 50 MB/zip (S-18); optimistic concurrency via `expected_sha256` returns `sha_mismatch` (409) on drift (S-19). All routes gated on `manage_options` route-side; per-handler capability checks add `install_themes` / `switch_themes` / `delete_themes` / `edit_themes`. Refuses to delete the active theme, the active parent theme, or `style.css` of the active theme.
= 2.13.0 =
* F.19.5 — Page editing surface (Gutenberg + Elementor + Classic). Adds seven write routes — `POST /admin/blocks/{replace,insert,remove}`, `POST /admin/elementor/{post_id}` (set), `POST /admin/elementor/{post_id}/regen-css`, `POST /admin/elementor/templates/apply`, `POST /admin/classic/{post_id}/replace` — and three new read routes — `GET /admin/elementor/status`, `GET /admin/elementor/{post_id}`, `GET /admin/elementor/templates`. Writes require `manage_options` AND `edit_post` on the target post (S-12). Block + classic content is sanitised via `wp_kses_post` unless the caller has `unfiltered_html` (S-13). Elementor JSON node count capped at 5,000 per call; oversized payloads return `elementor_too_large` (S-14). All Elementor writes fire `elementor/document/after_save` so caches and CSS regenerate cleanly.
= 2.12.0 =
* F.19.3.1 — Three more read-only admin routes ported from the legacy `wordpress_advanced` WP-CLI tools so they can run via the companion instead of `docker exec`. `GET /admin/system-info` (PHP/MySQL/WordPress versions + server software + memory limits), `GET /admin/phpinfo` (extension list + curated ini snapshot — never the full `phpinfo()` HTML which would leak server internals), `GET /admin/disk-usage` (uploads/plugins/themes/total bytes + `disk_free_space`, bounded at 200k files / 5s wall clock per tree). All gated on `manage_options`.
= 2.11.0 =
* F.19.1 — New read-only admin namespace `airano-mcp/v1/admin/*` for the WordPress Specialist tool surface. Six new routes — `plugins`, `themes`, `users`, `options/{name}`, `cron`, `maintenance` — all gated on `manage_options`. `options` route uses a deny-list of credential-shaped suffixes (`*_secret`, `*_password`, `*_api_key`, `*_token`, `auth_*_key`, `auth_*_salt`) on top of the cap check. `users` route paginates up to 200/call with optional `role` + `search` filters. No state changes in this version; write operations land in F.19.2.
= 2.9.0 =
* Added `POST /wp-json/airano-mcp/v1/upload-and-attach` — same raw-body semantics as `/upload-chunk`, but accepts query params (`attach_to_post`, `set_featured`, `title`, `alt_text`, `caption`, `description`) and applies them in one PHP round-trip. Saves 2-3 REST calls per upload. Per-target permission enforced via `current_user_can('edit_post', attach_to_post)`.
* `status` + `capabilities` routes now advertise `upload_and_attach: true` alongside the existing capability flags.
@@ -146,6 +245,33 @@ Yes. All REST API endpoints work with any application that can make authenticate
== Upgrade Notice ==
= 2.18.1 =
WordPress.org review fixes only — no behaviour changes. Drops a redundant core file include and tightens the `/upload-and-attach` permission gate so the per-target `edit_post` check runs before the callback (visible to static analysis).
= 2.18.0 =
Adds the F.19.3.2-.3 database inspection surface (3 new admin read routes — db/size / db/tables / db/search) wrapping `information_schema.TABLES` + `WP_Query`. NEVER exposes raw SQL (S-25 — query is sanitised and length-capped). All gated on `manage_options`. No breaking changes; existing routes unchanged.
= 2.17.0 =
Adds the F.19.6.B site layout surface (7 new admin routes — menus / widgets / customizer changeset). New security rules S-22 (per-item caps for nav-menu object refs), S-23 (`wp_kses_post` on widget HTML unless `unfiltered_html`), S-24 (customizer apply requires `customize` cap). No breaking changes; existing routes unchanged.
= 2.16.0 =
Adds the F.19.6.A site config surface (6 new admin routes — identity / reading / permalinks). All gated on `manage_options`. Permalink writes flush rewrite rules automatically. No breaking changes; existing routes unchanged.
= 2.15.0 =
Adds the F.19.2.1 plugin write management surface (5 new admin routes — install / activate / deactivate / update / delete). Per-route checks for `install_plugins` / `activate_plugins` / `delete_plugins`. Refuses to deactivate or delete the companion plugin itself. No breaking changes; existing routes unchanged.
= 2.14.0 =
Adds the F.19.7 theme dev surface (7 new admin routes — install / activate / delete + file CRUD). Per-route checks for `install_themes` / `switch_themes` / `delete_themes` / `edit_themes`; PHP file writes additionally require `!DISALLOW_FILE_EDIT`. No breaking changes; existing routes unchanged.
= 2.13.0 =
Adds the F.19.5 page editing surface (10 new admin routes — Gutenberg blocks, Elementor read/write/template, Classic html replace). Writes require `manage_options` AND `edit_post` on the target post; raw HTML is gated on the caller also holding `unfiltered_html`. No breaking changes; existing routes unchanged.
= 2.12.0 =
Adds three more read-only admin routes (`/admin/system-info`, `/admin/phpinfo`, `/admin/disk-usage`). They replace the legacy WP-CLI / Docker-socket flow used by the `wordpress_advanced` MCP plugin so admins on shared hosting can use those tools too. No breaking changes.
= 2.11.0 =
Adds the `airano-mcp/v1/admin/*` namespace (six read routes) for the new WordPress Specialist MCP tool surface. No breaking changes; existing routes unchanged. Activate the plugin and any admin Application Password gets the new routes.
= 2.9.0 =
Adds `POST /airano-mcp/v1/upload-and-attach` — upload + metadata + attach + featured in one REST round-trip. No breaking changes.