style(copy): remove em dashes from plugin copy, fix mcpb display name

Sweeps shipped copy clean of em dashes: readme.txt, README.md,
CHANGELOG.md, the bundled sample prompts, and user-facing PHP strings
(tool descriptions, admin labels). PHP was swept on non-comment lines
only, so developer docblocks and inline comments are untouched.
Internal dev docs (CLAUDE.md, docs/) were deliberately left alone.

Replacements are contextual: colons for label/definition patterns,
commas for asides, en dashes for numeric ranges.

The .mcpb bundle display_name now reads "EMCP Tools - <host>" with a
plain hyphen; its test is updated to match. Fixing CHANGELOG.md also
clears the em dashes from the website's generated changelog on the
next site build, since that file is its source.

Verified: php -l clean on every touched file, pro suite 1559 pass,
public suite 102 pass. Bumps the pro submodule for the Pro-side sweep.
This commit is contained in:
Mian Shahzad Raza
2026-07-21 08:03:19 +05:00
parent 940847c34d
commit 0ce420601b
82 changed files with 503 additions and 503 deletions
+21 -21
View File
@@ -4,15 +4,15 @@ All notable changes to MCP Tools for Elementor are documented in this file.
## [3.5.1]
> A redesigned AI Chat with local models, vision, and skills — plus three connectivity/builder fixes.
> A redesigned AI Chat with local models, vision, and skills, plus three connectivity/builder fixes.
### Added
- **AI Chat: a full-width, two-column settings screen (Pro).** Left column holds the built-in controls (capability & safety switches, tool-group access toggles, and the default-model selector); right column holds the providers. Cleaner, and it scales as providers grow.
- **AI Chat: more providers + local runtimes (Pro).** Added **DeepSeek**, **Moonshot**, and **Z.ai** as hosted providers, plus **Ollama** and **LM Studio** as **local runtimes** — auto-detected on `localhost`, no API key needed. Run a fully private model against your own site.
- **AI Chat: refresh-models button (Pro).** Pull a provider's current model list on demand — newly installed local models show up without reconnecting.
- **AI Chat: more providers + local runtimes (Pro).** Added **DeepSeek**, **Moonshot**, and **Z.ai** as hosted providers, plus **Ollama** and **LM Studio** as **local runtimes**: auto-detected on `localhost`, no API key needed. Run a fully private model against your own site.
- **AI Chat: refresh-models button (Pro).** Pull a provider's current model list on demand, newly installed local models show up without reconnecting.
- **AI Chat: remembers your last provider & model (Pro).** Your selection persists across reloads instead of resetting to the default each time.
- **AI Chat: EMCP skills in the chat (Pro).** The bundled agent skills (page-building playbooks, per-plugin guides) are loaded into the chat so the assistant follows the field-tested patterns.
- **AI Chat: vision for text-only models (Pro).** When the selected model has no vision, images are handled two ways — a local vision model (private, if you have one) describes them, and a **built-in image reader** (dependency-free: dimensions, format, transparency, dominant-colour palette) always provides usable context as a fallback.
- **AI Chat: vision for text-only models (Pro).** When the selected model has no vision, images are handled two ways, a local vision model (private, if you have one) describes them, and a **built-in image reader** (dependency-free: dimensions, format, transparency, dominant-colour palette) always provides usable context as a fallback.
### Changed
- **AI Chat: hardened for small local models (Pro).** Lean/compact tool schemas and capped oversized tool results keep the context window under control; empty-turn recovery and reasoning-stream capture stop "thinking" models from stalling; a one-page-per-request rule and explicit background-format guidance keep builds on track. `list-skills` returns a compact catalog and `emcp-skills` was split into a lean core plus on-demand sub-skills.
@@ -21,7 +21,7 @@ All notable changes to MCP Tools for Elementor are documented in this file.
- **Mixed MCP-adapter versions no longer break the server (#99).** When another active plugin (e.g. Rank Math) bundles a different version of the WordPress MCP Adapter, PHP's one-class-per-name rule caused a namespace shear that surfaced as `McpServerError: Session terminated`. EMCP now serves the entire `WP\MCP\` namespace from its own bundled copy, so the server initialises regardless of load order. Opt out with `EMCP_TOOLS_NO_ADAPTER_PRELOAD`.
- **Duplicating a v4 atomic element no longer drops its styles (#97).** `duplicate-element` now re-mints the element's local style classes (`e-<id>-<hash>`) so the copy keeps its own styling instead of colliding with the original.
- **Silent-save guard (#98).** When Elementor's document save returns success but persists nothing (CLI/proxy contexts), the data layer now verifies the write and falls back so page edits aren't silently lost.
- **Backgrounds set as a nested/array shorthand now render.** Agents commonly emit a container/widget background as a nested `background: { background_image: [{id,url}], … }` group without the `background_background: "classic"` activator — which Elementor silently ignores, so a sideloaded photo never appeared. The factory now coerces this to the flat keys Elementor reads (single image object + activator).
- **Backgrounds set as a nested/array shorthand now render.** Agents commonly emit a container/widget background as a nested `background: { background_image: [{id,url}], … }` group without the `background_background: "classic"` activator, which Elementor silently ignores, so a sideloaded photo never appeared. The factory now coerces this to the flat keys Elementor reads (single image object + activator).
- **`build-page` no longer silently drops shorthand nodes.** It accepts intuitive widget/container shorthand and returns warnings for anything it had to coerce, instead of quietly skipping it.
- **`sideload-image` resolves Unsplash download URLs.** Passing an `api.unsplash.com/photos/<id>/download` URL (which 401s without a key) is now resolved to the real image when a key is present, with actionable errors that steer to the direct `search-images` URL or the `add-stock-image` composite.
- **Clearer web-fetch errors.** SSL/timeout failures now return actionable guidance, with an opt-out for dev boxes with broken CA bundles.
@@ -31,41 +31,41 @@ All notable changes to MCP Tools for Elementor are documented in this file.
> Forms + SEO plugin integrations (15 plugins), clearer Pro tool sections, a redesigned Dashboard usage panel, and an organized Plugins tab.
### Added
- **SEO plugin integrations — 7 SEO plugins.** Two dispatcher tools per plugin — `<plugin>-read` / `<plugin>-write` — for **Slim SEO (free)** plus **Yoast SEO, Rank Math, All in One SEO, SEOPress, The SEO Framework, and SureRank (Pro)**. Read and write the SEO metadata each plugin stores — SEO title, meta description, canonical, robots (noindex/nofollow), focus keyword, and Open Graph / Twitter social fields — for posts and terms, over a **unified field vocabulary** (the adapter translates to each plugin's own storage). Also reads schema/structured data where the plugin supports it (Rank Math, AIOSEO, SEOPress). Each pair registers only when its plugin is active; reads on by default, writes disabled-by-default. Distinct from the Pro SEO & Accessibility toolkit (which audits/generates). Pro bundles an `emcp-plugins/seo` agent skill. Built verification-first — every adapter's field mapping was written against each plugin's real storage and exercised end-to-end. New **SEO** section on the Tools → Plugins tab.
- **Forms plugin integrations — 8 form builders.** Two dispatcher tools per plugin — `<plugin>-read` / `<plugin>-write` — for **Contact Form 7 (free)** plus **WPForms, Gravity Forms, Fluent Forms, Ninja Forms, Formidable Forms, MetForm, and SureForms (Pro)**. Read forms, fields, notifications, and entries (submissions); write notifications/settings and manage entries. Each pair registers only when its plugin is active; reads on by default, writes disabled-by-default, and `delete-entry` requires `confirm:true`. Entries carry personal data and are gated on the plugin's own view/delete capabilities. CF7 has no entries (it stores none), and WPForms entry operations require WPForms Pro (Lite has no entries — they return a clear message). Enable a plugin's **Write** tool under EMCP Tools → Tools → Plugins → Forms. Pro also bundles an `emcp-plugins/forms` agent skill that teaches the whole model. Built verification-first — every adapter's operations were written against each plugin's real API and exercised end-to-end.
- **SEO plugin integrations, 7 SEO plugins.** Two dispatcher tools per plugin, `<plugin>-read` / `<plugin>-write`, for **Slim SEO (free)** plus **Yoast SEO, Rank Math, All in One SEO, SEOPress, The SEO Framework, and SureRank (Pro)**. Read and write the SEO metadata each plugin stores, SEO title, meta description, canonical, robots (noindex/nofollow), focus keyword, and Open Graph / Twitter social fields, for posts and terms, over a **unified field vocabulary** (the adapter translates to each plugin's own storage). Also reads schema/structured data where the plugin supports it (Rank Math, AIOSEO, SEOPress). Each pair registers only when its plugin is active; reads on by default, writes disabled-by-default. Distinct from the Pro SEO & Accessibility toolkit (which audits/generates). Pro bundles an `emcp-plugins/seo` agent skill. Built verification-first, every adapter's field mapping was written against each plugin's real storage and exercised end-to-end. New **SEO** section on the Tools → Plugins tab.
- **Forms plugin integrations, 8 form builders.** Two dispatcher tools per plugin, `<plugin>-read` / `<plugin>-write`, for **Contact Form 7 (free)** plus **WPForms, Gravity Forms, Fluent Forms, Ninja Forms, Formidable Forms, MetForm, and SureForms (Pro)**. Read forms, fields, notifications, and entries (submissions); write notifications/settings and manage entries. Each pair registers only when its plugin is active; reads on by default, writes disabled-by-default, and `delete-entry` requires `confirm:true`. Entries carry personal data and are gated on the plugin's own view/delete capabilities. CF7 has no entries (it stores none), and WPForms entry operations require WPForms Pro (Lite has no entries, they return a clear message). Enable a plugin's **Write** tool under EMCP Tools → Tools → Plugins → Forms. Pro also bundles an `emcp-plugins/forms` agent skill that teaches the whole model. Built verification-first, every adapter's operations were written against each plugin's real API and exercised end-to-end.
### Changed
- **Pro tool sections are now visible (and clearly marked) on free builds.** WooCommerce, SEO & Accessibility, and Widget Builder used to either show interactive toggles with no "Pro" indication (WooCommerce) or not appear at all (SEO / Widget Builder). They now always appear on the Tools tab with a **Pro** badge, their toggles **disabled**, and a **"Requires EMCP Pro — Upgrade"** link — so it's clear what Pro unlocks. No functional change: the Pro abilities were already license-gated and never callable on free.
- **Dashboard "Your usage" is now a 4-card activity panel.** The single wide, mostly-empty card is split into four focused cards — **Usage** (templates applied / prompts copied), **History** (changes recorded, rolled back, last change), **Most used** (top actions from the change ledger), and **Sandbox** (PHP snippet count) — each linking to its tab. History, Most-used, and Sandbox are free features, so the panel now shows on free builds too (the Usage card carries a Pro badge there).
- **Plugins tab is now grouped by category.** Plugin integrations are clustered under group headings — **Dynamic Content** (ACF, Meta Box), **E-Commerce** (WooCommerce), with **Forms** and more to come — with each plugin shown as a self-contained card and slimmer toggle cards inside. Keeps the tab organized as the number of integrations grows.
- **Pro tool sections are now visible (and clearly marked) on free builds.** WooCommerce, SEO & Accessibility, and Widget Builder used to either show interactive toggles with no "Pro" indication (WooCommerce) or not appear at all (SEO / Widget Builder). They now always appear on the Tools tab with a **Pro** badge, their toggles **disabled**, and a **"Requires EMCP Pro, Upgrade"** link, so it's clear what Pro unlocks. No functional change: the Pro abilities were already license-gated and never callable on free.
- **Dashboard "Your usage" is now a 4-card activity panel.** The single wide, mostly-empty card is split into four focused cards, **Usage** (templates applied / prompts copied), **History** (changes recorded, rolled back, last change), **Most used** (top actions from the change ledger), and **Sandbox** (PHP snippet count), each linking to its tab. History, Most-used, and Sandbox are free features, so the panel now shows on free builds too (the Usage card carries a Pro badge there).
- **Plugins tab is now grouped by category.** Plugin integrations are clustered under group headings, **Dynamic Content** (ACF, Meta Box), **E-Commerce** (WooCommerce), with **Forms** and more to come, with each plugin shown as a self-contained card and slimmer toggle cards inside. Keeps the tab organized as the number of integrations grows.
## [3.4.2]
> WooCommerce + Meta Box integrations, template/prompt usage insights, and History delete.
### Added
- **WooCommerce Pro integration.** Two dispatcher tools — `woo-read` / `woo-write` — expose the **full WooCommerce `wc/v3` surface** (~120 operations): products (+ variations, categories, tags, attributes, reviews), orders (+ notes, refunds), customers, coupons, reports, settings, payment gateways, shipping, taxes, webhooks, and system status. Operations run through WooCommerce's own REST controllers (HPOS-safe, no privilege escalation). Reads are enabled by default; **writes ship disabled-by-default**, and money/irreversible operations (refunds, deletes, batch) additionally require `confirm:true`. Registers only when WooCommerce is active; enable **WooCommerce Write** under EMCP Tools → Tools → Plugins.
- **Meta Box integration (free, community PR #96).** Two dispatcher tools — `metabox-read` / `metabox-write` — read and write [Meta Box](https://metabox.io/) custom-field values and discover its field groups over MCP, targeting a post (`post_id`) or any object (`object_type` + `object_id`). Values only — no delete and no field-group/CPT authoring (Meta Box free core has no DB field builder), and unknown fields are skipped rather than created. Only registers when Meta Box is active; `metabox-read` on by default, `metabox-write` disabled-by-default. Thanks @Mrshahidali420.
- **WooCommerce Pro integration.** Two dispatcher tools, `woo-read` / `woo-write`, expose the **full WooCommerce `wc/v3` surface** (~120 operations): products (+ variations, categories, tags, attributes, reviews), orders (+ notes, refunds), customers, coupons, reports, settings, payment gateways, shipping, taxes, webhooks, and system status. Operations run through WooCommerce's own REST controllers (HPOS-safe, no privilege escalation). Reads are enabled by default; **writes ship disabled-by-default**, and money/irreversible operations (refunds, deletes, batch) additionally require `confirm:true`. Registers only when WooCommerce is active; enable **WooCommerce Write** under EMCP Tools → Tools → Plugins.
- **Meta Box integration (free, community PR #96).** Two dispatcher tools, `metabox-read` / `metabox-write`, read and write [Meta Box](https://metabox.io/) custom-field values and discover its field groups over MCP, targeting a post (`post_id`) or any object (`object_type` + `object_id`). Values only, no delete and no field-group/CPT authoring (Meta Box free core has no DB field builder), and unknown fields are skipped rather than created. Only registers when Meta Box is active; `metabox-read` on by default, `metabox-write` disabled-by-default. Thanks @Mrshahidali420.
- **History: delete entries.** The History tab now has a **Delete** action on each row and a **Clear all** button, so you can prune or wipe the change ledger. Both are confirm-gated, and deleting an entry that could still be rolled back warns you first (the before-image lives in the entry, so deleting it forfeits the rollback).
- **Usage counts on the Pro library.** Each template card in the Pro Templates library now shows how many times it has been applied across all sites ("Used N times"), so you can see what's popular. The **Dashboard** gains a **Your usage** widget — templates you've applied and prompts you've copied on this site, plus the globally-popular templates. Powered by lightweight, best-effort telemetry to emcptools.com (license + site only, non-blocking); no page content, customer data, or AI conversations are ever sent.
- **Usage counts on the Pro library.** Each template card in the Pro Templates library now shows how many times it has been applied across all sites ("Used N times"), so you can see what's popular. The **Dashboard** gains a **Your usage** widget, templates you've applied and prompts you've copied on this site, plus the globally-popular templates. Powered by lightweight, best-effort telemetry to emcptools.com (license + site only, non-blocking); no page content, customer data, or AI conversations are ever sent.
### Fixed
- **Affiliate Program link broke on fresh installs.** The header linked to the Freemius Affiliation page unconditionally, but Freemius only registers that page once the plugin leaves *activation mode* — i.e. after the user opts in or skips. On a fresh install (free especially) the link led to "Sorry, you are not allowed to access this page." The link now appears only when the page actually exists.
- **Affiliate Program link broke on fresh installs.** The header linked to the Freemius Affiliation page unconditionally, but Freemius only registers that page once the plugin leaves *activation mode*, i.e. after the user opts in or skips. On a fresh install (free especially) the link led to "Sorry, you are not allowed to access this page." The link now appears only when the page actually exists.
## [3.4.1]
> OAuth sign-in for MCP clients, plus atomic-element and compact-mode fixes.
### Added
- **OAuth sign-in for AI clients (free).** Claude and other MCP clients can connect through a standard OAuth 2.1 authorization flow — you approve access from your WordPress login instead of copying an Application Password. Self-contained authorization server (discovery metadata, Dynamic Client Registration, PKCE-S256, admin-only consent, 1h access + rotating 30-day refresh tokens); the token acts as the authorizing admin so all capability checks apply unchanged, and Application Passwords keep working alongside it. HTTPS-gated, enabled by default when available.
- **Connection tab: authentication-method chooser.** Choose **OAuth** ("Sign in through the browser, no password to copy") or **Application password**; the per-client setup adapts to your choice — a `mcp add … --transport http <url>` command, a custom-connector UI walkthrough (with a one-click deep link for Claude.ai/Cursor), or a config-file snippet (Cursor `mcp.json`, Codex `config.toml`, Antigravity `mcp_config.json`). Adds a **Connected apps** list with one-click Revoke, and a Claude.ai client.
- **OAuth sign-in for AI clients (free).** Claude and other MCP clients can connect through a standard OAuth 2.1 authorization flow, you approve access from your WordPress login instead of copying an Application Password. Self-contained authorization server (discovery metadata, Dynamic Client Registration, PKCE-S256, admin-only consent, 1h access + rotating 30-day refresh tokens); the token acts as the authorizing admin so all capability checks apply unchanged, and Application Passwords keep working alongside it. HTTPS-gated, enabled by default when available.
- **Connection tab: authentication-method chooser.** Choose **OAuth** ("Sign in through the browser, no password to copy") or **Application password**; the per-client setup adapts to your choice, a `mcp add … --transport http <url>` command, a custom-connector UI walkthrough (with a one-click deep link for Claude.ai/Cursor), or a config-file snippet (Cursor `mcp.json`, Codex `config.toml`, Antigravity `mcp_config.json`). Adds a **Connected apps** list with one-click Revoke, and a Claude.ai client.
### Fixed
- **OAuth discovery 404 (real-client blocker).** MCP clients request the discovery document at the resource-scoped path (`/.well-known/oauth-protected-resource/…`, RFC 9728 §3.1), not just the site root; that request 404'd, silently breaking OAuth with real clients. Both are now served.
- **`get-page-snapshot` ignored atomic (v4) widgets (#91).** The content outline and SEO-lite summary came back all-zeros on pages built with atomic elements. Now extracts `e-heading` / `e-paragraph` / `e-button` / `e-image` content.
- **Atomic `styles` writes silently ignored (#92).** Writing a local style class persisted the definition but never referenced it, so Elementor didn't apply it. Style writes now auto-wire the class into the element's `classes` prop.
- **Compact tool mode surfaced 6 tools instead of 3.** The three WordPress core context abilities leaked into the top-level list alongside the 3 meta-tools; they now fold into the dispatcher catalog (still reachable via `call-tool`).
- **Subdirectory WordPress installs were unreachable via the Node proxy (#93).** The stdio↔HTTP proxy ignored the path component of `WP_URL`, so any install under a subdirectory (e.g. `example.com/subdir`) had every request built against the domain root — `initialize` failed on every attempt. `detectPermalinks()` and `mcpPath()` now honor the site's base path (root installs unchanged); a non-JSON upstream body is turned into a JSON-RPC error instead of corrupting the stdio framing. Proxy bumped to 1.9.1. Thanks @carstenbaumhoegger.
- **Subdirectory WordPress installs were unreachable via the Node proxy (#93).** The stdio↔HTTP proxy ignored the path component of `WP_URL`, so any install under a subdirectory (e.g. `example.com/subdir`) had every request built against the domain root, `initialize` failed on every attempt. `detectPermalinks()` and `mcpPath()` now honor the site's base path (root installs unchanged); a non-JSON upstream body is turned into a JSON-RPC error instead of corrupting the stdio framing. Proxy bumped to 1.9.1. Thanks @carstenbaumhoegger.
### Changed
- **Codex renamed to "ChatGPT App", with a corrected OAuth flow.** The connection card is now labelled **ChatGPT App**, and its OAuth setup follows the app's in-app MCP flow (File → Settings → Plugins → MCPs → **Add server** → **Streamable HTTP** → **Save** → **Authenticate** → **Approve**) instead of a `config.toml` snippet, which has no OAuth path. The Application Password (`config.toml` + header) method is unchanged.
@@ -75,18 +75,18 @@ All notable changes to MCP Tools for Elementor are documented in this file.
> A new **Themes** domain: give an AI agent full context on the active theme, manage its settings, and create a child theme so it can safely edit theme files.
### Added
- **WP-CLI tools (4 tools, free, disabled-by-default).** Let a connected AI run `wp` commands: `run-wp-cli` (synchronous — returns stdout/stderr/exit code), plus `dispatch-wp-cli` / `get-wp-cli-job` / `list-wp-cli-jobs` for **background jobs** (long migrations / bulk tasks run as a detached process you poll for status + output). Two execution modes: **in-process** via `WP_CLI::runcommand()` when connected over the WP-CLI stdio transport (no shell), or a `proc_open` **shell path** (argv array — no shell interpolation) when an admin configures a `wp` base command (Connection tab, or the `EMCP_TOOLS_WPCLI_COMMAND` constant) for HTTP/proxy connections. **Guarded** by a command **blocklist** (refuses eval, eval-file, shell, server, raw `db query`/`cli`/`import`, `config set`, `package install`, and injection/retarget flags `--exec`/`--require`/`--path`/`--ssh`) with a quote-aware, Windows-path-safe tokenizer; all four require `manage_options`, ship **disabled-by-default** (command-execution surface), and runs are recorded to the change ledger. New classes `EMCP_Tools_WPCLI_Validator` / `_Runner` / `_Jobs` (`includes/wpcli/`) + `EMCP_Tools_WPCLI_Abilities`; a new **WP-CLI** category on the Tools tab.
- **WP-CLI tools (4 tools, free, disabled-by-default).** Let a connected AI run `wp` commands: `run-wp-cli` (synchronous, returns stdout/stderr/exit code), plus `dispatch-wp-cli` / `get-wp-cli-job` / `list-wp-cli-jobs` for **background jobs** (long migrations / bulk tasks run as a detached process you poll for status + output). Two execution modes: **in-process** via `WP_CLI::runcommand()` when connected over the WP-CLI stdio transport (no shell), or a `proc_open` **shell path** (argv array, no shell interpolation) when an admin configures a `wp` base command (Connection tab, or the `EMCP_TOOLS_WPCLI_COMMAND` constant) for HTTP/proxy connections. **Guarded** by a command **blocklist** (refuses eval, eval-file, shell, server, raw `db query`/`cli`/`import`, `config set`, `package install`, and injection/retarget flags `--exec`/`--require`/`--path`/`--ssh`) with a quote-aware, Windows-path-safe tokenizer; all four require `manage_options`, ship **disabled-by-default** (command-execution surface), and runs are recorded to the change ledger. New classes `EMCP_Tools_WPCLI_Validator` / `_Runner` / `_Jobs` (`includes/wpcli/`) + `EMCP_Tools_WPCLI_Abilities`; a new **WP-CLI** category on the Tools tab.
- **Themes tab: theme integrations (two dispatcher tools each).** A new **Themes** sub-tab on the Tools screen, following the ACF two-dispatcher pattern (one Read tool, one Write tool, each bundling internal operations). Two layers on a shared base so new frameworks are cheap to add:
- **Active Theme** (`theme-read` / `theme-write`, works for any active theme). `theme-read`: `get-theme-context` (active/parent, detected framework, `is_block_theme`, template dir, theme supports, registered menu locations, whether a child theme exists) and `get-mods` (theme_mod values). `theme-write`: `set-mods` (write theme_mod values, structural keys like menu locations refused) and **`create-child-theme`** (scaffold `style.css` + `functions.php` and activate a child of the active parent, so the agent can then edit theme files via the Filesystem tools; requires `confirm:true`, idempotent, refuses grandchildren).
- **Astra** (`astra-read` / `astra-write`, registers only when Astra is the active theme). Generic `get-settings` ({ `group`? colors/typography/layout/header-footer, `keys`? }) and `update-settings` ({ `values` }) over a curated `astra-settings` allowlist, mirroring the WordPress Settings domain (typed metadata, non-allowlisted keys reported in `skipped[]`).
- **Spectra Blocks** (`spectra-read` / `spectra-write`, registers only when the Spectra plugin (Ultimate Addons for Gutenberg) is active). The blocks/builder counterpart to the Astra theme pack, a Spectra-aware discover -> inspect -> act layer that mirrors the Elementor widget catalog. `spectra-read`: `list-blocks` (compact catalog of the site's Spectra blocks, with `category`/`search` filters, sourced live from Spectra's registry so nothing is guessed) and `get-block-schema` (a block's key attributes: **real attribute names + defaults read from Spectra's own `attributes.php`**, a highlighted subset by default or the first N capped, `full:true` for the whole set, plus a ready-to-use example and a doc link). `spectra-write`: `add-block` (insert a Spectra block into a post at a position, with a generated `block_id`; Spectra applies its own defaults, and container blocks like `buttons` get their required inner-block template). Building pages then reuses the existing Gutenberg block tools against the returned post.
- **Astra and Spectra are grouped as one "Astra + Spectra" section** on the Themes tab, since they are a theme + blocks combo. Each tool's toggle is enabled only when its component is installed and active (Astra tools require the Astra theme; Spectra tools require the Spectra plugin) and is greyed out + disabled otherwise, the same way Elementor tools are gated when Elementor is inactive, with a per-card hint on how to enable it.
Reads are enabled by default; the write dispatchers (`theme-write` / `astra-write` / `spectra-write`) ship disabled-by-default (opt in on the Tools tab). Building pages with a theme's blocks/patterns reuses the existing Gutenberg tools; this domain supplies the context. More framework packs (Kadence, GeneratePress) build on the same base.
- **SVG Uploads module (free, opt-in).** A self-contained, safe way to allow SVG uploads to the Media Library without a third-party plugin — aimed at sites **without** Elementor (Elementor already permits SVG for authorized users). When enabled on the Modules tab it (1) registers the `svg` mime for users who can `upload_files` (or admins-only, a per-module toggle), (2) fixes `wp_check_filetype_and_ext()` so the real-content MIME check doesn't reject SVGs on the **REST/sideload** path (the piece most SVG plugins miss — so `sideload-image` and the MCP media tools can import SVGs), and (3) **sanitizes every uploaded SVG** with the bundled `enshrined/svg-sanitize` library (scripts, event handlers, `javascript:` URIs and external references stripped) — **fail-closed**: an SVG that can't be cleaned is rejected. Off by default given the security surface. New classes `EMCP_Tools_SVG_Support_Module` + `EMCP_Tools_SVG_Sanitizer` (`includes/modules/svg-support/`); `enshrined/svg-sanitize` is bundled in `vendor/`.
- **`get-block-schema` now surfaces Spectra's shared-helper attributes.** A block's `attributes.php` (what `get-block-schema` reads) omits attributes registered via a shared helper — the container **background image/video**, **border radius**, and **box shadow**. The container schema now includes a `shared_attributes` map (with the real attribute names, e.g. `backgroundType:'image'` + `backgroundImage:{id,url}` + overlay, `overlayType:'gradient'` + `gradientValue`, `containerBorder*Radius`, `boxShadow*`) plus a `schema_note` steering agents to these native attributes instead of a `core/html` workaround.
- **SVG Uploads module (free, opt-in).** A self-contained, safe way to allow SVG uploads to the Media Library without a third-party plugin, aimed at sites **without** Elementor (Elementor already permits SVG for authorized users). When enabled on the Modules tab it (1) registers the `svg` mime for users who can `upload_files` (or admins-only, a per-module toggle), (2) fixes `wp_check_filetype_and_ext()` so the real-content MIME check doesn't reject SVGs on the **REST/sideload** path (the piece most SVG plugins miss, so `sideload-image` and the MCP media tools can import SVGs), and (3) **sanitizes every uploaded SVG** with the bundled `enshrined/svg-sanitize` library (scripts, event handlers, `javascript:` URIs and external references stripped), **fail-closed**: an SVG that can't be cleaned is rejected. Off by default given the security surface. New classes `EMCP_Tools_SVG_Support_Module` + `EMCP_Tools_SVG_Sanitizer` (`includes/modules/svg-support/`); `enshrined/svg-sanitize` is bundled in `vendor/`.
- **`get-block-schema` now surfaces Spectra's shared-helper attributes.** A block's `attributes.php` (what `get-block-schema` reads) omits attributes registered via a shared helper, the container **background image/video**, **border radius**, and **box shadow**. The container schema now includes a `shared_attributes` map (with the real attribute names, e.g. `backgroundType:'image'` + `backgroundImage:{id,url}` + overlay, `overlayType:'gradient'` + `gradientValue`, `containerBorder*Radius`, `boxShadow*`) plus a `schema_note` steering agents to these native attributes instead of a `core/html` workaround.
### Changed
- **`search-images` and `sideload-image` are now core WordPress tools** — they register on any site regardless of Elementor (they only need a stock-provider API key + the Media Library), matching the plugin's beyond-Elementor philosophy. Previously both were gated behind Elementor being active. `add-stock-image` (which adds an image widget to the page) remains Elementor-gated.
- **`search-images` and `sideload-image` are now core WordPress tools**: they register on any site regardless of Elementor (they only need a stock-provider API key + the Media Library), matching the plugin's beyond-Elementor philosophy. Previously both were gated behind Elementor being active. `add-stock-image` (which adds an image widget to the page) remains Elementor-gated.
## [3.3.0]
+105 -105
View File
@@ -29,54 +29,54 @@ A WordPress plugin that extends the [WordPress MCP Adapter](https://github.com/W
## Features
- **New in v3.3.0** — five "intelligence, not just transport" features that let an AI agent *understand*, *undo*, and *reuse* your site, plus a big community contribution:
- **Page Snapshot** (`get-page-snapshot`) — one normalized digest of a page (structure, tokens-in-use, responsive overrides, content outline, SEO-lite, + opt-in performance/a11y/SEO audits) so the AI reasons about a page in a single call
- **Change History & Rollback** (`list-changes` / `get-change` / `rollback-change`) — every AI-made Elementor/filesystem/database change is recorded to one ledger and can be undone, from MCP **and** a new **History** admin tab
- **Content Search** (`search-content` / `reindex-search`) — find the site's own pages, templates, widgets, and global styles by natural-language query so the AI reuses existing work instead of rebuilding it
- **Content Mirror** (`export-content` / `restore-content` / `list-content-exports`) — export page/template content to git-trackable JSON files for external version control
- **Navigation Menus** (`menu-read` / `menu-write` + `[emcp_menu]` shortcode) — full WordPress nav-menu management over MCP (community-contributed)
- **Multi-site proxy** (`@msrbuilds/emcp-proxy` 1.9.0) — drive many WordPress installs from one connection with a site registry and `emcp_use_site`
- **A focused MCP toolset** covering the full Elementor page-building workflow — and, since v3.0.0, growing beyond Elementor into general WordPress management across many domains: content, nav menus, settings, plugins & themes, media library, users, performance, filesystem, and database. The 62 per-widget tools are folded into a catalog-backed model, so the active surface stays small — every widget is still reachable via discover → inspect → act. Counts scale with your environment (registered counts; v3.3.0 adds 11 always-on tools over the live-verified 4.1.4 baseline):
- **128 tools** — free Elementor only (104 active by default)
- **142 tools** — free Elementor + Elementor 4.0 atomic elements (118 active)
- **138 tools** — with Elementor Pro (99 active)
- **152 tools** — with Elementor Pro + Elementor 4.0 (113 active; + WooCommerce adds no new tools)
- **+2 ACF tools** when Advanced Custom Fields (free or PRO) is active — `acf-read` and `acf-write`, two dispatchers exposing **15 ACF operations** (8 read, 7 write; the Custom-Post-Type/taxonomy operations need ACF 6.1+). Each dispatcher is a single toggle under Tools → Plugins → ACF (listing the operations it covers); `acf-read` is on by default, `acf-write` ships off (v3.2.1)
- **+2 Meta Box tools** when Meta Box (free core) is active — `metabox-read` and `metabox-write`, two dispatchers exposing **4 Meta Box operations** (3 read, 1 write). Each dispatcher is a single toggle under Tools → Plugins → Meta Box (listing the operations it covers); `metabox-read` is on by default, `metabox-write` ships off (v3.4.2)
- **+2 WooCommerce tools (Pro)** when WooCommerce is active — `woo-read` and `woo-write`, two dispatchers over the full `wc/v3` surface (~120 operations). Single toggle each under Tools → Plugins → WooCommerce; `woo-read` on by default, `woo-write` ships off; refunds/deletes/batch require `confirm:true` (v3.4.2)
- **39 of these ship disabled-by-default** (SEO & Accessibility, Widget Builder, PHP Snippets, the 9 Plugins & Themes write tools, `delete-media`, the 2 Users write tools, the 3 Filesystem write tools, and the 3 Database write tools) — plus the 7 ACF write operations are off by default within `acf-write` — so the typical active surface is smaller until you opt in on the Tools tab
- **WordPress Content (beyond Elementor)** — Create and manage posts, pages, and any custom post type — content, status, taxonomy terms, custom fields, and featured images — via MCP, without touching Elementor data. Built on WP core; every post carries an `is_elementor` flag that steers agents to the Elementor tools for builder pages
- **WordPress Settings (beyond Elementor, domain 2)** — Read and batch-update core WordPress settings (general/reading/writing/discussion/media/permalinks) over MCP. Curated allowlist only — no arbitrary option access; `admin_email` is read-only; permalink changes auto-flush rewrite rules. `manage_options`. (v3.0.0)
- **Plugins & Themes (beyond Elementor, domain 3)** — Discover, install (wordpress.org only), update, activate/deactivate, and delete plugins and themes over MCP. Strong guardrails (EMCP Tools + Elementor protected; per-op capability gating; direct-filesystem-only); the 9 mutation tools ship disabled-by-default. `manage_options`-class capabilities. (v3.0.0)
- **Media Library (beyond Elementor, domain 4)** — Fetch full attachment detail (`get-media`: every registered size, dimensions, metadata, alt/caption/description), edit metadata (`update-media`: alt text, title, caption, description — a one-call accessibility/SEO fix), and delete attachments (`delete-media`: destructive and effectively permanent; disabled-by-default and requires `confirm:true`). URL uploads continue via `sideload-image`. (v3.0.0)
- **Users (beyond Elementor, domain 5)** — List and read WordPress users, and safely create/edit non-admin profiles over MCP. Hard guardrails: no delete-user and no role-change tool; `create-user` assigns only non-admin roles and auto-generates a strong password (emailed to the new user — never returned); `update-user` edits profile fields only and refuses any user with admin-level capabilities (administrators are off-limits). `list-users`/`get-user` are enabled by default; `create-user`/`update-user` are disabled-by-default. (v3.0.0)
- **ACF / ACF PRO (beyond Elementor)** — Read and write Advanced Custom Fields values on posts and options pages, discover and author field groups, and register ACF-managed Custom Post Types and taxonomies (ACF 6.1+) — enough to stand up a full content structure end-to-end (CPT → taxonomy → field group → posts with values). Full Pro field support: repeaters, flexible content (layout-validated), galleries, groups, and clones round-trip as nested JSON. Only registers when ACF is active; writes resolve field names to keys, ship disabled-by-default, and there is deliberately no delete and no slug/field rename (renames orphan stored content). (v3.2.1)
- **Meta Box** — Read/write Meta Box custom-field values and discover field groups. Only registers when Meta Box is active; `metabox-read` is on by default, `metabox-write` ships disabled-by-default (values only — no delete, and unknown fields are skipped rather than created). (v3.4.2)
- **WooCommerce (Pro)** — Two dispatcher tools (`woo-read` / `woo-write`) over the full WooCommerce `wc/v3` surface (~120 operations): products (+ variations, categories, tags, attributes, reviews), orders (+ notes, refunds), customers, coupons, reports, settings, payment gateways, shipping, taxes, webhooks, and system status. Runs through WooCommerce's own REST controllers (HPOS-safe, no privilege escalation). Only registers when WooCommerce is active; `woo-read` is on by default, `woo-write` ships disabled-by-default, and money/irreversible operations (refunds, deletes, batch) additionally require `confirm:true`. (v3.4.2)
- **Forms — 8 form builders** — Two dispatcher tools per plugin (read/write) for **Contact Form 7 (free)** plus **WPForms, Gravity Forms, Fluent Forms, Ninja Forms, Formidable, MetForm, and SureForms (Pro)**. Read forms, fields, notifications and entries (submissions); manage entries. Each pair registers only when its plugin is active; reads on by default, writes disabled-by-default, and `delete-entry` requires `confirm:true`. CF7 has no entries (it stores none); WPForms entry operations require WPForms Pro. Grouped under Tools → Plugins → Forms. (v3.5.0)
- **SEO — 7 SEO plugins** — Two dispatcher tools per plugin (read/write) for **Slim SEO (free)** plus **Yoast, Rank Math, All in One SEO, SEOPress, The SEO Framework, and SureRank (Pro)**. Read and write the SEO metadata each plugin stores — title, description, canonical, robots (noindex/nofollow), focus keyword, Open Graph / Twitter — for posts and terms over **one unified field vocabulary** (the adapter translates to each plugin's own encoding), plus schema-read where supported. Distinct from the Pro SEO & Accessibility toolkit (which audits/generates). Grouped under Tools → Plugins → SEO. (v3.5.0)
- **Performance & Security (beyond Elementor, domain 6)** — Two read-only diagnostics. **`analyze-performance`** audits server config, WordPress internals (database size, autoloaded options, post revisions, cron backlog, persistent object cache, OPcache, plugin count), and a target page (defaults to the frontpage; optional `url`/`post_id`) for performance bottlenecks. **`scan-security`** — the security counterpart — scans across malware heuristics (eval/base64 obfuscation, request-driven execution, command execution, webshells, encoded blobs, executable PHP under uploads), WordPress core-file integrity (against the official wordpress.org checksums), hardening (file editing, debug output, `admin` username, XML-RPC, version disclosure, HTTPS, security headers), and outdated/abandoned software. Both return a scored report (0-100 + A–F grade) with severity-tagged findings and ranked recommendations; the malware walk is bounded and never returns full file contents (path:line + snippet only). Read-only, self-contained (no external API beyond optional, graceful wordpress.org calls), enabled by default. (v3.0.0)
- **Filesystem (beyond Elementor)** — Read and scan any file inside the WordPress install (`read-file`, `list-directory`, `search-files` — enabled by default). Modify and delete files (`write-file`, `edit-file`, `delete-file`) are disabled-by-default: every path is confined to ABSPATH (no traversal/symlink escape), writes auto-back up the original, `wp-config.php`/`.htaccess` are refused, the `edit_files` capability is enforced (honoring `DISALLOW_FILE_EDIT`), and all writes are audit-logged. `delete-file` requires `confirm:true`. `manage_options`. (v3.0.0)
- **Database (beyond Elementor)** — Inspect the database with flexible read-only SQL (`list-tables`, `describe-table`, `query` — SELECT/SHOW/DESCRIBE/EXPLAIN only; writes, DDL, stacked statements, MySQL `/*!` executable comments, and file-access SQL are rejected; results capped; enabled by default). Structured parameterized writes (`insert-row`, `update-rows`, `delete-rows`) are disabled-by-default: uses `$wpdb` throughout (no raw write-SQL/DDL), forces a non-empty WHERE on update/delete, refuses `wp_users`/`wp_usermeta`, captures a before-image snapshot to an audit log, and `delete-rows` requires `confirm:true`. `manage_options`. (v3.0.0)
- **Query & Discovery** — List widgets, inspect page structures, read element settings, browse templates, view global design tokens
- **Page Management** — Create pages, update settings, clear content, import/export templates
- **Layout Tools** — Add flexbox containers, move/remove/duplicate elements, update containers, find elements, batch update, reorder children, get container schema
- **Widget Tools** — A catalog-backed model: `list-widgets` (filter by tier/category/search) → `get-widget-schema` (curated params, batch, or full raw schema) → `add-free-widget` / `add-pro-widget` (with Pro) → `update-widget`. The 62 widgets' curated params live in a built-in catalog (27 free / 30 Pro / 5 WooCommerce), so every widget and parameter stays reachable while the per-turn tool-list cost drops ~10×
- **Pro Widget Support** — Conditional tools for nav menu, loop grid, loop carousel, media carousel, nested tabs, nested accordion, portfolio, author box, login, code highlight, reviews, off-canvas, progress tracker, search, and more (only register when Elementor Pro is active)
- **Atomic Elements (Elementor 4.0+)** — 13 dedicated tools for Elementor's atomic system: flexbox, div-block, heading, paragraph, button, image, svg, youtube, video, divider, plus universal `add-atomic-widget` / `update-atomic-widget` and `detect-elementor-version`
- **Theme Builder** — Create theme templates (header/footer/single/archive), set display conditions (Pro)
- **Dynamic Tags** — List all available dynamic tags, bind dynamic data to element settings (Pro)
- **Popup Builder** — Create popup templates, configure triggers/conditions/timing (Pro)
- **Template Tools** — Save pages or elements as reusable templates, apply templates to pages
- **Global Settings** — Update site-wide color palettes and typography presets
- **Brand Kits** — One-click coordinated color + typography kits. Apply a curated brand kit and the whole site re-skins (system tokens + Theme Style defaults); back up and restore any time. **10 kits are free to apply**; the full 50-kit library and the `apply-brand-kit` MCP tool are Pro
- **AI Widget Builder (Pro)** — Let an AI agent design custom Elementor widgets from a structured spec — no hand-written PHP. The plugin compiles the spec + an HTML template into a sandboxed `Widget_Base` class (35 control types, optional per-widget CSS/JS) that appears under a "Custom (EMCP)" category in the editor; a runtime safety net keeps a bad widget from breaking the editor
- **Composite Tools** — Build a complete page from a declarative JSON structure in a single call
- **Stock Images** — Search Unsplash, Pexels & Pixabay for stock photos, sideload into Media Library, add to pages
- **SVG Icons** — Upload SVG icons from URL or raw markup for use with Elementor icon widgets
- **Custom Code** — Add custom CSS (element/page level), inject JavaScript, create site-wide code snippets for head/body injection
- **Low-tools Mode** — One-click toggle that filters the active tool list to a curated essentials set, for MCP clients with strict tool caps (Antigravity, Gemini API, etc.). After the v3.0.0 widget consolidation the active count already fits most caps, so this is rarely needed now
- **Admin Dashboard** — Dedicated top-level **EMCP Tools** menu with Dashboard / Modules / Tools / Connection / Prompts / Templates / Brand Kits / Skills / Sandbox / **History** / Changelog tabs. Toggle individual tools on/off, view connection configs for all supported MCP clients, review and roll back AI-made changes on the History tab, and reach the **Get Support** portal from any tab
- **New in v3.3.0**: five "intelligence, not just transport" features that let an AI agent *understand*, *undo*, and *reuse* your site, plus a big community contribution:
- **Page Snapshot** (`get-page-snapshot`), one normalized digest of a page (structure, tokens-in-use, responsive overrides, content outline, SEO-lite, + opt-in performance/a11y/SEO audits) so the AI reasons about a page in a single call
- **Change History & Rollback** (`list-changes` / `get-change` / `rollback-change`), every AI-made Elementor/filesystem/database change is recorded to one ledger and can be undone, from MCP **and** a new **History** admin tab
- **Content Search** (`search-content` / `reindex-search`), find the site's own pages, templates, widgets, and global styles by natural-language query so the AI reuses existing work instead of rebuilding it
- **Content Mirror** (`export-content` / `restore-content` / `list-content-exports`), export page/template content to git-trackable JSON files for external version control
- **Navigation Menus** (`menu-read` / `menu-write` + `[emcp_menu]` shortcode), full WordPress nav-menu management over MCP (community-contributed)
- **Multi-site proxy** (`@msrbuilds/emcp-proxy` 1.9.0), drive many WordPress installs from one connection with a site registry and `emcp_use_site`
- **A focused MCP toolset** covering the full Elementor page-building workflow, and, since v3.0.0, growing beyond Elementor into general WordPress management across many domains: content, nav menus, settings, plugins & themes, media library, users, performance, filesystem, and database. The 62 per-widget tools are folded into a catalog-backed model, so the active surface stays small, every widget is still reachable via discover → inspect → act. Counts scale with your environment (registered counts; v3.3.0 adds 11 always-on tools over the live-verified 4.1.4 baseline):
- **128 tools**: free Elementor only (104 active by default)
- **142 tools**: free Elementor + Elementor 4.0 atomic elements (118 active)
- **138 tools**: with Elementor Pro (99 active)
- **152 tools**: with Elementor Pro + Elementor 4.0 (113 active; + WooCommerce adds no new tools)
- **+2 ACF tools** when Advanced Custom Fields (free or PRO) is active, `acf-read` and `acf-write`, two dispatchers exposing **15 ACF operations** (8 read, 7 write; the Custom-Post-Type/taxonomy operations need ACF 6.1+). Each dispatcher is a single toggle under Tools → Plugins → ACF (listing the operations it covers); `acf-read` is on by default, `acf-write` ships off (v3.2.1)
- **+2 Meta Box tools** when Meta Box (free core) is active, `metabox-read` and `metabox-write`, two dispatchers exposing **4 Meta Box operations** (3 read, 1 write). Each dispatcher is a single toggle under Tools → Plugins → Meta Box (listing the operations it covers); `metabox-read` is on by default, `metabox-write` ships off (v3.4.2)
- **+2 WooCommerce tools (Pro)** when WooCommerce is active, `woo-read` and `woo-write`, two dispatchers over the full `wc/v3` surface (~120 operations). Single toggle each under Tools → Plugins → WooCommerce; `woo-read` on by default, `woo-write` ships off; refunds/deletes/batch require `confirm:true` (v3.4.2)
- **39 of these ship disabled-by-default** (SEO & Accessibility, Widget Builder, PHP Snippets, the 9 Plugins & Themes write tools, `delete-media`, the 2 Users write tools, the 3 Filesystem write tools, and the 3 Database write tools), plus the 7 ACF write operations are off by default within `acf-write`, so the typical active surface is smaller until you opt in on the Tools tab
- **WordPress Content (beyond Elementor)**: Create and manage posts, pages, and any custom post type, content, status, taxonomy terms, custom fields, and featured images, via MCP, without touching Elementor data. Built on WP core; every post carries an `is_elementor` flag that steers agents to the Elementor tools for builder pages
- **WordPress Settings (beyond Elementor, domain 2)**: Read and batch-update core WordPress settings (general/reading/writing/discussion/media/permalinks) over MCP. Curated allowlist only, no arbitrary option access; `admin_email` is read-only; permalink changes auto-flush rewrite rules. `manage_options`. (v3.0.0)
- **Plugins & Themes (beyond Elementor, domain 3)**: Discover, install (wordpress.org only), update, activate/deactivate, and delete plugins and themes over MCP. Strong guardrails (EMCP Tools + Elementor protected; per-op capability gating; direct-filesystem-only); the 9 mutation tools ship disabled-by-default. `manage_options`-class capabilities. (v3.0.0)
- **Media Library (beyond Elementor, domain 4)**: Fetch full attachment detail (`get-media`: every registered size, dimensions, metadata, alt/caption/description), edit metadata (`update-media`: alt text, title, caption, description, a one-call accessibility/SEO fix), and delete attachments (`delete-media`: destructive and effectively permanent; disabled-by-default and requires `confirm:true`). URL uploads continue via `sideload-image`. (v3.0.0)
- **Users (beyond Elementor, domain 5)**: List and read WordPress users, and safely create/edit non-admin profiles over MCP. Hard guardrails: no delete-user and no role-change tool; `create-user` assigns only non-admin roles and auto-generates a strong password (emailed to the new user, never returned); `update-user` edits profile fields only and refuses any user with admin-level capabilities (administrators are off-limits). `list-users`/`get-user` are enabled by default; `create-user`/`update-user` are disabled-by-default. (v3.0.0)
- **ACF / ACF PRO (beyond Elementor)**: Read and write Advanced Custom Fields values on posts and options pages, discover and author field groups, and register ACF-managed Custom Post Types and taxonomies (ACF 6.1+), enough to stand up a full content structure end-to-end (CPT → taxonomy → field group → posts with values). Full Pro field support: repeaters, flexible content (layout-validated), galleries, groups, and clones round-trip as nested JSON. Only registers when ACF is active; writes resolve field names to keys, ship disabled-by-default, and there is deliberately no delete and no slug/field rename (renames orphan stored content). (v3.2.1)
- **Meta Box**: Read/write Meta Box custom-field values and discover field groups. Only registers when Meta Box is active; `metabox-read` is on by default, `metabox-write` ships disabled-by-default (values only, no delete, and unknown fields are skipped rather than created). (v3.4.2)
- **WooCommerce (Pro)**: Two dispatcher tools (`woo-read` / `woo-write`) over the full WooCommerce `wc/v3` surface (~120 operations): products (+ variations, categories, tags, attributes, reviews), orders (+ notes, refunds), customers, coupons, reports, settings, payment gateways, shipping, taxes, webhooks, and system status. Runs through WooCommerce's own REST controllers (HPOS-safe, no privilege escalation). Only registers when WooCommerce is active; `woo-read` is on by default, `woo-write` ships disabled-by-default, and money/irreversible operations (refunds, deletes, batch) additionally require `confirm:true`. (v3.4.2)
- **Forms, 8 form builders**: Two dispatcher tools per plugin (read/write) for **Contact Form 7 (free)** plus **WPForms, Gravity Forms, Fluent Forms, Ninja Forms, Formidable, MetForm, and SureForms (Pro)**. Read forms, fields, notifications and entries (submissions); manage entries. Each pair registers only when its plugin is active; reads on by default, writes disabled-by-default, and `delete-entry` requires `confirm:true`. CF7 has no entries (it stores none); WPForms entry operations require WPForms Pro. Grouped under Tools → Plugins → Forms. (v3.5.0)
- **SEO, 7 SEO plugins**: Two dispatcher tools per plugin (read/write) for **Slim SEO (free)** plus **Yoast, Rank Math, All in One SEO, SEOPress, The SEO Framework, and SureRank (Pro)**. Read and write the SEO metadata each plugin stores, title, description, canonical, robots (noindex/nofollow), focus keyword, Open Graph / Twitter, for posts and terms over **one unified field vocabulary** (the adapter translates to each plugin's own encoding), plus schema-read where supported. Distinct from the Pro SEO & Accessibility toolkit (which audits/generates). Grouped under Tools → Plugins → SEO. (v3.5.0)
- **Performance & Security (beyond Elementor, domain 6)**: Two read-only diagnostics. **`analyze-performance`** audits server config, WordPress internals (database size, autoloaded options, post revisions, cron backlog, persistent object cache, OPcache, plugin count), and a target page (defaults to the frontpage; optional `url`/`post_id`) for performance bottlenecks. **`scan-security`**: the security counterpart, scans across malware heuristics (eval/base64 obfuscation, request-driven execution, command execution, webshells, encoded blobs, executable PHP under uploads), WordPress core-file integrity (against the official wordpress.org checksums), hardening (file editing, debug output, `admin` username, XML-RPC, version disclosure, HTTPS, security headers), and outdated/abandoned software. Both return a scored report (0-100 + A–F grade) with severity-tagged findings and ranked recommendations; the malware walk is bounded and never returns full file contents (path:line + snippet only). Read-only, self-contained (no external API beyond optional, graceful wordpress.org calls), enabled by default. (v3.0.0)
- **Filesystem (beyond Elementor)**: Read and scan any file inside the WordPress install (`read-file`, `list-directory`, `search-files`, enabled by default). Modify and delete files (`write-file`, `edit-file`, `delete-file`) are disabled-by-default: every path is confined to ABSPATH (no traversal/symlink escape), writes auto-back up the original, `wp-config.php`/`.htaccess` are refused, the `edit_files` capability is enforced (honoring `DISALLOW_FILE_EDIT`), and all writes are audit-logged. `delete-file` requires `confirm:true`. `manage_options`. (v3.0.0)
- **Database (beyond Elementor)**: Inspect the database with flexible read-only SQL (`list-tables`, `describe-table`, `query`, SELECT/SHOW/DESCRIBE/EXPLAIN only; writes, DDL, stacked statements, MySQL `/*!` executable comments, and file-access SQL are rejected; results capped; enabled by default). Structured parameterized writes (`insert-row`, `update-rows`, `delete-rows`) are disabled-by-default: uses `$wpdb` throughout (no raw write-SQL/DDL), forces a non-empty WHERE on update/delete, refuses `wp_users`/`wp_usermeta`, captures a before-image snapshot to an audit log, and `delete-rows` requires `confirm:true`. `manage_options`. (v3.0.0)
- **Query & Discovery**: List widgets, inspect page structures, read element settings, browse templates, view global design tokens
- **Page Management**: Create pages, update settings, clear content, import/export templates
- **Layout Tools**: Add flexbox containers, move/remove/duplicate elements, update containers, find elements, batch update, reorder children, get container schema
- **Widget Tools**: A catalog-backed model: `list-widgets` (filter by tier/category/search) → `get-widget-schema` (curated params, batch, or full raw schema) → `add-free-widget` / `add-pro-widget` (with Pro) → `update-widget`. The 62 widgets' curated params live in a built-in catalog (27 free / 30 Pro / 5 WooCommerce), so every widget and parameter stays reachable while the per-turn tool-list cost drops ~10×
- **Pro Widget Support**: Conditional tools for nav menu, loop grid, loop carousel, media carousel, nested tabs, nested accordion, portfolio, author box, login, code highlight, reviews, off-canvas, progress tracker, search, and more (only register when Elementor Pro is active)
- **Atomic Elements (Elementor 4.0+)**: 13 dedicated tools for Elementor's atomic system: flexbox, div-block, heading, paragraph, button, image, svg, youtube, video, divider, plus universal `add-atomic-widget` / `update-atomic-widget` and `detect-elementor-version`
- **Theme Builder**: Create theme templates (header/footer/single/archive), set display conditions (Pro)
- **Dynamic Tags**: List all available dynamic tags, bind dynamic data to element settings (Pro)
- **Popup Builder**: Create popup templates, configure triggers/conditions/timing (Pro)
- **Template Tools**: Save pages or elements as reusable templates, apply templates to pages
- **Global Settings**: Update site-wide color palettes and typography presets
- **Brand Kits**: One-click coordinated color + typography kits. Apply a curated brand kit and the whole site re-skins (system tokens + Theme Style defaults); back up and restore any time. **10 kits are free to apply**; the full 50-kit library and the `apply-brand-kit` MCP tool are Pro
- **AI Widget Builder (Pro)**: Let an AI agent design custom Elementor widgets from a structured spec, no hand-written PHP. The plugin compiles the spec + an HTML template into a sandboxed `Widget_Base` class (35 control types, optional per-widget CSS/JS) that appears under a "Custom (EMCP)" category in the editor; a runtime safety net keeps a bad widget from breaking the editor
- **Composite Tools**: Build a complete page from a declarative JSON structure in a single call
- **Stock Images**: Search Unsplash, Pexels & Pixabay for stock photos, sideload into Media Library, add to pages
- **SVG Icons**: Upload SVG icons from URL or raw markup for use with Elementor icon widgets
- **Custom Code**: Add custom CSS (element/page level), inject JavaScript, create site-wide code snippets for head/body injection
- **Low-tools Mode**: One-click toggle that filters the active tool list to a curated essentials set, for MCP clients with strict tool caps (Antigravity, Gemini API, etc.). After the v3.0.0 widget consolidation the active count already fits most caps, so this is rarely needed now
- **Admin Dashboard**: Dedicated top-level **EMCP Tools** menu with Dashboard / Modules / Tools / Connection / Prompts / Templates / Brand Kits / Skills / Sandbox / **History** / Changelog tabs. Toggle individual tools on/off, view connection configs for all supported MCP clients, review and roll back AI-made changes on the History tab, and reach the **Get Support** portal from any tab
## Requirements
@@ -93,7 +93,7 @@ A WordPress plugin that extends the [WordPress MCP Adapter](https://github.com/W
1. Install and activate [Elementor](https://wordpress.org/plugins/elementor/) (version 3.20+).
2. Download the latest release zip from the [Releases page](https://github.com/msrbuilds/elementor-mcp/releases/).
3. In WordPress, go to **Plugins > Add New > Upload Plugin** and upload the downloaded zip file.
4. Activate the plugin through the **Plugins** menu in WordPress. The WordPress MCP Adapter is **bundled** — no separate install needed (WordPress 6.9+/7.0 already includes the Abilities API).
4. Activate the plugin through the **Plugins** menu in WordPress. The WordPress MCP Adapter is **bundled**: no separate install needed (WordPress 6.9+/7.0 already includes the Abilities API).
5. Open the new **EMCP Tools** top-level menu in the WordPress admin sidebar to configure tools and view connection info.
## Connecting to the MCP Server
@@ -102,11 +102,11 @@ The MCP server lives at `https://your-site.com/wp-json/mcp/emcp-tools-server` (W
### Recommended: the in-admin Connection manager
The fastest path is the built-in **EMCP Tools → Connection** screen. It walks you through it: toggle the server on, generate an Application Password in one click, then **pick your AI client** — and only the options that fit that client appear, tailored and ready to copy:
The fastest path is the built-in **EMCP Tools → Connection** screen. It walks you through it: toggle the server on, generate an Application Password in one click, then **pick your AI client**: and only the options that fit that client appear, tailored and ready to copy:
- **Claude Desktop** — a one-click **`.mcpb` bundle**. Download it, double-click to install, done — no config files to edit. The bundle is fully self-contained: it ships a single `server/index.js` (the proxy compiled to CommonJS with your site URL + a fresh Application Password embedded) that runs on Claude Desktop's **built-in Node.js** — no `npx`, no separate Node install, no PATH setup. (The `.mcpb` contains a live credential — delete it after importing.)
- **Claude Code / Cursor / VS Code / Antigravity** — ready-to-paste JSON (HTTP) or a terminal command, generated with your endpoint and credentials filled in.
- **Any client** — a copy-paste **AI setup prompt** that tells the agent how to wire itself up, or the raw manual JSON.
- **Claude Desktop**: a one-click **`.mcpb` bundle**. Download it, double-click to install, done, no config files to edit. The bundle is fully self-contained: it ships a single `server/index.js` (the proxy compiled to CommonJS with your site URL + a fresh Application Password embedded) that runs on Claude Desktop's **built-in Node.js**: no `npx`, no separate Node install, no PATH setup. (The `.mcpb` contains a live credential, delete it after importing.)
- **Claude Code / Cursor / VS Code / Antigravity**: ready-to-paste JSON (HTTP) or a terminal command, generated with your endpoint and credentials filled in.
- **Any client**: a copy-paste **AI setup prompt** that tells the agent how to wire itself up, or the raw manual JSON.
The same screen includes a one-click **authentication test** (confirms a client can actually connect and flags a host that strips the `Authorization` header) and a **Context** page where you write site-wide guidance delivered to every connecting agent as the server's MCP `instructions`.
@@ -224,9 +224,9 @@ For local development with WP-CLI available, you can use the stdio transport (no
### Node.js proxy (remote sites or protocol compatibility)
For remote WordPress sites, environments without WP-CLI, or when your AI client needs a different MCP protocol version, use the Node.js proxy. Your AI client launches it as a **local subprocess**, so it must run on the machine with the client — **not** on the WordPress server. On shared hosting you have no local access to the plugin directory, so use one of the two methods below.
For remote WordPress sites, environments without WP-CLI, or when your AI client needs a different MCP protocol version, use the Node.js proxy. Your AI client launches it as a **local subprocess**, so it must run on the machine with the client, **not** on the WordPress server. On shared hosting you have no local access to the plugin directory, so use one of the two methods below.
**Recommended — `npx` runner (nothing to install or keep in sync):**
**Recommended, `npx` runner (nothing to install or keep in sync):**
```json
{
@@ -248,9 +248,9 @@ For remote WordPress sites, environments without WP-CLI, or when your AI client
`npx` fetches the latest proxy on each launch, so there is no local copy to drift from the plugin version. Requires Node.js 18+ on the client machine.
**Alternative — local copy of the proxy file:**
**Alternative, local copy of the proxy file:**
Extract `bin/mcp-proxy.mjs` from the plugin ZIP, save it anywhere on the machine running your AI client, and point `args` at that **local** path (e.g. `["C:\\local\\path\\to\\mcp-proxy.mjs"]`) — not at the copy inside `wp-content/plugins/...` on the server. Re-extract it after plugin updates to pick up proxy fixes.
Extract `bin/mcp-proxy.mjs` from the plugin ZIP, save it anywhere on the machine running your AI client, and point `args` at that **local** path (e.g. `["C:\\local\\path\\to\\mcp-proxy.mjs"]`), not at the copy inside `wp-content/plugins/...` on the server. Re-extract it after plugin updates to pick up proxy fixes.
**Optional environment variables:**
@@ -280,40 +280,40 @@ npx @modelcontextprotocol/inspector wp mcp-adapter serve \
| `list-templates` | Saved Elementor templates from the template library |
| `get-global-settings` | Active kit/global settings (colors, typography, spacing) |
### WordPress Content — beyond Elementor (8 tools, v3.0.0)
### WordPress Content, beyond Elementor (8 tools, v3.0.0)
General WordPress content management over MCP — built on WordPress core, these tools **never touch Elementor data**. Every returned post carries an `is_elementor` flag so an agent knows to switch to the Elementor tools for builder pages. Enabled by default; capability-gated. Featured image and custom-field meta are parameters of create/update.
General WordPress content management over MCP, built on WordPress core, these tools **never touch Elementor data**. Every returned post carries an `is_elementor` flag so an agent knows to switch to the Elementor tools for builder pages. Enabled by default; capability-gated. Featured image and custom-field meta are parameters of create/update.
| Tool | Description |
|---|---|
| `list-post-types` | List registered public post types (read-only) |
| `list-taxonomies` | List registered taxonomies and their object types (read-only) |
| `create-post` | Create a post/page/CPT — title, content (HTML or block markup), status, slug, author, terms, custom fields, featured image |
| `create-post` | Create a post/page/CPT, title, content (HTML or block markup), status, slug, author, terms, custom fields, featured image |
| `get-post` | Read a single post by ID (read-only; includes `is_elementor` flag) |
| `update-post` | Update an existing post's fields, terms, custom fields, featured image |
| `list-posts` | List/query posts of any type with filters (read-only) |
| `delete-post` | Delete a post — trashes by default; `force:true` permanently deletes |
| `delete-post` | Delete a post, trashes by default; `force:true` permanently deletes |
| `set-post-terms` | Assign taxonomy terms to a post |
> WordPress core's read-only context abilities (`core/get-site-info`, `core/get-user-info`, `core/get-environment-info`) are also surfaced on the EMCP server.
### WordPress Settings — beyond Elementor, domain 2 (2 tools, v3.0.0)
### WordPress Settings, beyond Elementor, domain 2 (2 tools, v3.0.0)
Core WordPress settings management over MCP — curated allowlist of general/reading/writing/discussion/media/permalink settings. `get-settings` doubles as discovery (returns each setting's type, label, enum options, and writable flag). `update-settings` batch-writes allowlisted keys and reports rejects in `skipped[]` without aborting the batch; permalink changes auto-flush rewrite rules. Both require `manage_options`. Safety: curated allowlist only — no arbitrary option access; `siteurl`/`home` and `users_can_register`/`default_role` are excluded; `admin_email` is read-only.
Core WordPress settings management over MCP, curated allowlist of general/reading/writing/discussion/media/permalink settings. `get-settings` doubles as discovery (returns each setting's type, label, enum options, and writable flag). `update-settings` batch-writes allowlisted keys and reports rejects in `skipped[]` without aborting the batch; permalink changes auto-flush rewrite rules. Both require `manage_options`. Safety: curated allowlist only, no arbitrary option access; `siteurl`/`home` and `users_can_register`/`default_role` are excluded; `admin_email` is read-only.
| Tool | Description |
|---|---|
| `get-settings` | Read allowlisted WordPress settings across all groups; doubles as discovery (read-only, `manage_options`) |
| `update-settings` | Batch-update allowlisted settings; rejects reported in `skipped[]`; permalink changes auto-flush rewrite rules (`manage_options`) |
### WordPress Plugins & Themes — beyond Elementor, domain 3 (13 tools, v3.0.0)
### WordPress Plugins & Themes, beyond Elementor, domain 3 (13 tools, v3.0.0)
Discover and manage WordPress plugins and themes over MCP — built on WP core upgrader APIs. Installs are wordpress.org-only (by slug; no arbitrary URLs). Guardrails: EMCP Tools and Elementor can never be deactivated or deleted; the active plugin/theme is protected from deletion; install/update/delete require a directly-writable filesystem (clear error instead of an FTP hang). The 4 read/search tools are **enabled by default**; the **9 mutation tools ship disabled-by-default** (admin opts in on the Tools tab).
Discover and manage WordPress plugins and themes over MCP, built on WP core upgrader APIs. Installs are wordpress.org-only (by slug; no arbitrary URLs). Guardrails: EMCP Tools and Elementor can never be deactivated or deleted; the active plugin/theme is protected from deletion; install/update/delete require a directly-writable filesystem (clear error instead of an FTP hang). The 4 read/search tools are **enabled by default**; the **9 mutation tools ship disabled-by-default** (admin opts in on the Tools tab).
| Tool | Description |
|---|---|
| `list-plugins` | List installed plugins with active/inactive status, version, update-available flag, and protected marker (read-only, `activate_plugins`) |
| `search-plugins` | Search the wordpress.org plugin directory by keyword — returns slug, name, version, rating, requirements (read-only, `install_plugins`) |
| `search-plugins` | Search the wordpress.org plugin directory by keyword, returns slug, name, version, rating, requirements (read-only, `install_plugins`) |
| `install-plugin` | Install a plugin from wordpress.org by slug; optionally activate after install (`install_plugins`) |
| `activate-plugin` | Activate an installed plugin by file path or slug (`activate_plugins`) |
| `deactivate-plugin` | Deactivate a plugin; refuses to deactivate EMCP Tools or Elementor (`activate_plugins`) |
@@ -326,57 +326,57 @@ Discover and manage WordPress plugins and themes over MCP — built on WP core u
| `update-theme` | Update an installed theme to the latest wordpress.org version (`update_themes`) |
| `delete-theme` | Permanently delete an inactive, unprotected theme (`delete_themes`) |
### WordPress Media Library — beyond Elementor, domain 4 (3 tools, v3.0.0)
### WordPress Media Library, beyond Elementor, domain 4 (3 tools, v3.0.0)
Manage existing Media Library attachments over MCP — built on WP core attachment functions. `get-media` and `update-media` are **enabled by default**; `delete-media` ships **disabled-by-default** and additionally requires an explicit `confirm:true` (destructive and effectively permanent — WordPress bypasses Trash for media unless `MEDIA_TRASH` is defined). URL uploads continue to use the existing `sideload-image`.
Manage existing Media Library attachments over MCP, built on WP core attachment functions. `get-media` and `update-media` are **enabled by default**; `delete-media` ships **disabled-by-default** and additionally requires an explicit `confirm:true` (destructive and effectively permanent, WordPress bypasses Trash for media unless `MEDIA_TRASH` is defined). URL uploads continue to use the existing `sideload-image`.
| Tool | Description |
|---|---|
| `get-media` | Full detail for one attachment — every registered image size (URL + dimensions), mime type, filesize, alt text, caption, description, and raw attachment metadata (read-only, `edit_posts`) |
| `update-media` | Edit an attachment's title, alt text, caption, and/or description — only the fields you pass change (`edit_post` on ID) |
| `get-media` | Full detail for one attachment, every registered image size (URL + dimensions), mime type, filesize, alt text, caption, description, and raw attachment metadata (read-only, `edit_posts`) |
| `update-media` | Edit an attachment's title, alt text, caption, and/or description, only the fields you pass change (`edit_post` on ID) |
| `delete-media` | Delete an attachment; **destructive and effectively permanent**; disabled-by-default; requires `confirm:true`; pass `force:true` to skip Trash even when `MEDIA_TRASH` is defined (`delete_post` on ID) |
### WordPress Users — beyond Elementor, domain 5 (4 tools, v3.0.0)
### WordPress Users, beyond Elementor, domain 5 (4 tools, v3.0.0)
Safe user management over MCP — built on WP core user functions (`WP_User_Query`, `wp_insert_user`, `wp_update_user`). Guardrails: `create-user` only assigns non-admin roles and auto-generates a strong password (emailed to the new user via `wp_send_new_user_notifications` — the password is **never returned**); `update-user` edits profile fields only (no role or password changes) and refuses any user whose capabilities include `manage_options`, `promote_users`, `edit_users`, `delete_users`, or `manage_network`. There is deliberately no delete-user and no role-change tool. `list-users`/`get-user` are **enabled by default** (`list_users` cap); `create-user`/`update-user` ship **disabled-by-default** (admin opts in on the Tools tab).
Safe user management over MCP, built on WP core user functions (`WP_User_Query`, `wp_insert_user`, `wp_update_user`). Guardrails: `create-user` only assigns non-admin roles and auto-generates a strong password (emailed to the new user via `wp_send_new_user_notifications`, the password is **never returned**); `update-user` edits profile fields only (no role or password changes) and refuses any user whose capabilities include `manage_options`, `promote_users`, `edit_users`, `delete_users`, or `manage_network`. There is deliberately no delete-user and no role-change tool. `list-users`/`get-user` are **enabled by default** (`list_users` cap); `create-user`/`update-user` ship **disabled-by-default** (admin opts in on the Tools tab).
| Tool | Description |
|---|---|
| `list-users` | List WordPress users; filter by role or search text; paginated. Returns id, username, display name, email, roles, registration date, and post count. Never returns passwords or auth data (read-only, `list_users`) |
| `get-user` | Full profile detail for one user — adds first/last name, nickname, URL, description, and an `is_admin` flag (true users are off-limits to `update-user`) (read-only, `list_users`) |
| `get-user` | Full profile detail for one user, adds first/last name, nickname, URL, description, and an `is_admin` flag (true users are off-limits to `update-user`) (read-only, `list_users`) |
| `create-user` | Create a new non-admin WordPress user. A strong password is auto-generated and emailed; the password is never returned. Role defaults to `subscriber`; administrator and any admin-grade role are refused (`create_users`) |
| `update-user` | Update a non-admin user's profile (email, first/last name, display name, nickname, URL, description). Cannot change roles or passwords; refuses any user with admin-level capabilities (`edit_users`) |
### ACF (Advanced Custom Fields) — beyond Elementor (2 dispatcher tools / 15 operations, v3.2.1)
### ACF (Advanced Custom Fields), beyond Elementor (2 dispatcher tools / 15 operations, v3.2.1)
Read and write ACF field values, discover and author field groups, and register ACF-managed Custom Post Types and taxonomies over MCP — built on the public ACF API (`acf_get_field_groups`, `get_field`, `update_field`, `acf_import_field_group`, `acf_import_post_type`, `acf_import_taxonomy`). To keep the MCP tool-list small, the domain is exposed as **2 dispatcher tools** — `acf-read` and `acf-write` — each covering the operations of its kind. Call either with no `operation` to list its operations (the discovery catalog), then again with `{ operation, arguments }`. The dispatchers **only register when ACF (free or PRO) is active**; Pro-only capabilities (options pages; repeater / flexible content / gallery / clone fields) degrade gracefully on free ACF, and the CPT/taxonomy operations are available only on **ACF 6.1+**. Writes always resolve field names to field keys first (writing by name silently fails on posts with no stored value), and complex Pro fields round-trip as nested JSON arrays. Deliberately conservative authoring: no delete operations, fields can never be removed, and a field's `name`/`key`/`type` — or a post type / taxonomy slug — can never change (renames orphan stored content). Code-registered field groups (acf-json / PHP) are listed with a `local` flag and refused by the group editor; the CPT/taxonomy read operations list only the ACF-managed ones. Each dispatcher is a single toggle under **Tools → Plugins → ACF** (listing the operations it covers): `acf-read` is **enabled by default**, `acf-write` ships **disabled-by-default**. Every operation keeps its original capability check, enforced per call by the dispatcher.
Read and write ACF field values, discover and author field groups, and register ACF-managed Custom Post Types and taxonomies over MCP, built on the public ACF API (`acf_get_field_groups`, `get_field`, `update_field`, `acf_import_field_group`, `acf_import_post_type`, `acf_import_taxonomy`). To keep the MCP tool-list small, the domain is exposed as **2 dispatcher tools**: `acf-read` and `acf-write`, each covering the operations of its kind. Call either with no `operation` to list its operations (the discovery catalog), then again with `{ operation, arguments }`. The dispatchers **only register when ACF (free or PRO) is active**; Pro-only capabilities (options pages; repeater / flexible content / gallery / clone fields) degrade gracefully on free ACF, and the CPT/taxonomy operations are available only on **ACF 6.1+**. Writes always resolve field names to field keys first (writing by name silently fails on posts with no stored value), and complex Pro fields round-trip as nested JSON arrays. Deliberately conservative authoring: no delete operations, fields can never be removed, and a field's `name`/`key`/`type`, or a post type / taxonomy slug, can never change (renames orphan stored content). Code-registered field groups (acf-json / PHP) are listed with a `local` flag and refused by the group editor; the CPT/taxonomy read operations list only the ACF-managed ones. Each dispatcher is a single toggle under **Tools → Plugins → ACF** (listing the operations it covers): `acf-read` is **enabled by default**, `acf-write` ships **disabled-by-default**. Every operation keeps its original capability check, enforced per call by the dispatcher.
**Field values, field groups & options pages:**
| Tool | Description |
|---|---|
| `list-acf-field-groups` | List field groups: key, title, active state, field count, `local` flag; filter by search text, active state, or the post they apply to (read-only, `edit_posts`) |
| `get-acf-field-group` | One group's location rules and recursive field tree — names, keys, types, required, choices, `sub_fields`, flexible `layouts`. The schema-discovery step before writing values (read-only, `edit_posts`) |
| `get-acf-field-group` | One group's location rules and recursive field tree, names, keys, types, required, choices, `sub_fields`, flexible `layouts`. The schema-discovery step before writing values (read-only, `edit_posts`) |
| `list-acf-options-pages` | Registered ACF options pages with the `post_id` string to target them (ACF PRO; empty list with `pro:false` on free ACF) (read-only, `edit_posts`) |
| `get-acf-fields` | Read field values from a post (`post_id`) or options page (`options_page`) — formatted values, repeaters/flexible as nested arrays, images/posts as compact objects, unsaved fields as `null`/empty; optional `fields[]` filter (read-only, `edit_post` / `manage_options` for options) |
| `get-acf-fields` | Read field values from a post (`post_id`) or options page (`options_page`), formatted values, repeaters/flexible as nested arrays, images/posts as compact objects, unsaved fields as `null`/empty; optional `fields[]` filter (read-only, `edit_post` / `manage_options` for options) |
| `update-acf-fields` | Write field values on a post or options page: repeater rows, flexible content rows (validated `acf_fc_layout`), galleries (attachment IDs), relationships. Unknown fields are skipped with a reason; results are confirmed by re-reading (`edit_post` / `manage_options`; disabled-by-default) |
| `create-acf-field-group` | Create a field group with fields (including nested sub_fields/layouts) and location rules, persisted to the database (`manage_options`; disabled-by-default) |
| `update-acf-field-group` | Edit a database-stored group: settings, append new fields, or adjust existing fields' settings by key — no deletes, no renames, local groups refused (`manage_options`; disabled-by-default) |
| `update-acf-field-group` | Edit a database-stored group: settings, append new fields, or adjust existing fields' settings by key, no deletes, no renames, local groups refused (`manage_options`; disabled-by-default) |
**Custom Post Types & Taxonomies (ACF 6.1+):**
| Tool | Description |
|---|---|
| `list-acf-post-types` | List the Custom Post Types managed by ACF — slug, title, visibility, hierarchy, supports, attached taxonomies; `search`/`active_only` filters (read-only, `manage_options`) |
| `list-acf-post-types` | List the Custom Post Types managed by ACF, slug, title, visibility, hierarchy, supports, attached taxonomies; `search`/`active_only` filters (read-only, `manage_options`) |
| `get-acf-post-type` | One ACF-managed CPT's full definition (labels, supports, REST, taxonomies) by key or ID (read-only, `manage_options`) |
| `create-acf-post-type` | Register a new CPT through ACF (saved as data, then registered by ACF — no code executed); slug validated (≤ 20 chars, not reserved/existing); labels auto-generated (`manage_options`; disabled-by-default) |
| `create-acf-post-type` | Register a new CPT through ACF (saved as data, then registered by ACF, no code executed); slug validated (≤ 20 chars, not reserved/existing); labels auto-generated (`manage_options`; disabled-by-default) |
| `update-acf-post-type` | Edit an ACF-managed CPT (labels, visibility, supports, taxonomies); slug is immutable; native CPTs refused (`manage_options`; disabled-by-default) |
| `list-acf-taxonomies` | List the taxonomies managed by ACF — slug, title, hierarchy, and the post types each is attached to (`object_type`); `search`/`active_only` filters (read-only, `manage_options`) |
| `list-acf-taxonomies` | List the taxonomies managed by ACF, slug, title, hierarchy, and the post types each is attached to (`object_type`); `search`/`active_only` filters (read-only, `manage_options`) |
| `get-acf-taxonomy` | One ACF-managed taxonomy's full definition (labels, hierarchy, REST, `object_type`) by key or ID (read-only, `manage_options`) |
| `create-acf-taxonomy` | Register a new taxonomy through ACF; requires `object_type` (post type slugs); slug validated (≤ 32 chars); labels auto-generated (`manage_options`; disabled-by-default) |
| `update-acf-taxonomy` | Edit an ACF-managed taxonomy (labels, hierarchy, attached post types); slug is immutable (`manage_options`; disabled-by-default) |
### Meta Box — beyond Elementor (2 dispatcher tools, v3.4.2)
### Meta Box, beyond Elementor (2 dispatcher tools, v3.4.2)
Read and write [Meta Box](https://metabox.io/) custom-field values and discover its field groups over MCP. Only registers when Meta Box is active.
@@ -385,33 +385,33 @@ Read and write [Meta Box](https://metabox.io/) custom-field values and discover
| `metabox-read` | Read Meta Box field groups, field definitions, and values (posts + object_type passthrough) |
| `metabox-write` | Write Meta Box field values (disabled by default) |
### WordPress Performance & Security — beyond Elementor, domain 6 (2 tools, v3.0.0)
### WordPress Performance & Security, beyond Elementor, domain 6 (2 tools, v3.0.0)
Two read-only diagnostics over MCP, both self-contained (the only off-site calls are to wordpress.org — optional and graceful offline). Both return a scored report (0-100 + A–F grade) with severity-tagged findings and ranked recommendations. `manage_options`; enabled by default.
Two read-only diagnostics over MCP, both self-contained (the only off-site calls are to wordpress.org, optional and graceful offline). Both return a scored report (0-100 + A–F grade) with severity-tagged findings and ranked recommendations. `manage_options`; enabled by default.
| Tool | Description |
|---|---|
| `analyze-performance` | Audit server config, WordPress internals (DB size, autoloaded options, revisions, cron backlog, object cache, OPcache, plugin count), and a target page (default frontpage; optional `url`/`post_id`) for bottlenecks. Returns a scored report grouped into `server`/`database`/`config`/`page`/`assets` with ranked `top_recommendations`. Same-host-enforced loopback fetch (rejects off-host URLs/redirects); degrades gracefully when the page fetch is blocked (read-only, `manage_options`) |
| `scan-security` | Scan across malware heuristics (eval/base64 obfuscation, request-driven execution, command execution, webshells, encoded blobs, executable PHP under uploads), WordPress core-file integrity (against the official wordpress.org checksums), hardening (file editing, debug output, `admin` username, XML-RPC, version disclosure, HTTPS, security headers), and outdated/abandoned software. The malware walk is confined to ABSPATH and bounded by file-count + time caps (`deep`/`max_files`/`max_seconds`; partial results flagged) and never returns full file contents (path:line + snippet only); optional `checks` selects a subset. (read-only, `manage_options`) |
### Filesystem — beyond Elementor (6 tools, v3.0.0)
### Filesystem, beyond Elementor (6 tools, v3.0.0)
Read and scan any file inside the WordPress installation — core, plugins, themes, uploads — confined to ABSPATH (no traversal or symlink escape). The 3 read tools are **enabled by default**; the **3 write/delete tools ship disabled-by-default** and require the `edit_files` capability (honoring `DISALLOW_FILE_EDIT`). Writes auto-back up the original file; `wp-config.php` and `.htaccess` are refused by all write/delete tools; every mutation is audit-logged. `manage_options`.
Read and scan any file inside the WordPress installation, core, plugins, themes, uploads, confined to ABSPATH (no traversal or symlink escape). The 3 read tools are **enabled by default**; the **3 write/delete tools ship disabled-by-default** and require the `edit_files` capability (honoring `DISALLOW_FILE_EDIT`). Writes auto-back up the original file; `wp-config.php` and `.htaccess` are refused by all write/delete tools; every mutation is audit-logged. `manage_options`.
| Tool | Description |
|---|---|
| `read-file` | Return the contents of any file inside ABSPATH (read-only, `manage_options`) |
| `list-directory` | List the entries in a directory inside ABSPATH — names, types, sizes, modified times (read-only, `manage_options`) |
| `list-directory` | List the entries in a directory inside ABSPATH, names, types, sizes, modified times (read-only, `manage_options`) |
| `search-files` | Search for files matching a pattern or containing a string within ABSPATH (read-only, `manage_options`) |
| `write-file` | Write (create or overwrite) a file inside ABSPATH; auto-backs up the original; refuses `wp-config.php`/`.htaccess`; audit-logged (`edit_files` + `manage_options`; disabled-by-default) |
| `edit-file` | Apply a targeted find-and-replace or line-range edit to a file inside ABSPATH; auto-backs up the original; refuses `wp-config.php`/`.htaccess`; audit-logged (`edit_files` + `manage_options`; disabled-by-default) |
| `delete-file` | Delete a file inside ABSPATH; requires `confirm:true`; refuses `wp-config.php`/`.htaccess`; audit-logged (`edit_files` + `manage_options`; disabled-by-default) |
### Database — beyond Elementor (6 tools, v3.0.0)
### Database, beyond Elementor (6 tools, v3.0.0)
Inspect and manage the WordPress database over MCP. The 3 read tools are **enabled by default**; the **3 write tools ship disabled-by-default** and use `$wpdb` (no raw write-SQL, so no DDL). Writes force a non-empty WHERE clause on update/delete, refuse `wp_users`/`wp_usermeta`, snapshot a before-image into an audit log, and `delete-rows` requires an explicit `confirm:true`. `manage_options`.
> **Direct DB access.** The read path (`query`) is bounded by a read-only-SQL validator that rejects writes/DDL/stacked statements, MySQL `/*!` executable comments, and file-access SQL. Writes are structured/parameterized via `$wpdb` (no raw write-SQL/DDL), disabled-by-default, admin-gated, refuse `wp_users`/`wp_usermeta`, force a WHERE clause, snapshot a before-image, and confirm on delete. Arbitrary DB access is contrary to WordPress.org plugin guidelines — included per an explicit project decision.
> **Direct DB access.** The read path (`query`) is bounded by a read-only-SQL validator that rejects writes/DDL/stacked statements, MySQL `/*!` executable comments, and file-access SQL. Writes are structured/parameterized via `$wpdb` (no raw write-SQL/DDL), disabled-by-default, admin-gated, refuse `wp_users`/`wp_usermeta`, force a WHERE clause, snapshot a before-image, and confirm on delete. Arbitrary DB access is contrary to WordPress.org plugin guidelines, included per an explicit project decision.
| Tool | Description |
|---|---|
@@ -447,9 +447,9 @@ Inspect and manage the WordPress database over MCP. The 3 read tools are **enabl
| `batch-update` | Apply multiple element updates in a single call |
| `reorder-elements` | Reorder child elements within a container |
### Widgets — catalog-backed (5 tools)
### Widgets, catalog-backed (5 tools)
As of v3.0.0 the 62 per-widget tools (`add-heading`, `add-button`, `add-form`, the `add-wc-*` set, …) and the universal `add-widget` were replaced by a catalog-backed model. The 62 widgets' curated params now live in a built-in catalog (27 free / 30 Pro / 5 WooCommerce) instead of as individual tools — every widget and parameter is still reachable through **discover → inspect → act**, and any valid Elementor control passes straight through.
As of v3.0.0 the 62 per-widget tools (`add-heading`, `add-button`, `add-form`, the `add-wc-*` set, …) and the universal `add-widget` were replaced by a catalog-backed model. The 62 widgets' curated params now live in a built-in catalog (27 free / 30 Pro / 5 WooCommerce) instead of as individual tools, every widget and parameter is still reachable through **discover → inspect → act**, and any valid Elementor control passes straight through.
| Tool | Description |
|---|---|
@@ -459,7 +459,7 @@ As of v3.0.0 the 62 per-widget tools (`add-heading`, `add-button`, `add-form`, t
| `add-pro-widget` | Add an Elementor Pro / WooCommerce widget by type (only when Elementor Pro is active) |
| `update-widget` | Update settings on an existing widget |
### Atomic Elements — Elementor 4.0+ (13 tools)
### Atomic Elements, Elementor 4.0+ (13 tools)
These tools only register when Elementor >= 4.0 is detected. Legacy widget tools continue to work alongside them.
@@ -528,9 +528,9 @@ These tools only register when Elementor >= 4.0 is detected. Legacy widget tools
| `add-code-snippet` | Create site-wide Custom Code snippets for head/body injection (Pro) |
| `list-code-snippets` | List all Custom Code snippets with location and status filters (Pro) |
### Widget Builder — Pro (8 tools, off by default)
### Widget Builder, Pro (8 tools, off by default)
Design and register custom Elementor widgets from a structured spec — no hand-written PHP. The generator compiles the spec + an HTML template (`{{control}}`, `{{#if}}`, `{{#each}}`) into a sandboxed `Widget_Base` class, escaping every value by its control type.
Design and register custom Elementor widgets from a structured spec, no hand-written PHP. The generator compiles the spec + an HTML template (`{{control}}`, `{{#if}}`, `{{#each}}`) into a sandboxed `Widget_Base` class, escaping every value by its control type.
| Tool | Description |
|---|---|
@@ -543,9 +543,9 @@ Design and register custom Elementor widgets from a structured spec — no hand-
| `set-widget-status` | Activate or deactivate a widget |
| `delete-custom-widget` | Delete a widget (CPT + sandbox files) |
### PHP Snippets — Sandbox (6 tools, free, off by default)
### PHP Snippets, Sandbox (6 tools, free, off by default)
Let an AI agent author server-side PHP behind a hard human-approval gate. The AI can validate code and create **drafts**, but a draft never runs until an admin activates it in EMCP Tools → Sandbox — there is deliberately no "activate" tool. Every snippet is statically parse-checked and security-scanned (blocks `exec`/`eval`/backticks/file-writes/network/destructive SQL/obfuscation) before it can be saved or activated.
Let an AI agent author server-side PHP behind a hard human-approval gate. The AI can validate code and create **drafts**, but a draft never runs until an admin activates it in EMCP Tools → Sandbox, there is deliberately no "activate" tool. Every snippet is statically parse-checked and security-scanned (blocks `exec`/`eval`/backticks/file-writes/network/destructive SQL/obfuscation) before it can be saved or activated.
| Tool | Description |
|---|---|
@@ -556,7 +556,7 @@ Let an AI agent author server-side PHP behind a hard human-approval gate. The AI
| `list-php-snippets` | List snippets with status and run context |
| `delete-php-snippet` | Delete a snippet and its sandbox file |
### Global Classes — Elementor 4.0+ (1 tool)
### Global Classes, Elementor 4.0+ (1 tool)
| Tool | Description |
|---|---|
@@ -615,15 +615,15 @@ If the MCP server connects but no tools appear in Claude Code, Cursor, or other
### Common errors
- **"No MCP servers registered"** — Ensure the MCP Tools for Elementor plugin is active and all dependencies (Elementor, MCP Adapter, Abilities API) are met.
- **HTTP 401** — Check your Application Password is correct and the user has `edit_posts` capability.
- **"Missing Mcp-Session-Id header"** — The HTTP endpoint requires an `Mcp-Session-Id` header on all requests after `initialize`. Use the Node.js proxy (which handles this automatically) instead of direct HTTP connections.
- **Session errors** — If connecting via direct HTTP, your client must capture the `Mcp-Session-Id` response header from `initialize` and include it on all subsequent requests.
- **WP-CLI not found on Windows** — Use the full path to `php.exe` and `wp-cli.phar`.
- **"No MCP servers registered"**: Ensure the MCP Tools for Elementor plugin is active and all dependencies (Elementor, MCP Adapter, Abilities API) are met.
- **HTTP 401**: Check your Application Password is correct and the user has `edit_posts` capability.
- **"Missing Mcp-Session-Id header"**: The HTTP endpoint requires an `Mcp-Session-Id` header on all requests after `initialize`. Use the Node.js proxy (which handles this automatically) instead of direct HTTP connections.
- **Session errors**: If connecting via direct HTTP, your client must capture the `Mcp-Session-Id` response header from `initialize` and include it on all subsequent requests.
- **WP-CLI not found on Windows**: Use the full path to `php.exe` and `wp-cli.phar`.
## Sample Prompts
The [`prompts/`](prompts/) directory includes ready-to-use landing page prompts that demonstrate the full power of MCP Tools for Elementor tools. Each prompt is a complete blueprint — paste it into your AI client and watch an entire page get built automatically.
The [`prompts/`](prompts/) directory includes ready-to-use landing page prompts that demonstrate the full power of MCP Tools for Elementor tools. Each prompt is a complete blueprint, paste it into your AI client and watch an entire page get built automatically.
| Prompt | Industry | Description |
|---|---|---|
@@ -647,7 +647,7 @@ Each prompt includes:
## Contributing
We welcome contributions from the community! Whether it's bug reports, feature requests, documentation improvements, or code contributions — every bit helps.
We welcome contributions from the community! Whether it's bug reports, feature requests, documentation improvements, or code contributions, every bit helps.
See [CONTRIBUTING.md](CONTRIBUTING.md) for detailed guidelines on how to get started.
@@ -660,7 +660,7 @@ See [CONTRIBUTING.md](CONTRIBUTING.md) for detailed guidelines on how to get sta
## Contributors
- **[@msrbuilds](https://github.com/msrbuilds)** — Original author and maintainer
- **[@msrbuilds](https://github.com/msrbuilds)**: Original author and maintainer
## License
+1 -1
View File
@@ -132,7 +132,7 @@ class EMCP_Tools_ACF_Abilities {
'emcp-tools/acf-write',
array(
'label' => __( 'ACF Write', 'emcp-tools' ),
'description' => __( 'Write Advanced Custom Fields data: field values, field groups, and (ACF 6.1+) ACF-managed post types and taxonomies. Individual write operations are disabled by default — enable them under EMCP Tools → Tools → Plugins → ACF. Call with no "operation" to list the available write operations and their arguments, then call again with { operation, arguments }.', 'emcp-tools' ),
'description' => __( 'Write Advanced Custom Fields data: field values, field groups, and (ACF 6.1+) ACF-managed post types and taxonomies. Individual write operations are disabled by default, enable them under EMCP Tools → Tools → Plugins → ACF. Call with no "operation" to list the available write operations and their arguments, then call again with { operation, arguments }.', 'emcp-tools' ),
'category' => 'emcp-tools',
'execute_callback' => array( $this, 'run_acf_write' ),
'permission_callback' => array( $this, 'check_read_permission' ),
@@ -171,7 +171,7 @@ class EMCP_Tools_Atomic_Widget_Abilities {
$name,
array(
'label' => __( 'Update Atomic Widget', 'emcp-tools' ),
'description' => __( 'Updates settings on an existing Elementor 4.0+ atomic widget. Performs a partial merge — only provided keys are changed.', 'emcp-tools' ),
'description' => __( 'Updates settings on an existing Elementor 4.0+ atomic widget. Performs a partial merge, only provided keys are changed.', 'emcp-tools' ),
'category' => 'emcp-tools',
'execute_callback' => array( $this, 'execute_update_atomic_widget' ),
'permission_callback' => array( $this, 'check_edit_permission' ),
@@ -100,7 +100,7 @@ class EMCP_Tools_Composite_Abilities {
'emcp-tools/build-page',
array(
'label' => __( 'Build Page', 'emcp-tools' ),
'description' => __( 'Creates a complete Elementor page from a declarative structure in a single call. Supports nested containers and any widget types. IMPORTANT LAYOUT RULES: (1) For side-by-side columns, use a parent container with flex_direction=row — children are auto-set to content_width=full with equal percentage widths (e.g. 2 children = 50%, 3 = 33.33%). (2) NEVER set flex_wrap or _flex_size in settings — these cause layout overflow. The tool handles layout automatically. (3) Background colors: set background_background=classic and background_color=#hex on containers. (4) Background images: set background_background=classic, background_image={url,id}, background_size=cover. (5) Background overlay: background_overlay_background=classic, background_overlay_color=#hex, background_overlay_opacity={size:0.7,unit:px}. (6) Text alignment: text_align on text/heading widgets. (7) Use search-images and sideload-image tools to get real images before building.', 'emcp-tools' ),
'description' => __( 'Creates a complete Elementor page from a declarative structure in a single call. Supports nested containers and any widget types. IMPORTANT LAYOUT RULES: (1) For side-by-side columns, use a parent container with flex_direction=row, children are auto-set to content_width=full with equal percentage widths (e.g. 2 children = 50%, 3 = 33.33%). (2) NEVER set flex_wrap or _flex_size in settings, these cause layout overflow. The tool handles layout automatically. (3) Background colors: set background_background=classic and background_color=#hex on containers. (4) Background images: set background_background=classic, background_image={url,id}, background_size=cover. (5) Background overlay: background_overlay_background=classic, background_overlay_color=#hex, background_overlay_opacity={size:0.7,unit:px}. (6) Text alignment: text_align on text/heading widgets. (7) Use search-images and sideload-image tools to get real images before building.', 'emcp-tools' ),
'category' => 'emcp-tools',
'execute_callback' => array( $this, 'execute_build_page' ),
'permission_callback' => array( $this, 'check_create_permission' ),
@@ -127,7 +127,7 @@ class EMCP_Tools_Composite_Abilities {
),
'structure' => array(
'type' => 'array',
'description' => __( 'Declarative element tree. Each item is type:"container" (with children) or type:"widget" (with widget_type + settings). Shorthand is accepted and coerced: type:"heading" is read as a heading widget, and any node with children is treated as a container — but the response lists these coercions under "warnings", so prefer the explicit shape. Every widget needs a widget_type; a widget with none is skipped and reported.', 'emcp-tools' ),
'description' => __( 'Declarative element tree. Each item is type:"container" (with children) or type:"widget" (with widget_type + settings). Shorthand is accepted and coerced: type:"heading" is read as a heading widget, and any node with children is treated as a container, but the response lists these coercions under "warnings", so prefer the explicit shape. Every widget needs a widget_type; a widget with none is skipped and reported.', 'emcp-tools' ),
'items' => array(
'type' => 'object',
'properties' => array(
@@ -155,7 +155,7 @@ class EMCP_Tools_Composite_Abilities {
'elements_created' => array( 'type' => 'integer' ),
'warnings' => array(
'type' => 'array',
'description' => __( 'Non-fatal notes: nodes that were coerced from shorthand or skipped. If present, some elements did not land exactly as written — fix and rebuild or patch with the layout/widget tools.', 'emcp-tools' ),
'description' => __( 'Non-fatal notes: nodes that were coerced from shorthand or skipped. If present, some elements did not land exactly as written, fix and rebuild or patch with the layout/widget tools.', 'emcp-tools' ),
'items' => array( 'type' => 'string' ),
),
),
@@ -307,7 +307,7 @@ class EMCP_Tools_Composite_Abilities {
if ( '' !== $type ) {
if ( empty( $item['widget_type'] ) ) {
$item['widget_type'] = $type;
$this->warnings[] = sprintf( 'Interpreted "type":"%1$s" as a "%1$s" widget — prefer {"type":"widget","widget_type":"%1$s"}.', $type );
$this->warnings[] = sprintf( 'Interpreted "type":"%1$s" as a "%1$s" widget, prefer {"type":"widget","widget_type":"%1$s"}.', $type );
} else {
$this->warnings[] = sprintf( 'Node had type "%1$s" and widget_type "%2$s"; used widget_type "%2$s".', $type, (string) $item['widget_type'] );
}
@@ -373,7 +373,7 @@ class EMCP_Tools_Composite_Abilities {
$settings = $item['settings'] ?? array();
if ( empty( $widget_type ) ) {
$this->warnings[] = 'Skipped a widget with no widget_type — give each widget a widget_type (e.g. "heading", "button", "image").';
$this->warnings[] = 'Skipped a widget with no widget_type, give each widget a widget_type (e.g. "heading", "button", "image").';
} else {
$widget = $this->factory->create_widget( $widget_type, $settings );
$this->elements_created++;
@@ -382,7 +382,7 @@ class EMCP_Tools_Content_Abilities {
'emcp-tools/create-post',
array(
'label' => __( 'Create Post', 'emcp-tools' ),
'description' => __( 'Creates a post, page, or any custom post type. Sets title, content (classic HTML or Gutenberg block markup), excerpt, status, slug, author, date, parent/menu_order, plus optional taxonomy terms, custom-field meta, and a featured image — in one call. This writes post_content; to build an Elementor page use the Elementor tools instead.', 'emcp-tools' ),
'description' => __( 'Creates a post, page, or any custom post type. Sets title, content (classic HTML or Gutenberg block markup), excerpt, status, slug, author, date, parent/menu_order, plus optional taxonomy terms, custom-field meta, and a featured image, in one call. This writes post_content; to build an Elementor page use the Elementor tools instead.', 'emcp-tools' ),
'category' => 'emcp-tools',
'execute_callback' => array( $this, 'execute_create_post' ),
'permission_callback' => array( $this, 'check_create_permission' ),
@@ -391,7 +391,7 @@ class EMCP_Tools_Content_Abilities {
'properties' => array(
'post_type' => array( 'type' => 'string', 'description' => __( 'Target type (post, page, or a CPT from list-post-types). Default: post.', 'emcp-tools' ) ),
'title' => array( 'type' => 'string', 'description' => __( 'Post title.', 'emcp-tools' ) ),
'content' => array( 'type' => 'string', 'description' => __( 'post_content — classic HTML or Gutenberg block markup, stored verbatim.', 'emcp-tools' ) ),
'content' => array( 'type' => 'string', 'description' => __( 'post_content, classic HTML or Gutenberg block markup, stored verbatim.', 'emcp-tools' ) ),
'excerpt' => array( 'type' => 'string' ),
'status' => array( 'type' => 'string', 'enum' => array( 'draft', 'publish', 'pending', 'private', 'future' ), 'description' => __( 'Default: draft.', 'emcp-tools' ) ),
'slug' => array( 'type' => 'string' ),
@@ -635,7 +635,7 @@ class EMCP_Tools_Content_Abilities {
'emcp-tools/update-post',
array(
'label' => __( 'Update Post', 'emcp-tools' ),
'description' => __( 'Partial update of a post/page/CPT. Only the fields you pass change. terms_mode controls replace/append; meta upserts the given keys; featured_image:null clears it. Does not touch Elementor data — use the Elementor tools for builder pages.', 'emcp-tools' ),
'description' => __( 'Partial update of a post/page/CPT. Only the fields you pass change. terms_mode controls replace/append; meta upserts the given keys; featured_image:null clears it. Does not touch Elementor data, use the Elementor tools for builder pages.', 'emcp-tools' ),
'category' => 'emcp-tools',
'execute_callback' => array( $this, 'execute_update_post' ),
'permission_callback' => array( $this, 'check_edit_permission' ),
@@ -644,7 +644,7 @@ class EMCP_Tools_Content_Abilities {
'properties' => array(
'post_id' => array( 'type' => 'integer', 'description' => __( 'The post ID.', 'emcp-tools' ) ),
'title' => array( 'type' => 'string' ),
'content' => array( 'type' => 'string', 'description' => __( 'post_content — classic HTML or block markup.', 'emcp-tools' ) ),
'content' => array( 'type' => 'string', 'description' => __( 'post_content, classic HTML or block markup.', 'emcp-tools' ) ),
'excerpt' => array( 'type' => 'string' ),
'status' => array( 'type' => 'string', 'enum' => array( 'draft', 'publish', 'pending', 'private', 'future' ) ),
'slug' => array( 'type' => 'string' ),
@@ -811,7 +811,7 @@ class EMCP_Tools_Content_Abilities {
'emcp-tools/list-posts',
array(
'label' => __( 'List Posts', 'emcp-tools' ),
'description' => __( 'Lists/searches posts, pages, or any CPT. Filter by type, status, search text, taxonomy term, author, or parent; paginated. Returns compact rows (no content body) — call get-post for the full content. The is_elementor flag flags builder pages.', 'emcp-tools' ),
'description' => __( 'Lists/searches posts, pages, or any CPT. Filter by type, status, search text, taxonomy term, author, or parent; paginated. Returns compact rows (no content body), call get-post for the full content. The is_elementor flag flags builder pages.', 'emcp-tools' ),
'category' => 'emcp-tools',
'execute_callback' => array( $this, 'execute_list_posts' ),
'permission_callback' => array( $this, 'check_read_permission' ),
@@ -64,7 +64,7 @@ class EMCP_Tools_Content_Mirror_Abilities {
'emcp-tools/restore-content',
array(
'label' => __( 'Restore Content', 'emcp-tools' ),
'description' => __( 'Restores a page/template\'s Elementor content from its mirror file (the JSON previously written by export-content) — a file-based undo. Overwrites the current content with the mirrored version.', 'emcp-tools' ),
'description' => __( 'Restores a page/template\'s Elementor content from its mirror file (the JSON previously written by export-content), a file-based undo. Overwrites the current content with the mirrored version.', 'emcp-tools' ),
'category' => 'emcp-tools',
'execute_callback' => array( $this, 'execute_restore' ),
'permission_callback' => array( $this, 'check_permission' ),
@@ -322,7 +322,7 @@ class EMCP_Tools_Custom_Code_Abilities {
'emcp-tools/add-custom-js',
array(
'label' => __( 'Add Custom JavaScript', 'emcp-tools' ),
'description' => __( 'Adds a custom JavaScript snippet to a page by inserting an HTML widget containing a <script> tag. Works with free Elementor (no Pro required). The JS code is automatically wrapped in <script> tags — do NOT include them yourself. Use wrap_dom_ready=true to wrap in a DOMContentLoaded listener. For site-wide JS, use add-code-snippet instead (requires Pro).', 'emcp-tools' ),
'description' => __( 'Adds a custom JavaScript snippet to a page by inserting an HTML widget containing a <script> tag. Works with free Elementor (no Pro required). The JS code is automatically wrapped in <script> tags, do NOT include them yourself. Use wrap_dom_ready=true to wrap in a DOMContentLoaded listener. For site-wide JS, use add-code-snippet instead (requires Pro).', 'emcp-tools' ),
'category' => 'emcp-tools',
'execute_callback' => array( $this, 'execute_add_custom_js' ),
'permission_callback' => array( $this, 'check_js_permission' ),
@@ -339,7 +339,7 @@ class EMCP_Tools_Custom_Code_Abilities {
),
'js' => array(
'type' => 'string',
'description' => __( 'JavaScript code to inject. Do NOT include <script> tags — they are added automatically.', 'emcp-tools' ),
'description' => __( 'JavaScript code to inject. Do NOT include <script> tags, they are added automatically.', 'emcp-tools' ),
),
'position' => array(
'type' => 'integer',
@@ -85,7 +85,7 @@ class EMCP_Tools_Filesystem_Abilities {
$content = (string) file_get_contents( $abs );
$rel = EMCP_Tools_Filesystem_Guard::to_relative( $abs );
if ( ! EMCP_Tools_Filesystem_Guard::is_utf8( $content ) ) {
return array( 'path' => $rel, 'size' => $size, 'binary' => true, 'message' => __( 'Binary file — not returned as text.', 'emcp-tools' ) );
return array( 'path' => $rel, 'size' => $size, 'binary' => true, 'message' => __( 'Binary file, not returned as text.', 'emcp-tools' ) );
}
$offset = isset( $input['offset'] ) ? max( 1, (int) $input['offset'] ) : 0;
$limit = isset( $input['limit'] ) ? max( 0, (int) $input['limit'] ) : 0;
@@ -119,7 +119,7 @@ class EMCP_Tools_Global_Classes_Abilities {
*/
public function execute_list_global_classes( $input ) {
if ( ! self::is_available() ) {
return new \WP_Error( 'unavailable', __( 'Global Classes are not available — Elementor 4.0+ is required.', 'emcp-tools' ) );
return new \WP_Error( 'unavailable', __( 'Global Classes are not available, Elementor 4.0+ is required.', 'emcp-tools' ) );
}
$filter = array();
@@ -235,7 +235,7 @@ class EMCP_Tools_Gutenberg_Abilities {
'emcp-tools/list-patterns',
array(
'label' => __( 'List Patterns', 'emcp-tools' ),
'description' => __( 'Lists registered block patterns (prebuilt block compositions) — name, title, categories, description. Use insert-pattern to drop one into a post.', 'emcp-tools' ),
'description' => __( 'Lists registered block patterns (prebuilt block compositions), name, title, categories, description. Use insert-pattern to drop one into a post.', 'emcp-tools' ),
'category' => 'emcp-tools',
'execute_callback' => array( $this, 'execute_list_patterns' ),
'permission_callback' => array( $this, 'check_read_permission' ),
@@ -33,7 +33,7 @@ class EMCP_Tools_Image_Resize_Abilities {
'emcp-tools/resize-media',
array(
'label' => __( 'Resize Media', 'emcp-tools' ),
'description' => __( 'Resize an existing Media Library image in place (the attachment ID and its URLs stay the same). Scales to fit the given width and/or height, preserving aspect ratio; pass crop:true to hard-crop to exactly width×height. The original is backed up (reversible), all sub-sizes + WebP are regenerated, and — if the module\'s max-dimension cap is smaller than your target — the cap applies. JPEG/PNG/WebP/GIF.', 'emcp-tools' ),
'description' => __( 'Resize an existing Media Library image in place (the attachment ID and its URLs stay the same). Scales to fit the given width and/or height, preserving aspect ratio; pass crop:true to hard-crop to exactly width×height. The original is backed up (reversible), all sub-sizes + WebP are regenerated, and, if the module\'s max-dimension cap is smaller than your target, the cap applies. JPEG/PNG/WebP/GIF.', 'emcp-tools' ),
'category' => 'emcp-tools',
'execute_callback' => array( $this, 'execute_resize' ),
'permission_callback' => array( $this, 'check_permission' ),
@@ -351,7 +351,7 @@ class EMCP_Tools_Layout_Abilities {
'emcp-tools/update-element',
array(
'label' => __( 'Update Element', 'emcp-tools' ),
'description' => __( 'Updates settings on any element (container or widget). Settings are merged (partial update). Works for all element types — no need to know if the target is a container or widget. For v4 atomic elements you may also include a `styles` map (the element\'s local CSS classes) and/or `editor_settings` (e.g. `{ "title": "Hero" }` for the Navigator label) in the settings object — these are routed to the element root automatically. Use set-element-label for just the Navigator name.', 'emcp-tools' ),
'description' => __( 'Updates settings on any element (container or widget). Settings are merged (partial update). Works for all element types, no need to know if the target is a container or widget. For v4 atomic elements you may also include a `styles` map (the element\'s local CSS classes) and/or `editor_settings` (e.g. `{ "title": "Hero" }` for the Navigator label) in the settings object, these are routed to the element root automatically. Use set-element-label for just the Navigator name.', 'emcp-tools' ),
'category' => 'emcp-tools',
'execute_callback' => array( $this, 'execute_update_element' ),
'permission_callback' => array( $this, 'check_edit_permission' ),
@@ -442,7 +442,7 @@ class EMCP_Tools_Layout_Abilities {
'emcp-tools/batch-update',
array(
'label' => __( 'Batch Update Elements', 'emcp-tools' ),
'description' => __( 'Updates multiple elements in a single save operation. Each operation specifies an element_id and settings to merge. Much more efficient than calling update-element multiple times. As with update-element, a per-operation settings object may include a `styles` map and/or `editor_settings` for v4 atomic elements — these are routed to the element root automatically.', 'emcp-tools' ),
'description' => __( 'Updates multiple elements in a single save operation. Each operation specifies an element_id and settings to merge. Much more efficient than calling update-element multiple times. As with update-element, a per-operation settings object may include a `styles` map and/or `editor_settings` for v4 atomic elements, these are routed to the element root automatically.', 'emcp-tools' ),
'category' => 'emcp-tools',
'execute_callback' => array( $this, 'execute_batch_update' ),
'permission_callback' => array( $this, 'check_edit_permission' ),
@@ -552,7 +552,7 @@ class EMCP_Tools_Layout_Abilities {
'emcp-tools/set-element-label',
array(
'label' => __( 'Set Element Label', 'emcp-tools' ),
'description' => __( 'Sets an element\'s Navigator label (stored in editor_settings.title). Works for any element; especially useful on v4 atomic elements to keep the layout readable. A convenience wrapper — the same result can be had via update-element with editor_settings.', 'emcp-tools' ),
'description' => __( 'Sets an element\'s Navigator label (stored in editor_settings.title). Works for any element; especially useful on v4 atomic elements to keep the layout readable. A convenience wrapper, the same result can be had via update-element with editor_settings.', 'emcp-tools' ),
'category' => 'emcp-tools',
'execute_callback' => array( $this, 'execute_set_element_label' ),
'permission_callback' => array( $this, 'check_edit_permission' ),
@@ -486,7 +486,7 @@ class EMCP_Tools_Media_Library_Abilities {
'emcp-tools/delete-media',
array(
'label' => __( 'Delete Media', 'emcp-tools' ),
'description' => __( 'Deletes a Media Library attachment. DESTRUCTIVE and effectively permanent — WordPress bypasses Trash for media unless MEDIA_TRASH is defined. Requires confirm:true. Pass force:true to skip Trash even when MEDIA_TRASH is on.', 'emcp-tools' ),
'description' => __( 'Deletes a Media Library attachment. DESTRUCTIVE and effectively permanent, WordPress bypasses Trash for media unless MEDIA_TRASH is defined. Requires confirm:true. Pass force:true to skip Trash even when MEDIA_TRASH is on.', 'emcp-tools' ),
'category' => 'emcp-tools',
'execute_callback' => array( $this, 'execute_delete_media' ),
'permission_callback' => array( $this, 'check_delete_permission' ),
@@ -105,7 +105,7 @@ class EMCP_Tools_Meta_Box_Abilities {
'emcp-tools/metabox-write',
array(
'label' => __( 'Meta Box Write', 'emcp-tools' ),
'description' => __( 'Write Meta Box (metabox.io) custom-field values. Disabled by default — enable under EMCP Tools → Tools → Plugins → Meta Box. Call with no "operation" to list the available write operations and their arguments, then call again with { operation, arguments }.', 'emcp-tools' ),
'description' => __( 'Write Meta Box (metabox.io) custom-field values. Disabled by default, enable under EMCP Tools → Tools → Plugins → Meta Box. Call with no "operation" to list the available write operations and their arguments, then call again with { operation, arguments }.', 'emcp-tools' ),
'category' => 'emcp-tools',
'execute_callback' => array( $this, 'run_metabox_write' ),
'permission_callback' => array( $this, 'check_read_permission' ),
@@ -179,7 +179,7 @@ class EMCP_Tools_Plugin_Abilities {
'emcp-tools/install-plugin',
array(
'label' => __( 'Install Plugin', 'emcp-tools' ),
'description' => __( 'Installs a plugin from the wordpress.org directory by slug (e.g. "contact-form-7"). Optionally activates it. Source is always wordpress.org — arbitrary URLs are not accepted.', 'emcp-tools' ),
'description' => __( 'Installs a plugin from the wordpress.org directory by slug (e.g. "contact-form-7"). Optionally activates it. Source is always wordpress.org, arbitrary URLs are not accepted.', 'emcp-tools' ),
'category' => 'emcp-tools',
'execute_callback' => array( $this, 'execute_install_plugin' ),
'permission_callback' => array( $this, 'can_install' ),
@@ -43,7 +43,7 @@ class EMCP_Tools_Search_Abilities {
'emcp-tools/search-content',
array(
'label' => __( 'Search Content', 'emcp-tools' ),
'description' => __( 'Searches an indexed corpus of the site\'s own pages, saved templates, widgets, and global styles by natural-language query, returning the best matches ranked by relevance — so you can REUSE an existing page/template/widget instead of building from scratch. Returns object_type + object_id + title + score + snippet; then read/clone the winner with the relevant tool (get-page-structure, apply-template, add-*-widget, etc.). Filter by types. Call reindex-search first if results look stale. Read-only.', 'emcp-tools' ),
'description' => __( 'Searches an indexed corpus of the site\'s own pages, saved templates, widgets, and global styles by natural-language query, returning the best matches ranked by relevance, so you can REUSE an existing page/template/widget instead of building from scratch. Returns object_type + object_id + title + score + snippet; then read/clone the winner with the relevant tool (get-page-structure, apply-template, add-*-widget, etc.). Filter by types. Call reindex-search first if results look stale. Read-only.', 'emcp-tools' ),
'category' => 'emcp-tools',
'execute_callback' => array( $this, 'execute_search' ),
'permission_callback' => array( $this, 'check_read_permission' ),
@@ -350,7 +350,7 @@ class EMCP_Tools_Settings_Abilities {
'emcp-tools/update-settings',
array(
'label' => __( 'Update Settings', 'emcp-tools' ),
'description' => __( 'Updates curated WordPress site settings from a map of key → value. Only allowlisted, writable keys are changed; non-allowlisted, read-only (admin_email), or invalid values are returned in "skipped" with a reason — one bad key never aborts the batch. Changing a permalink setting (permalink_structure, category_base, tag_base) flushes rewrite rules automatically.', 'emcp-tools' ),
'description' => __( 'Updates curated WordPress site settings from a map of key → value. Only allowlisted, writable keys are changed; non-allowlisted, read-only (admin_email), or invalid values are returned in "skipped" with a reason, one bad key never aborts the batch. Changing a permalink setting (permalink_structure, category_base, tag_base) flushes rewrite rules automatically.', 'emcp-tools' ),
'category' => 'emcp-tools',
'execute_callback' => array( $this, 'execute_update_settings' ),
'permission_callback' => array( $this, 'check_manage_permission' ),
@@ -59,7 +59,7 @@ class EMCP_Tools_Snapshot_Abilities {
'emcp-tools/get-page-snapshot',
array(
'label' => __( 'Get Page Snapshot', 'emcp-tools' ),
'description' => __( 'Returns ONE normalized digest of a page: structure tree + counts, global colors/typography/classes actually in use, per-device responsive overrides, content outline, and an SEO-lite summary — so you can reason about a page from a single call instead of chaining get-page-structure/get-global-settings/list-global-classes. Pass include:[performance,a11y,seo] for heavy audit summaries (a11y/seo are Pro). Read-only.', 'emcp-tools' ),
'description' => __( 'Returns ONE normalized digest of a page: structure tree + counts, global colors/typography/classes actually in use, per-device responsive overrides, content outline, and an SEO-lite summary, so you can reason about a page from a single call instead of chaining get-page-structure/get-global-settings/list-global-classes. Pass include:[performance,a11y,seo] for heavy audit summaries (a11y/seo are Pro). Read-only.', 'emcp-tools' ),
'category' => 'emcp-tools',
'execute_callback' => array( $this, 'execute' ),
'permission_callback' => array( $this, 'check_read_permission' ),
@@ -203,7 +203,7 @@ class EMCP_Tools_Spectra_Integration extends EMCP_Tools_Theme_Integration {
$shared = EMCP_Tools_Spectra_Catalog::shared_attributes( $name );
if ( ! empty( $shared ) ) {
$entry['shared_attributes'] = $shared;
$entry['schema_note'] = __( 'Some native attributes (background image/video, border radius, box shadow) are registered via a shared helper and are NOT in the attribute list above — see shared_attributes. Prefer these native container attributes over a core/html workaround; they are editable in Spectra and survive editor saves.', 'emcp-tools' );
$entry['schema_note'] = __( 'Some native attributes (background image/video, border radius, box shadow) are registered via a shared helper and are NOT in the attribute list above, see shared_attributes. Prefer these native container attributes over a core/html workaround; they are editable in Spectra and survive editor saves.', 'emcp-tools' );
}
if ( $full ) {
@@ -324,7 +324,7 @@ class EMCP_Tools_Stock_Image_Abilities {
'emcp-tools/sideload-image',
array(
'label' => __( 'Sideload Image', 'emcp-tools' ),
'description' => __( 'Downloads an external image URL into the WordPress Media Library and returns the local attachment ID and URL. Use this after search-images: pass the EXACT `url` from a search result — never construct, guess, or edit an image URL (a made-up URL 404s, and the Unsplash api.unsplash.com/…/download endpoint needs a key). For stock photos, prefer add-stock-image, which searches, sideloads, and places the image in one call.', 'emcp-tools' ),
'description' => __( 'Downloads an external image URL into the WordPress Media Library and returns the local attachment ID and URL. Use this after search-images: pass the EXACT `url` from a search result, never construct, guess, or edit an image URL (a made-up URL 404s, and the Unsplash api.unsplash.com/…/download endpoint needs a key). For stock photos, prefer add-stock-image, which searches, sideloads, and places the image in one call.', 'emcp-tools' ),
'category' => 'emcp-tools',
'execute_callback' => array( $this, 'execute_sideload_image' ),
'permission_callback' => array( $this, 'check_upload_permission' ),
@@ -423,11 +423,11 @@ class EMCP_Tools_Stock_Image_Abilities {
$msg = $tmp_file->get_error_message();
$hint = '';
if ( false !== strpos( $url, 'api.unsplash.com' ) ) {
$hint = ' ' . __( 'That is the Unsplash API URL — pass the direct image `url` from search-images instead, or use add-stock-image.', 'emcp-tools' );
$hint = ' ' . __( 'That is the Unsplash API URL, pass the direct image `url` from search-images instead, or use add-stock-image.', 'emcp-tools' );
} elseif ( preg_match( '/not found|404/i', $msg ) ) {
$hint = ' ' . __( 'Use the exact `url` returned by search-images (do not construct or edit image URLs), or use add-stock-image (search + sideload + place in one call).', 'emcp-tools' );
} elseif ( preg_match( '/unauthorized|forbidden|401|403/i', $msg ) ) {
$hint = ' ' . __( 'The URL requires authorization — use the direct `url` from search-images, or use add-stock-image.', 'emcp-tools' );
$hint = ' ' . __( 'The URL requires authorization, use the direct `url` from search-images, or use add-stock-image.', 'emcp-tools' );
}
return new \WP_Error(
'download_failed',
@@ -519,7 +519,7 @@ class EMCP_Tools_Stock_Image_Abilities {
'emcp-tools/add-stock-image',
array(
'label' => __( 'Add Stock Image', 'emcp-tools' ),
'description' => __( 'Searches a stock provider (Unsplash, Pexels, or Pixabay) for a photo, downloads it to the Media Library, and adds it as an image widget to the page — all in one step. Defaults to landscape (wide) images for consistent layouts. Combines search-images + sideload-image + add-free-widget. Requires a free API key for at least one provider (EMCP Tools → Connection).', 'emcp-tools' ),
'description' => __( 'Searches a stock provider (Unsplash, Pexels, or Pixabay) for a photo, downloads it to the Media Library, and adds it as an image widget to the page, all in one step. Defaults to landscape (wide) images for consistent layouts. Combines search-images + sideload-image + add-free-widget. Requires a free API key for at least one provider (EMCP Tools → Connection).', 'emcp-tools' ),
'category' => 'emcp-tools',
'execute_callback' => array( $this, 'execute_add_stock_image' ),
'permission_callback' => array( $this, 'check_combined_permission' ),
@@ -235,13 +235,13 @@ abstract class EMCP_Tools_Theme_Integration {
* @return string
*/
protected function read_description(): string {
return $this->label() . ' — read operations. Call with no operation to list them.';
return $this->label() . ', read operations. Call with no operation to list them.';
}
/**
* @return string
*/
protected function write_description(): string {
return $this->label() . ' — write operations. Call with no operation to list them.';
return $this->label() . ', write operations. Call with no operation to list them.';
}
}
@@ -341,7 +341,7 @@ class EMCP_Tools_Themer_Abilities {
'emcp-tools/resolve-template',
array(
'label' => __( 'Resolve Template', 'emcp-tools' ),
'description' => __( 'Given a post_id (single) or a query descriptor, returns which templates fill the header/body/footer slots — so you can verify your conditions wired correctly.', 'emcp-tools' ),
'description' => __( 'Given a post_id (single) or a query descriptor, returns which templates fill the header/body/footer slots, so you can verify your conditions wired correctly.', 'emcp-tools' ),
'category' => 'emcp-tools',
'execute_callback' => array( $this, 'execute_resolve' ),
'permission_callback' => array( $this, 'check_read_permission' ),
@@ -61,7 +61,7 @@ class EMCP_Tools_Themer_PHP_Abilities {
'emcp-tools/create-theme-php-template',
array(
'label' => __( 'Create Theme PHP Template', 'emcp-tools' ),
'description' => __( 'Create a DRAFT PHP template for a Themer region (header|footer|single|archive|any). Emit markup with echo/heredoc — a closing PHP tag is not allowed. Validated; critical constructs (code execution, shell, file loading, network, file writes) are rejected. Never runs until a human selects it in a template metabox.', 'emcp-tools' ),
'description' => __( 'Create a DRAFT PHP template for a Themer region (header|footer|single|archive|any). Emit markup with echo/heredoc, a closing PHP tag is not allowed. Validated; critical constructs (code execution, shell, file loading, network, file writes) are rejected. Never runs until a human selects it in a template metabox.', 'emcp-tools' ),
'category' => 'emcp-tools',
'execute_callback' => array( $this, 'execute_create' ),
'permission_callback' => array( $this, 'check_write_permission' ),
@@ -47,7 +47,7 @@ class EMCP_Tools_Transaction_Abilities {
'emcp-tools/list-changes',
array(
'label' => __( 'List Changes', 'emcp-tools' ),
'description' => __( 'Lists recent AI-made changes (Elementor edits, filesystem writes, database writes) recorded in the change ledger, newest first — each with a summary, whether it is reversible, and whether it has already been rolled back. Filter by domain (elementor/filesystem/database), rolled_back, or reversible. Use get-change for full detail and rollback-change to undo one. Read-only.', 'emcp-tools' ),
'description' => __( 'Lists recent AI-made changes (Elementor edits, filesystem writes, database writes) recorded in the change ledger, newest first, each with a summary, whether it is reversible, and whether it has already been rolled back. Filter by domain (elementor/filesystem/database), rolled_back, or reversible. Use get-change for full detail and rollback-change to undo one. Read-only.', 'emcp-tools' ),
'category' => 'emcp-tools',
'execute_callback' => array( $this, 'execute_list' ),
'permission_callback' => array( $this, 'check_manage' ),
@@ -85,7 +85,7 @@ class EMCP_Tools_Transaction_Abilities {
'emcp-tools/rollback-change',
array(
'label' => __( 'Roll Back Change', 'emcp-tools' ),
'description' => __( 'Undoes one recorded change by id — restores a page\'s prior Elementor data, restores/removes a file from its backup, or inverses a database write from its before-image. Marks the entry rolled back (no double-rollback) and records a compensating entry. Only reverts changes EMCP itself recorded.', 'emcp-tools' ),
'description' => __( 'Undoes one recorded change by id, restores a page\'s prior Elementor data, restores/removes a file from its backup, or inverses a database write from its before-image. Marks the entry rolled back (no double-rollback) and records a compensating entry. Only reverts changes EMCP itself recorded.', 'emcp-tools' ),
'category' => 'emcp-tools',
'execute_callback' => array( $this, 'execute_rollback' ),
'permission_callback' => array( $this, 'check_manage' ),
+1 -1
View File
@@ -260,7 +260,7 @@ class EMCP_Tools_User_Abilities {
'emcp-tools/create-user',
array(
'label' => __( 'Create User', 'emcp-tools' ),
'description' => __( 'Creates a new non-admin WordPress user. A strong password is generated automatically and the user is emailed a set-password link — the password is never returned. The role defaults to subscriber and cannot be an administrator or any admin-grade role.', 'emcp-tools' ),
'description' => __( 'Creates a new non-admin WordPress user. A strong password is generated automatically and the user is emailed a set-password link, the password is never returned. The role defaults to subscriber and cannot be an administrator or any admin-grade role.', 'emcp-tools' ),
'category' => 'emcp-tools',
'execute_callback' => array( $this, 'execute_create_user' ),
'permission_callback' => array( $this, 'can_create' ),
@@ -248,10 +248,10 @@ class EMCP_Tools_Widget_Abilities {
// Tier gate. 'free' tool: reject Pro/Woo types. 'pro' tool: reject free types.
if ( 'free' === $expected_tier && $is_pro ) {
return new \WP_Error( 'wrong_tier', __( 'That is a Pro widget — use add-pro-widget.', 'emcp-tools' ) );
return new \WP_Error( 'wrong_tier', __( 'That is a Pro widget, use add-pro-widget.', 'emcp-tools' ) );
}
if ( 'pro' === $expected_tier && null !== $entry && ! $is_pro ) {
return new \WP_Error( 'wrong_tier', __( 'That is a free widget — use add-free-widget.', 'emcp-tools' ) );
return new \WP_Error( 'wrong_tier', __( 'That is a free widget, use add-free-widget.', 'emcp-tools' ) );
}
// Merge catalog defaults under the caller's settings (caller wins).
@@ -277,13 +277,13 @@ abstract class EMCP_Tools_Form_Integration {
* @return string
*/
protected function read_description(): string {
return $this->label() . ' — read operations. Call with no operation to list them.';
return $this->label() . ', read operations. Call with no operation to list them.';
}
/**
* @return string
*/
protected function write_description(): string {
return $this->label() . ' — write operations. Call with no operation to list them.';
return $this->label() . ', write operations. Call with no operation to list them.';
}
}
@@ -283,13 +283,13 @@ abstract class EMCP_Tools_SEO_Integration {
* @return string
*/
protected function read_description(): string {
return $this->label() . ' — read operations (post/term SEO, settings). Call with no operation to list them.';
return $this->label() . ', read operations (post/term SEO, settings). Call with no operation to list them.';
}
/**
* @return string
*/
protected function write_description(): string {
return $this->label() . ' — write operations (post/term SEO, settings). Call with no operation to list them.';
return $this->label() . ', write operations (post/term SEO, settings). Call with no operation to list them.';
}
}
+37 -37
View File
@@ -1274,7 +1274,7 @@ class EMCP_Tools_Admin {
'oauthEnabled' => class_exists( 'EMCP_Tools_OAuth_Server' ) && EMCP_Tools_OAuth_Server::is_enabled(),
'oauthSignin' => __( 'The next time your AI client connects, your browser opens so you can authorize it. Approve to finish connecting.', 'emcp-tools' ),
/* translators: %s: client label */
'genFirst' => __( 'Generate your credentials above — the config for %s then appears here.', 'emcp-tools' ),
'genFirst' => __( 'Generate your credentials above, the config for %s then appears here.', 'emcp-tools' ),
'siteUrl' => site_url(),
'restMeUrl' => rest_url( 'wp/v2/users/me' ),
// Only the filename — never the absolute server path. The proxy runs
@@ -1284,14 +1284,14 @@ class EMCP_Tools_Admin {
'proxyPath' => 'mcp-proxy.mjs',
// Connection auth self-test (#41).
'authTesting' => __( 'Testing…', 'emcp-tools' ),
'authOk' => __( '✓ Authentication works — your AI client should connect successfully.', 'emcp-tools' ),
'authFail' => __( '✗ Authentication failed (HTTP %d). If the credentials are correct, your server is stripping the Authorization header — see the fix below.', 'emcp-tools' ),
'authOk' => __( '✓ Authentication works, your AI client should connect successfully.', 'emcp-tools' ),
'authFail' => __( '✗ Authentication failed (HTTP %d). If the credentials are correct, your server is stripping the Authorization header, see the fix below.', 'emcp-tools' ),
'authError' => __( 'Could not reach the REST API to test. Check the site URL and that the REST API is enabled.', 'emcp-tools' ),
'ajaxUrl' => admin_url( 'admin-ajax.php' ),
'createPwNonce' => wp_create_nonce( 'emcp_tools_create_app_password' ),
'trackPromptNonce' => wp_create_nonce( 'emcp_tools_track_prompt_copy' ),
'generating' => __( 'Generating…', 'emcp-tools' ),
'pwCreated' => __( 'Application password created — save it below, it is shown only once.', 'emcp-tools' ),
'pwCreated' => __( 'Application password created, save it below, it is shown only once.', 'emcp-tools' ),
'syncing' => __( 'Syncing…', 'emcp-tools' ),
// Brand Kits.
'applying' => __( 'Applying…', 'emcp-tools' ),
@@ -1330,7 +1330,7 @@ class EMCP_Tools_Admin {
'optimizing' => __( 'Optimizing…', 'emcp-tools' ),
'restoring' => __( 'Restoring…', 'emcp-tools' ),
'done' => __( 'Done', 'emcp-tools' ),
'unsaved' => __( 'Unsaved changes — click Save Modules to apply.', 'emcp-tools' ),
'unsaved' => __( 'Unsaved changes, click Save Modules to apply.', 'emcp-tools' ),
)
);
}
@@ -1362,12 +1362,12 @@ class EMCP_Tools_Admin {
}
echo '<div class="notice notice-error"><p><strong>EMCP Tools:</strong> ';
echo esc_html__( 'A required script is missing — assets/js/admin.js was not found in the plugin folder, so admin features like the Connection-tab config generator will not work.', 'emcp-tools' );
echo esc_html__( 'A required script is missing, assets/js/admin.js was not found in the plugin folder, so admin features like the Connection-tab config generator will not work.', 'emcp-tools' );
echo ' ';
if ( '' !== $mangled ) {
printf(
/* translators: %s: the mangled filename found, e.g. admin.j_ */
esc_html__( 'It looks like security software renamed it to assets/js/%s — rename that file back to admin.js.', 'emcp-tools' ),
esc_html__( 'It looks like security software renamed it to assets/js/%s, rename that file back to admin.js.', 'emcp-tools' ),
esc_html( $mangled )
);
} else {
@@ -1426,7 +1426,7 @@ class EMCP_Tools_Admin {
$app_name = sprintf(
/* translators: %s: current date and time */
__( 'EMCP Tools (MCP) — %s', 'emcp-tools' ),
__( 'EMCP Tools (MCP), %s', 'emcp-tools' ),
gmdate( 'Y-m-d H:i' )
);
@@ -1906,7 +1906,7 @@ class EMCP_Tools_Admin {
return array(
'dynamic' => array(
'label' => __( 'Dynamic Content', 'emcp-tools' ),
'desc' => __( 'Custom fields & metadata — read and write dynamic content.', 'emcp-tools' ),
'desc' => __( 'Custom fields & metadata, read and write dynamic content.', 'emcp-tools' ),
),
'ecommerce' => array(
'label' => __( 'E-Commerce', 'emcp-tools' ),
@@ -1987,7 +1987,7 @@ class EMCP_Tools_Admin {
array( 'title' => __( 'URL', 'emcp-tools' ), 'copy' => '%ENDPOINT%' ),
array(
'title' => __( 'c. Save, then Authenticate', 'emcp-tools' ),
'desc' => __( 'Click Save. An “Authenticate” button appears on the server row — click it, then “Approve” on the consent screen that opens. Your site is now connected and you can start chatting.', 'emcp-tools' ),
'desc' => __( 'Click Save. An “Authenticate” button appears on the server row, click it, then “Approve” on the consent screen that opens. Your site is now connected and you can start chatting.', 'emcp-tools' ),
),
),
);
@@ -2016,10 +2016,10 @@ class EMCP_Tools_Admin {
. '<tr><th>' . esc_html__( 'Name', 'emcp-tools' ) . '</th><td><code>%NAME%</code></td></tr>'
. '<tr><th>' . esc_html__( 'Transport', 'emcp-tools' ) . '</th><td>' . esc_html__( 'Streamable HTTP', 'emcp-tools' ) . '</td></tr>'
. '<tr><th>' . esc_html__( 'URL', 'emcp-tools' ) . '</th><td><code>%ENDPOINT%</code></td></tr>'
. '<tr><th>' . esc_html__( 'Bearer token env var', 'emcp-tools' ) . '</th><td>' . esc_html__( 'Leave blank — EMCP uses a WordPress Application Password (HTTP Basic), not a bearer token.', 'emcp-tools' ) . '</td></tr>'
. '<tr><th>' . esc_html__( 'Bearer token env var', 'emcp-tools' ) . '</th><td>' . esc_html__( 'Leave blank, EMCP uses a WordPress Application Password (HTTP Basic), not a bearer token.', 'emcp-tools' ) . '</td></tr>'
. '<tr><th>' . esc_html__( 'Headers', 'emcp-tools' ) . '</th><td>' . esc_html__( 'Key', 'emcp-tools' ) . ' <code>Authorization</code> &middot; ' . esc_html__( 'Value', 'emcp-tools' ) . ' <code>Basic %B64%</code></td></tr>'
. '</tbody></table>'
. '<p class="description">' . esc_html__( 'Then Save. The config blocks below do the same thing — “direct HTTP” for the URL + header approach, or the “Node proxy / npx” config if the HTTP transport gives you handshake trouble.', 'emcp-tools' ) . '</p>';
. '<p class="description">' . esc_html__( 'Then Save. The config blocks below do the same thing, “direct HTTP” for the URL + header approach, or the “Node proxy / npx” config if the HTTP transport gives you handshake trouble.', 'emcp-tools' ) . '</p>';
return array(
array(
@@ -2843,11 +2843,11 @@ class EMCP_Tools_Admin {
'platform' => 'plugins',
'group' => 'dynamic',
'label' => __( 'ACF (Advanced Custom Fields)', 'emcp-tools' ),
'note' => __( 'Plugin integrations are exposed as two tools — one Read, one Write. The AI calls a tool with an operation name; each tool bundles the operations listed on its card. Toggle a tool to allow or block all of its operations at once. Post-type & taxonomy operations need ACF 6.1+.', 'emcp-tools' ),
'note' => __( 'Plugin integrations are exposed as two tools, one Read, one Write. The AI calls a tool with an operation name; each tool bundles the operations listed on its card. Toggle a tool to allow or block all of its operations at once. Post-type & taxonomy operations need ACF 6.1+.', 'emcp-tools' ),
'tools' => array(
'emcp-tools/acf-read' => array(
'label' => __( 'ACF Read', 'emcp-tools' ),
'description' => __( 'Read Advanced Custom Fields data — field groups, field values, options pages, and (ACF 6.1+) ACF-managed post types and taxonomies.', 'emcp-tools' ),
'description' => __( 'Read Advanced Custom Fields data, field groups, field values, options pages, and (ACF 6.1+) ACF-managed post types and taxonomies.', 'emcp-tools' ),
'badges' => array( 'read-only' ),
'operations' => array(
'list-field-groups',
@@ -2862,7 +2862,7 @@ class EMCP_Tools_Admin {
),
'emcp-tools/acf-write' => array(
'label' => __( 'ACF Write', 'emcp-tools' ),
'description' => __( 'Write Advanced Custom Fields data — field values, field groups, and (ACF 6.1+) ACF-managed post types and taxonomies. No delete operations; slugs and field keys are immutable.', 'emcp-tools' ),
'description' => __( 'Write Advanced Custom Fields data, field values, field groups, and (ACF 6.1+) ACF-managed post types and taxonomies. No delete operations; slugs and field keys are immutable.', 'emcp-tools' ),
'badges' => array(),
'operations' => array(
'update-fields',
@@ -2881,7 +2881,7 @@ class EMCP_Tools_Admin {
'group' => 'ecommerce',
'pro' => true,
'label' => __( 'WooCommerce', 'emcp-tools' ),
'note' => __( 'WooCommerce is exposed as two tools — one Read, one Write — over the full wc/v3 API (~120 operations). The AI calls a tool with an operation name; toggle a tool to allow or block all of its operations at once. Money/irreversible operations (refunds, deletes, batch) additionally require confirm:true. Requires WooCommerce active.', 'emcp-tools' ),
'note' => __( 'WooCommerce is exposed as two tools, one Read, one Write, over the full wc/v3 API (~120 operations). The AI calls a tool with an operation name; toggle a tool to allow or block all of its operations at once. Money/irreversible operations (refunds, deletes, batch) additionally require confirm:true. Requires WooCommerce active.', 'emcp-tools' ),
'tools' => array(
'emcp-tools/woo-read' => array(
'label' => __( 'WooCommerce Read', 'emcp-tools' ),
@@ -2905,11 +2905,11 @@ class EMCP_Tools_Admin {
'platform' => 'plugins',
'group' => 'dynamic',
'label' => __( 'Meta Box', 'emcp-tools' ),
'note' => __( 'Plugin integrations are exposed as two tools — one Read, one Write. The AI calls a tool with an operation name; each tool bundles the operations listed on its card. Toggle a tool to allow or block all of its operations at once.', 'emcp-tools' ),
'note' => __( 'Plugin integrations are exposed as two tools, one Read, one Write. The AI calls a tool with an operation name; each tool bundles the operations listed on its card. Toggle a tool to allow or block all of its operations at once.', 'emcp-tools' ),
'tools' => array(
'emcp-tools/metabox-read' => array(
'label' => __( 'Meta Box Read', 'emcp-tools' ),
'description' => __( 'Read Meta Box data — field groups, field definitions, and field values for posts and other supported object types.', 'emcp-tools' ),
'description' => __( 'Read Meta Box data, field groups, field definitions, and field values for posts and other supported object types.', 'emcp-tools' ),
'badges' => array( 'read-only' ),
'operations' => array(
'list-field-groups',
@@ -2931,11 +2931,11 @@ class EMCP_Tools_Admin {
'platform' => 'plugins',
'group' => 'forms',
'label' => __( 'Contact Form 7', 'emcp-tools' ),
'note' => __( 'Contact Form 7 exposed as two tools — one Read, one Write. Reads list forms, fields, mail templates and messages; writes update mail, messages, and settings. CF7 stores no submissions, so there are no entry operations.', 'emcp-tools' ),
'note' => __( 'Contact Form 7 exposed as two tools, one Read, one Write. Reads list forms, fields, mail templates and messages; writes update mail, messages, and settings. CF7 stores no submissions, so there are no entry operations.', 'emcp-tools' ),
'tools' => array(
'emcp-tools/cf7-read' => array(
'label' => __( 'Contact Form 7 Read', 'emcp-tools' ),
'description' => __( 'Read CF7 forms — fields, mail templates, messages, and settings.', 'emcp-tools' ),
'description' => __( 'Read CF7 forms, fields, mail templates, messages, and settings.', 'emcp-tools' ),
'badges' => array( 'read-only' ),
'operations' => array( 'list-forms', 'get-form', 'list-notifications', 'get-settings' ),
'available' => self::cf7_available(),
@@ -2956,7 +2956,7 @@ class EMCP_Tools_Admin {
'group' => 'forms',
'pro' => true,
'label' => __( 'WPForms', 'emcp-tools' ),
'note' => __( 'WPForms exposed as two tools — one Read, one Write. Reads cover forms, fields, notifications, and entries (entries require WPForms Pro); writes update settings/notifications and manage entries. Requires WPForms active.', 'emcp-tools' ),
'note' => __( 'WPForms exposed as two tools, one Read, one Write. Reads cover forms, fields, notifications, and entries (entries require WPForms Pro); writes update settings/notifications and manage entries. Requires WPForms active.', 'emcp-tools' ),
'tools' => array(
'emcp-tools/wpforms-read' => array(
'label' => __( 'WPForms Read', 'emcp-tools' ),
@@ -2981,7 +2981,7 @@ class EMCP_Tools_Admin {
'group' => 'forms',
'pro' => true,
'label' => __( 'Gravity Forms', 'emcp-tools' ),
'note' => __( 'Gravity Forms exposed as two tools — one Read, one Write — over the GFAPI. Reads cover forms, fields, notifications, and entries; writes set entry status and delete entries. Requires Gravity Forms active.', 'emcp-tools' ),
'note' => __( 'Gravity Forms exposed as two tools, one Read, one Write, over the GFAPI. Reads cover forms, fields, notifications, and entries; writes set entry status and delete entries. Requires Gravity Forms active.', 'emcp-tools' ),
'tools' => array(
'emcp-tools/gravityforms-read' => array(
'label' => __( 'Gravity Forms Read', 'emcp-tools' ),
@@ -3006,7 +3006,7 @@ class EMCP_Tools_Admin {
'group' => 'forms',
'pro' => true,
'label' => __( 'Fluent Forms', 'emcp-tools' ),
'note' => __( 'Fluent Forms exposed as two tools — one Read, one Write. Reads cover forms, fields, and submissions; writes set submission status and delete submissions. Requires Fluent Forms active.', 'emcp-tools' ),
'note' => __( 'Fluent Forms exposed as two tools, one Read, one Write. Reads cover forms, fields, and submissions; writes set submission status and delete submissions. Requires Fluent Forms active.', 'emcp-tools' ),
'tools' => array(
'emcp-tools/fluentforms-read' => array(
'label' => __( 'Fluent Forms Read', 'emcp-tools' ),
@@ -3031,7 +3031,7 @@ class EMCP_Tools_Admin {
'group' => 'forms',
'pro' => true,
'label' => __( 'Ninja Forms', 'emcp-tools' ),
'note' => __( 'Ninja Forms exposed as two tools — one Read, one Write. Reads cover forms, fields, and submissions; writes delete submissions. Requires Ninja Forms active.', 'emcp-tools' ),
'note' => __( 'Ninja Forms exposed as two tools, one Read, one Write. Reads cover forms, fields, and submissions; writes delete submissions. Requires Ninja Forms active.', 'emcp-tools' ),
'tools' => array(
'emcp-tools/ninjaforms-read' => array(
'label' => __( 'Ninja Forms Read', 'emcp-tools' ),
@@ -3056,7 +3056,7 @@ class EMCP_Tools_Admin {
'group' => 'forms',
'pro' => true,
'label' => __( 'Formidable Forms', 'emcp-tools' ),
'note' => __( 'Formidable Forms exposed as two tools — one Read, one Write. Reads cover forms, fields, notifications, and entries; writes update notifications and delete entries. Requires Formidable Forms active.', 'emcp-tools' ),
'note' => __( 'Formidable Forms exposed as two tools, one Read, one Write. Reads cover forms, fields, notifications, and entries; writes update notifications and delete entries. Requires Formidable Forms active.', 'emcp-tools' ),
'tools' => array(
'emcp-tools/formidable-read' => array(
'label' => __( 'Formidable Forms Read', 'emcp-tools' ),
@@ -3081,7 +3081,7 @@ class EMCP_Tools_Admin {
'group' => 'forms',
'pro' => true,
'label' => __( 'MetForm', 'emcp-tools' ),
'note' => __( 'MetForm exposed as two tools — one Read, one Write. Reads cover forms, fields, and entries; writes delete entries. Requires MetForm (and Elementor) active.', 'emcp-tools' ),
'note' => __( 'MetForm exposed as two tools, one Read, one Write. Reads cover forms, fields, and entries; writes delete entries. Requires MetForm (and Elementor) active.', 'emcp-tools' ),
'tools' => array(
'emcp-tools/metform-read' => array(
'label' => __( 'MetForm Read', 'emcp-tools' ),
@@ -3106,7 +3106,7 @@ class EMCP_Tools_Admin {
'group' => 'forms',
'pro' => true,
'label' => __( 'SureForms', 'emcp-tools' ),
'note' => __( 'SureForms exposed as two tools — one Read, one Write. Reads cover forms, fields, and entries; writes set entry status and delete entries. Requires SureForms active.', 'emcp-tools' ),
'note' => __( 'SureForms exposed as two tools, one Read, one Write. Reads cover forms, fields, and entries; writes set entry status and delete entries. Requires SureForms active.', 'emcp-tools' ),
'tools' => array(
'emcp-tools/sureforms-read' => array(
'label' => __( 'SureForms Read', 'emcp-tools' ),
@@ -3130,7 +3130,7 @@ class EMCP_Tools_Admin {
'platform' => 'plugins',
'group' => 'seo',
'label' => __( 'Slim SEO', 'emcp-tools' ),
'note' => __( 'Slim SEO exposed as two tools — one Read, one Write. Read and write the SEO metadata (title, description, canonical, robots, social) Slim SEO stores for posts and terms, plus its site settings.', 'emcp-tools' ),
'note' => __( 'Slim SEO exposed as two tools, one Read, one Write. Read and write the SEO metadata (title, description, canonical, robots, social) Slim SEO stores for posts and terms, plus its site settings.', 'emcp-tools' ),
'tools' => array(
'emcp-tools/slimseo-read' => array(
'label' => __( 'Slim SEO Read', 'emcp-tools' ),
@@ -3155,7 +3155,7 @@ class EMCP_Tools_Admin {
'group' => 'seo',
'pro' => true,
'label' => __( 'Yoast SEO', 'emcp-tools' ),
'note' => __( 'Yoast SEO exposed as two tools — one Read, one Write. Read and write the SEO metadata (title, description, canonical, robots, social, focus keyword) Yoast stores for posts and terms, plus site settings.', 'emcp-tools' ),
'note' => __( 'Yoast SEO exposed as two tools, one Read, one Write. Read and write the SEO metadata (title, description, canonical, robots, social, focus keyword) Yoast stores for posts and terms, plus site settings.', 'emcp-tools' ),
'tools' => array(
'emcp-tools/yoast-read' => array(
'label' => __( 'Yoast SEO Read', 'emcp-tools' ),
@@ -3180,7 +3180,7 @@ class EMCP_Tools_Admin {
'group' => 'seo',
'pro' => true,
'label' => __( 'Rank Math', 'emcp-tools' ),
'note' => __( 'Rank Math exposed as two tools — one Read, one Write. Read/write post & term SEO metadata and site settings; also read schema (structured data).', 'emcp-tools' ),
'note' => __( 'Rank Math exposed as two tools, one Read, one Write. Read/write post & term SEO metadata and site settings; also read schema (structured data).', 'emcp-tools' ),
'tools' => array(
'emcp-tools/rankmath-read' => array(
'label' => __( 'Rank Math Read', 'emcp-tools' ),
@@ -3205,7 +3205,7 @@ class EMCP_Tools_Admin {
'group' => 'seo',
'pro' => true,
'label' => __( 'All in One SEO', 'emcp-tools' ),
'note' => __( 'All in One SEO exposed as two tools — one Read, one Write. Read/write post SEO metadata and read schema (structured data) + site settings.', 'emcp-tools' ),
'note' => __( 'All in One SEO exposed as two tools, one Read, one Write. Read/write post SEO metadata and read schema (structured data) + site settings.', 'emcp-tools' ),
'tools' => array(
'emcp-tools/aioseo-read' => array(
'label' => __( 'All in One SEO Read', 'emcp-tools' ),
@@ -3230,7 +3230,7 @@ class EMCP_Tools_Admin {
'group' => 'seo',
'pro' => true,
'label' => __( 'SEOPress', 'emcp-tools' ),
'note' => __( 'SEOPress exposed as two tools — one Read, one Write. Read/write post & term SEO metadata and site settings; also read schema (structured data).', 'emcp-tools' ),
'note' => __( 'SEOPress exposed as two tools, one Read, one Write. Read/write post & term SEO metadata and site settings; also read schema (structured data).', 'emcp-tools' ),
'tools' => array(
'emcp-tools/seopress-read' => array(
'label' => __( 'SEOPress Read', 'emcp-tools' ),
@@ -3255,7 +3255,7 @@ class EMCP_Tools_Admin {
'group' => 'seo',
'pro' => true,
'label' => __( 'The SEO Framework', 'emcp-tools' ),
'note' => __( 'The SEO Framework exposed as two tools — one Read, one Write. Read and write the SEO metadata (title, description, canonical, robots, social) it stores for posts and terms.', 'emcp-tools' ),
'note' => __( 'The SEO Framework exposed as two tools, one Read, one Write. Read and write the SEO metadata (title, description, canonical, robots, social) it stores for posts and terms.', 'emcp-tools' ),
'tools' => array(
'emcp-tools/seoframework-read' => array(
'label' => __( 'The SEO Framework Read', 'emcp-tools' ),
@@ -3280,7 +3280,7 @@ class EMCP_Tools_Admin {
'group' => 'seo',
'pro' => true,
'label' => __( 'SureRank', 'emcp-tools' ),
'note' => __( 'SureRank exposed as two tools — one Read, one Write. Read and write the SEO metadata (title, description, canonical, robots, social) SureRank stores for posts and terms.', 'emcp-tools' ),
'note' => __( 'SureRank exposed as two tools, one Read, one Write. Read and write the SEO metadata (title, description, canonical, robots, social) SureRank stores for posts and terms.', 'emcp-tools' ),
'tools' => array(
'emcp-tools/surerank-read' => array(
'label' => __( 'SureRank Read', 'emcp-tools' ),
@@ -3303,7 +3303,7 @@ class EMCP_Tools_Admin {
'theme_active' => array(
'platform' => 'themes',
'label' => __( 'Active Theme', 'emcp-tools' ),
'note' => __( 'Theme integrations are exposed as two tools — one Read, one Write — that bundle internal operations. The AI calls a tool with an operation name; toggle a tool to allow or block all of its operations at once.', 'emcp-tools' ),
'note' => __( 'Theme integrations are exposed as two tools, one Read, one Write, that bundle internal operations. The AI calls a tool with an operation name; toggle a tool to allow or block all of its operations at once.', 'emcp-tools' ),
'tools' => array(
'emcp-tools/theme-read' => array(
'label' => __( 'Theme Read', 'emcp-tools' ),
@@ -3576,12 +3576,12 @@ class EMCP_Tools_Admin {
),
'emcp-tools/search-images' => array(
'label' => __( 'Search Images', 'emcp-tools' ),
'description' => __( 'Searches a stock-photo provider (Unsplash, Pexels, or Pixabay) for images. Core WordPress tool — available without Elementor. Needs a free provider API key (Connection tab).', 'emcp-tools' ),
'description' => __( 'Searches a stock-photo provider (Unsplash, Pexels, or Pixabay) for images. Core WordPress tool, available without Elementor. Needs a free provider API key (Connection tab).', 'emcp-tools' ),
'badges' => array( 'read-only' ),
),
'emcp-tools/sideload-image' => array(
'label' => __( 'Sideload Image', 'emcp-tools' ),
'description' => __( 'Downloads an external image URL into the WordPress Media Library. Core WordPress tool — available without Elementor.', 'emcp-tools' ),
'description' => __( 'Downloads an external image URL into the WordPress Media Library. Core WordPress tool, available without Elementor.', 'emcp-tools' ),
'badges' => array(),
),
'emcp-tools/add-stock-image' => array(
+1 -1
View File
@@ -67,7 +67,7 @@ class EMCP_Tools_Elementor_Notice {
printf(
'<div class="notice notice-warning is-dismissible" data-emcp-elementor-notice="1" data-emcp-nonce="%s"><p>%s</p><p><a class="button button-secondary" href="%s">%s</a></p></div>',
esc_attr( $nonce ),
esc_html__( 'EMCP Tools is active. Install and activate Elementor to enable the Elementor page-building tools (widgets, layout, templates, brand kits). All other tools — WordPress content, plugins & themes, users, media, performance, security, filesystem, and database — work without it.', 'emcp-tools' ),
esc_html__( 'EMCP Tools is active. Install and activate Elementor to enable the Elementor page-building tools (widgets, layout, templates, brand kits). All other tools, WordPress content, plugins & themes, users, media, performance, security, filesystem, and database, work without it.', 'emcp-tools' ),
esc_url( $install ),
esc_html__( 'Install Elementor', 'emcp-tools' )
);
+2 -2
View File
@@ -48,7 +48,7 @@ class EMCP_Tools_Mcpb_Builder {
// manifest `name`, so a fixed name makes each install silently
// overwrite the previous site's bundle. See #86.
'name' => '' !== $slug ? 'emcp-tools-' . $slug : 'emcp-tools',
'display_name' => sprintf( 'EMCP Tools — %s', $host ),
'display_name' => sprintf( 'EMCP Tools - %s', $host ),
'version' => $version,
'description' => sprintf( 'Connect Claude Desktop to %s for Elementor and WordPress management via MCP.', $host ),
'author' => array( 'name' => 'MSR Builds' ),
@@ -182,7 +182,7 @@ class EMCP_Tools_Mcpb_Builder {
// injects the mcp_config.env values.
$lines = array(
"'use strict';",
'// EMCP Tools — self-contained MCP proxy (credentials embedded).',
'// EMCP Tools, self-contained MCP proxy (credentials embedded).',
'// Credentials are set here so this works even when the host does',
'// not inject mcp_config.env into the process environment.',
);
+1 -1
View File
@@ -257,7 +257,7 @@ class EMCP_Tools_Upgrade_Notice {
<div class="emcp-upgrade-banner__lede">
<span class="emcp-upgrade-banner__eyebrow"><?php esc_html_e( 'EMCP Tools Pro', 'emcp-tools' ); ?></span>
<h2 class="emcp-upgrade-banner__title">
<?php esc_html_e( 'Branded landing pages in minutes — straight from your AI.', 'emcp-tools' ); ?>
<?php esc_html_e( 'Branded landing pages in minutes, straight from your AI.', 'emcp-tools' ); ?>
</h2>
<div class="emcp-upgrade-banner__actions">
<a class="emcp-upgrade-banner__btn emcp-upgrade-banner__btn--primary" href="<?php echo esc_url( $upgrade_url ); ?>" target="_blank" rel="noopener noreferrer">
+1 -1
View File
@@ -15,7 +15,7 @@ $emcp_tools_upgrade_url = function_exists( 'emcp_tools_upgrade_url' ) ? emcp_too
<div class="elementor-mcp-prompts-cta">
<div class="elementor-mcp-prompts-cta-content">
<h3><?php esc_html_e( 'Unlock AI Chat', 'emcp-tools' ); ?></h3>
<p><?php esc_html_e( 'Chat with any AI model right inside WordPress and the Elementor editor to build pages, add sections, and edit widgets — powered by the EMCP tools. Bring your own key (Claude, OpenAI, Gemini, or OpenRouter) and let the assistant do the work.', 'emcp-tools' ); ?></p>
<p><?php esc_html_e( 'Chat with any AI model right inside WordPress and the Elementor editor to build pages, add sections, and edit widgets, powered by the EMCP tools. Bring your own key (Claude, OpenAI, Gemini, or OpenRouter) and let the assistant do the work.', 'emcp-tools' ); ?></p>
<a href="<?php echo esc_url( $emcp_tools_upgrade_url ); ?>" class="button button-primary elementor-mcp-prompts-cta-btn" target="_blank" rel="noopener noreferrer">
<svg viewBox="0 0 20 20" width="16" height="16" xmlns="http://www.w3.org/2000/svg"><path d="M9.049 2.927c.3-.921 1.603-.921 1.902 0l1.07 3.292a1 1 0 00.95.69h3.462c.969 0 1.371 1.24.588 1.81l-2.8 2.034a1 1 0 00-.364 1.118l1.07 3.292c.3.921-.755 1.688-1.54 1.118l-2.8-2.034a1 1 0 00-1.175 0l-2.8 2.034c-.784.57-1.838-.197-1.539-1.118l1.07-3.292a1 1 0 00-.364-1.118L2.98 8.72c-.783-.57-.38-1.81.588-1.81h3.461a1 1 0 00.951-.69l1.07-3.292z"/></svg>
<?php esc_html_e( 'Upgrade to Pro', 'emcp-tools' ); ?>
+2 -2
View File
@@ -93,7 +93,7 @@ foreach ( $emcp_tools_render['categories'] as $emcp_tools_bk_cat ) {
<?php
printf(
/* translators: %d: number of free brand kits */
esc_html__( '%d coordinated color + typography kits, free to apply. One click replaces your site\'s global palette and fonts — back up first and restore any time.', 'emcp-tools' ),
esc_html__( '%d coordinated color + typography kits, free to apply. One click replaces your site\'s global palette and fonts, back up first and restore any time.', 'emcp-tools' ),
(int) $emcp_tools_bk_total
);
?>
@@ -149,7 +149,7 @@ foreach ( $emcp_tools_render['categories'] as $emcp_tools_bk_cat ) {
</span>
<div class="elementor-mcp-coming-soon__text">
<strong><?php esc_html_e( 'Unlock 40+ more brand kits with Pro.', 'emcp-tools' ); ?></strong>
<?php esc_html_e( 'The full library spans corporate, creative, e-commerce, editorial, hospitality, trades, and wellness — plus MCP tools so AI agents can re-skin sites for you.', 'emcp-tools' ); ?>
<?php esc_html_e( 'The full library spans corporate, creative, e-commerce, editorial, hospitality, trades, and wellness, plus MCP tools so AI agents can re-skin sites for you.', 'emcp-tools' ); ?>
<a href="<?php echo esc_url( $emcp_tools_upgrade_url ); ?>" target="_blank" rel="noopener noreferrer"><?php esc_html_e( 'Upgrade to Pro →', 'emcp-tools' ); ?></a>
</div>
</div>
+15 -15
View File
@@ -149,7 +149,7 @@ $emcp_tools_server_enabled = class_exists( 'EMCP_Tools_Plugin' )
<p class="elementor-mcp-activate-note elementor-mcp-activate-note--security">
<strong><?php esc_html_e( 'Security note:', 'emcp-tools' ); ?></strong>
<?php esc_html_e( 'When enabled, connected AI agents can create, edit, and delete Elementor pages and content on this site through the MCP server. Use a capable AI model and set your client to ask for confirmation before every action — read what the agent is about to do before approving.', 'emcp-tools' ); ?>
<?php esc_html_e( 'When enabled, connected AI agents can create, edit, and delete Elementor pages and content on this site through the MCP server. Use a capable AI model and set your client to ask for confirmation before every action, read what the agent is about to do before approving.', 'emcp-tools' ); ?>
</p>
<p class="elementor-mcp-activate-note">
<?php
@@ -162,7 +162,7 @@ $emcp_tools_server_enabled = class_exists( 'EMCP_Tools_Plugin' )
: ( 'external' === $emcp_tools_adapter_source ? esc_html__( 'provided by an active MCP Adapter plugin', 'emcp-tools' ) : esc_html__( 'unavailable', 'emcp-tools' ) )
);
} else {
esc_html_e( 'WordPress Abilities API is unavailable — WordPress 6.9 or newer is required.', 'emcp-tools' );
esc_html_e( 'WordPress Abilities API is unavailable, WordPress 6.9 or newer is required.', 'emcp-tools' );
}
?>
</p>
@@ -184,7 +184,7 @@ $emcp_tools_server_enabled = class_exists( 'EMCP_Tools_Plugin' )
</label>
<p class="elementor-mcp-activate-note">
<?php esc_html_e( 'Enable only for OpenAI-compatible strict function-calling clients (e.g. CrewAI) that reject the default tool schemas. It lists every property as required (optional ones become nullable) and sets additionalProperties:false. Leave this OFF for Claude, Gemini, and Antigravity — they work with the default schemas, and strict mode can break Gemini/Antigravity.', 'emcp-tools' ); ?>
<?php esc_html_e( 'Enable only for OpenAI-compatible strict function-calling clients (e.g. CrewAI) that reject the default tool schemas. It lists every property as required (optional ones become nullable) and sets additionalProperties:false. Leave this OFF for Claude, Gemini, and Antigravity, they work with the default schemas, and strict mode can break Gemini/Antigravity.', 'emcp-tools' ); ?>
</p>
<p class="elementor-mcp-activate-note">
<?php
@@ -211,12 +211,12 @@ $emcp_tools_server_enabled = class_exists( 'EMCP_Tools_Plugin' )
<?php disabled( ! $emcp_oauth_available ); ?>
/>
<span class="elementor-mcp-toggle" aria-hidden="true"><span class="elementor-mcp-toggle-track"></span></span>
<span class="emcp-switch-label"><?php esc_html_e( 'Let clients connect by signing in (OAuth) — no password to copy', 'emcp-tools' ); ?></span>
<span class="emcp-switch-label"><?php esc_html_e( 'Let clients connect by signing in (OAuth), no password to copy', 'emcp-tools' ); ?></span>
</label>
<?php if ( ! $emcp_oauth_available ) : ?>
<p class="elementor-mcp-activate-note elementor-mcp-activate-note--security">
<?php esc_html_e( 'OAuth requires HTTPS. This site is not served over HTTPS, so OAuth sign-in is unavailable — use an Application Password below.', 'emcp-tools' ); ?>
<?php esc_html_e( 'OAuth requires HTTPS. This site is not served over HTTPS, so OAuth sign-in is unavailable, use an Application Password below.', 'emcp-tools' ); ?>
</p>
<?php endif; ?>
<p class="elementor-mcp-activate-note">
@@ -304,7 +304,7 @@ $emcp_tools_server_enabled = class_exists( 'EMCP_Tools_Plugin' )
<div data-authfor="app-password">
<h3><?php esc_html_e( 'Step 1: Generate Your Credentials', 'emcp-tools' ); ?></h3>
<p class="description">
<?php esc_html_e( 'Pick an administrator and click Generate — a new Application Password is created automatically and every client config below is filled in. No need to visit your profile.', 'emcp-tools' ); ?>
<?php esc_html_e( 'Pick an administrator and click Generate, a new Application Password is created automatically and every client config below is filled in. No need to visit your profile.', 'emcp-tools' ); ?>
</p>
<?php
@@ -368,7 +368,7 @@ $emcp_tools_server_enabled = class_exists( 'EMCP_Tools_Plugin' )
<div id="elementor-mcp-generated-pw-row" style="display: none;">
<div class="elementor-mcp-cred-field">
<label for="elementor-mcp-generated-pw-copy"><?php esc_html_e( 'New Application Password (save it — shown only once)', 'emcp-tools' ); ?></label>
<label for="elementor-mcp-generated-pw-copy"><?php esc_html_e( 'New Application Password (save it, shown only once)', 'emcp-tools' ); ?></label>
<div class="elementor-mcp-auth-result">
<code id="elementor-mcp-generated-pw"></code>
<button type="button" class="button elementor-mcp-copy-btn" data-target="elementor-mcp-generated-pw-copy"><?php esc_html_e( 'Copy', 'emcp-tools' ); ?></button>
@@ -414,7 +414,7 @@ $emcp_tools_server_enabled = class_exists( 'EMCP_Tools_Plugin' )
<p><strong><?php esc_html_e( 'Got 401 Unauthorized? Your server is most likely stripping the Authorization header.', 'emcp-tools' ); ?></strong></p>
<p class="description"><?php esc_html_e( 'Common on Apache, Plesk, LiteSpeed and some Azure/IIS stacks: the Authorization header never reaches PHP, so WordPress never sees the Application Password and every MCP "initialize" fails with Unauthorized. Pass the header through to PHP, then re-test:', 'emcp-tools' ); ?></p>
<p class="description" style="margin-bottom: 4px;"><strong><?php esc_html_e( 'Apache / Plesk / LiteSpeed', 'emcp-tools' ); ?></strong> — <?php esc_html_e( 'add to .htaccess, above the # BEGIN WordPress block:', 'emcp-tools' ); ?></p>
<p class="description" style="margin-bottom: 4px;"><strong><?php esc_html_e( 'Apache / Plesk / LiteSpeed', 'emcp-tools' ); ?></strong>: <?php esc_html_e( 'add to .htaccess, above the # BEGIN WordPress block:', 'emcp-tools' ); ?></p>
<pre class="elementor-mcp-authtest-snippet">&lt;IfModule mod_rewrite.c&gt;
RewriteEngine On
RewriteCond %{HTTP:Authorization} ^(.*)
@@ -422,14 +422,14 @@ RewriteRule .* - [E=HTTP_AUTHORIZATION:%{HTTP:Authorization}]
&lt;/IfModule&gt;
SetEnvIf Authorization "(.*)" HTTP_AUTHORIZATION=$1</pre>
<p class="description" style="margin-bottom: 4px;"><strong><?php esc_html_e( 'Nginx', 'emcp-tools' ); ?></strong> — <?php esc_html_e( 'add inside the PHP location block, then reload nginx:', 'emcp-tools' ); ?></p>
<p class="description" style="margin-bottom: 4px;"><strong><?php esc_html_e( 'Nginx', 'emcp-tools' ); ?></strong>: <?php esc_html_e( 'add inside the PHP location block, then reload nginx:', 'emcp-tools' ); ?></p>
<pre class="elementor-mcp-authtest-snippet">fastcgi_param HTTP_AUTHORIZATION $http_authorization;</pre>
<p class="description">
<?php
printf(
/* translators: %s: command-line example */
esc_html__( 'You can also confirm from a terminal: %s — a 200 response means auth works; 401 means the header is being stripped.', 'emcp-tools' ),
esc_html__( 'You can also confirm from a terminal: %s, a 200 response means auth works; 401 means the header is being stripped.', 'emcp-tools' ),
'<code>curl -u "USER:APP PASSWORD" ' . esc_html( esc_url_raw( rest_url( 'wp/v2/users/me' ) ) ) . '</code>'
);
?>
@@ -441,7 +441,7 @@ SetEnvIf Authorization "(.*)" HTTP_AUTHORIZATION=$1</pre>
<div id="elementor-mcp-client-picker">
<h3><?php esc_html_e( 'Connect Your AI Client', 'emcp-tools' ); ?></h3>
<p class="description"><?php esc_html_e( 'Pick the app you will connect from — the setup steps below are tailored to it.', 'emcp-tools' ); ?></p>
<p class="description"><?php esc_html_e( 'Pick the app you will connect from, the setup steps below are tailored to it.', 'emcp-tools' ); ?></p>
<div class="elementor-mcp-client-grid" role="tablist" aria-label="<?php esc_attr_e( 'AI client', 'emcp-tools' ); ?>">
<?php foreach ( EMCP_Tools_Admin::connection_clients() as $emcp_tools_client ) : ?>
@@ -495,7 +495,7 @@ SetEnvIf Authorization "(.*)" HTTP_AUTHORIZATION=$1</pre>
<div class="elementor-mcp-tabpanel" id="emcp-conn-services" role="tabpanel" data-tab="conn-services">
<div class="elementor-mcp-section">
<h2><?php esc_html_e( '3rd Party Services', 'emcp-tools' ); ?></h2>
<p class="description"><?php esc_html_e( 'Connect external services EMCP tools can use. Stock-image providers power the search-images / add-stock-image tools — add at least one free key; the tools use the first connected provider unless a specific one is requested.', 'emcp-tools' ); ?></p>
<p class="description"><?php esc_html_e( 'Connect external services EMCP tools can use. Stock-image providers power the search-images / add-stock-image tools, add at least one free key; the tools use the first connected provider unless a specific one is requested.', 'emcp-tools' ); ?></p>
<form method="post" action="options.php" id="emcp-conn-services-form" class="emcp-services-form">
<?php settings_fields( EMCP_Tools_Admin::SETTINGS_GROUP_SERVICES ); ?>
@@ -516,7 +516,7 @@ SetEnvIf Authorization "(.*)" HTTP_AUTHORIZATION=$1</pre>
/* translators: %s: PHP constant name. */
$emcp_sp_placeholder = sprintf( __( 'Set via the %s constant', 'emcp-tools' ), $emcp_sp['const'] );
} elseif ( $emcp_sp_saved ) {
$emcp_sp_placeholder = __( '•••••••••••••• saved — leave blank to keep', 'emcp-tools' );
$emcp_sp_placeholder = __( '•••••••••••••• saved, leave blank to keep', 'emcp-tools' );
} else {
$emcp_sp_placeholder = __( 'Paste your API key', 'emcp-tools' );
}
@@ -568,11 +568,11 @@ SetEnvIf Authorization "(.*)" HTTP_AUTHORIZATION=$1</pre>
id="emcp-tools-wpcli-command"
name="emcp_tools_wpcli_command"
value="<?php echo esc_attr( $emcp_wpcli_const ? '' : (string) get_option( 'emcp_tools_wpcli_command', '' ) ); ?>"
placeholder="<?php echo esc_attr( $emcp_wpcli_const ? sprintf( __( 'Set via the %s constant', 'emcp-tools' ), 'EMCP_TOOLS_WPCLI_COMMAND' ) : 'wp — or php /path/to/wp-cli.phar' ); ?>"
placeholder="<?php echo esc_attr( $emcp_wpcli_const ? sprintf( __( 'Set via the %s constant', 'emcp-tools' ), 'EMCP_TOOLS_WPCLI_COMMAND' ) : 'wp, or php /path/to/wp-cli.phar' ); ?>"
autocomplete="off" spellcheck="false"
<?php disabled( $emcp_wpcli_const ); ?>
/>
<p class="description"><?php esc_html_e( 'The wp launcher used by the WP-CLI tools over HTTP and for background jobs (e.g. "wp", or "php /path/to/wp-cli.phar"). Leave blank if you connect only over the WP-CLI stdio transport — commands then run in-process, no binary needed.', 'emcp-tools' ); ?></p>
<p class="description"><?php esc_html_e( 'The wp launcher used by the WP-CLI tools over HTTP and for background jobs (e.g. "wp", or "php /path/to/wp-cli.phar"). Leave blank if you connect only over the WP-CLI stdio transport, commands then run in-process, no binary needed.', 'emcp-tools' ); ?></p>
</div>
</form>
</div>
+1 -1
View File
@@ -21,7 +21,7 @@ $emcp_tools_ctx_final = EMCP_Tools_Site_Context::compose_instructions( $emcp_too
<div class="elementor-mcp-section">
<h2><?php esc_html_e( 'Site Context', 'emcp-tools' ); ?></h2>
<p class="description">
<?php esc_html_e( 'Stable, site-wide guidance that every AI agent connecting to this site receives automatically and applies to all of its work here — your business identity, brand voice, content rules, technical constraints, and guardrails. It is delivered as the MCP server\'s instructions at connection; changes take effect the next time an agent connects.', 'emcp-tools' ); ?>
<?php esc_html_e( 'Stable, site-wide guidance that every AI agent connecting to this site receives automatically and applies to all of its work here, your business identity, brand voice, content rules, technical constraints, and guardrails. It is delivered as the MCP server\'s instructions at connection; changes take effect the next time an agent connects.', 'emcp-tools' ); ?>
</p>
<form method="post" action="options.php" class="elementor-mcp-context-form">
+8 -8
View File
@@ -43,14 +43,14 @@ $emcp_features = array(
array(
'icon' => 'dashicons-admin-tools',
'title' => __( 'MCP Tools', 'emcp-tools' ),
'desc' => __( 'Toggle the ~140 abilities your AI client can call — Elementor, WordPress core, and Gutenberg.', 'emcp-tools' ),
'desc' => __( 'Toggle the ~140 abilities your AI client can call, Elementor, WordPress core, and Gutenberg.', 'emcp-tools' ),
'href' => admin_url( 'admin.php?page=' . $emcp_page . '-tools' ),
'show' => true,
),
array(
'icon' => 'dashicons-admin-links',
'title' => __( 'Connection', 'emcp-tools' ),
'desc' => __( 'Connect Claude, Cursor, the ChatGPT App and more — copy-paste configs, app passwords, and a one-click bundle.', 'emcp-tools' ),
'desc' => __( 'Connect Claude, Cursor, the ChatGPT App and more, copy-paste configs, app passwords, and a one-click bundle.', 'emcp-tools' ),
'href' => admin_url( 'admin.php?page=' . $emcp_page . '-connection' ),
'show' => true,
),
@@ -64,7 +64,7 @@ $emcp_features = array(
array(
'icon' => 'dashicons-undo',
'title' => __( 'History', 'emcp-tools' ),
'desc' => __( 'Review every change your AI made and roll any of them back — a unified change ledger with one-click undo.', 'emcp-tools' ),
'desc' => __( 'Review every change your AI made and roll any of them back, a unified change ledger with one-click undo.', 'emcp-tools' ),
'href' => admin_url( 'admin.php?page=' . $emcp_page . '-history' ),
'show' => true,
),
@@ -79,7 +79,7 @@ $emcp_features = array(
array(
'icon' => 'dashicons-layout',
'title' => __( 'EMCP Themer', 'emcp-tools' ),
'desc' => __( 'Build headers, footers, and dynamic layouts with any page builder — assigned by display conditions.', 'emcp-tools' ),
'desc' => __( 'Build headers, footers, and dynamic layouts with any page builder, assigned by display conditions.', 'emcp-tools' ),
'href' => admin_url( 'edit.php?post_type=emcp_theme_template' ),
'show' => class_exists( 'EMCP_Tools_Themer_Module' ) && EMCP_Tools_Themer_Module::is_enabled(),
),
@@ -116,7 +116,7 @@ $emcp_features = array(
array(
'icon' => 'dashicons-editor-code',
'title' => __( 'PHP Sandbox', 'emcp-tools' ),
'desc' => __( 'Review and activate AI-authored PHP snippets behind a human approval gate — nothing runs unattended.', 'emcp-tools' ),
'desc' => __( 'Review and activate AI-authored PHP snippets behind a human approval gate, nothing runs unattended.', 'emcp-tools' ),
'href' => admin_url( 'admin.php?page=' . $emcp_page . '-widgets' ),
'show' => true,
),
@@ -325,7 +325,7 @@ $emcp_videos = array(
<section class="emcp-dash-section" aria-labelledby="emcp-dash-features-h">
<div class="emcp-dash-section-head">
<h2 id="emcp-dash-features-h" class="emcp-dash-section-title"><?php esc_html_e( 'Explore your toolkit', 'emcp-tools' ); ?></h2>
<p class="emcp-dash-section-sub"><?php esc_html_e( 'Everything this plugin can do — jump straight in.', 'emcp-tools' ); ?></p>
<p class="emcp-dash-section-sub"><?php esc_html_e( 'Everything this plugin can do, jump straight in.', 'emcp-tools' ); ?></p>
</div>
<div class="emcp-dash-grid">
<?php
@@ -359,7 +359,7 @@ $emcp_videos = array(
<section class="emcp-dash-section emcp-dash-section--videos" aria-labelledby="emcp-dash-videos-h">
<div class="emcp-dash-section-head">
<h2 id="emcp-dash-videos-h" class="emcp-dash-section-title"><?php esc_html_e( 'Featured video guides', 'emcp-tools' ); ?></h2>
<p class="emcp-dash-section-sub"><?php esc_html_e( 'Watch and learn — from first connection to full-page builds.', 'emcp-tools' ); ?></p>
<p class="emcp-dash-section-sub"><?php esc_html_e( 'Watch and learn, from first connection to full-page builds.', 'emcp-tools' ); ?></p>
</div>
<div class="emcp-dash-videos">
<?php
@@ -408,7 +408,7 @@ $emcp_videos = array(
<?php
printf(
/* translators: 1: installed version, 2: available version */
esc_html__( 'You have v%1$s — v%2$s is ready to install.', 'emcp-tools' ),
esc_html__( 'You have v%1$s, v%2$s is ready to install.', 'emcp-tools' ),
esc_html( $emcp_ver['current'] ),
esc_html( $emcp_ver['latest'] )
);
+4 -4
View File
@@ -99,7 +99,7 @@ $emcp_cleared = isset( $_GET['cleared'] ) ? absint( wp_unslash( $_GET['cleared']
<?php if ( empty( $emcp_entries ) ) : ?>
<div class="emcp-history__empty">
<span class="dashicons dashicons-undo" aria-hidden="true"></span>
<p><?php esc_html_e( 'No changes recorded yet. As soon as a connected AI edits a page, writes a file, or changes the database, it will appear here — ready to roll back.', 'emcp-tools' ); ?></p>
<p><?php esc_html_e( 'No changes recorded yet. As soon as a connected AI edits a page, writes a file, or changes the database, it will appear here, ready to roll back.', 'emcp-tools' ); ?></p>
</div>
<?php else : ?>
<table class="widefat striped emcp-history__table">
@@ -123,7 +123,7 @@ $emcp_cleared = isset( $_GET['cleared'] ) ? absint( wp_unslash( $_GET['cleared']
?>
<tr>
<td>
<?php echo esc_html( $emcp_ts ? date_i18n( 'Y-m-d H:i', $emcp_ts ) : '—' ); ?>
<?php echo esc_html( $emcp_ts ? date_i18n( 'Y-m-d H:i', $emcp_ts ) : ', ' ); ?>
<?php if ( $emcp_ts ) : ?>
<span class="emcp-history__ago"><?php echo esc_html( sprintf( /* translators: %s: human time diff */ __( '%s ago', 'emcp-tools' ), human_time_diff( $emcp_ts ) ) ); ?></span>
<?php endif; ?>
@@ -146,14 +146,14 @@ $emcp_cleared = isset( $_GET['cleared'] ) ? absint( wp_unslash( $_GET['cleared']
<?php esc_html_e( 'Roll back', 'emcp-tools' ); ?>
</a>
<?php else : ?>
<span class="emcp-history__state">—</span>
<span class="emcp-history__state">, </span>
<?php endif; ?>
<a class="emcp-history__delete"
href="<?php echo esc_url( EMCP_Tools_Admin::delete_change_url( $emcp_id ) ); ?>"
aria-label="<?php esc_attr_e( 'Delete this history entry', 'emcp-tools' ); ?>"
onclick="return confirm('<?php echo esc_js(
$emcp_reversible
? __( 'Delete this entry? It can still be rolled back — deleting it discards that ability permanently.', 'emcp-tools' )
? __( 'Delete this entry? It can still be rolled back, deleting it discards that ability permanently.', 'emcp-tools' )
: __( 'Delete this history entry? This cannot be undone.', 'emcp-tools' )
); ?>');">
<span class="dashicons dashicons-trash" aria-hidden="true"></span>
+1 -1
View File
@@ -64,7 +64,7 @@ $modules = $registry ? $registry->all() : array();
if ( EMCP_Tools_Image_Optimization_Module::ID === $module->id() ) {
esc_html_e( 'Not available on this server (WebP support is missing in the image editor).', 'emcp-tools' );
} else {
esc_html_e( 'Not available — requires an active Pro license.', 'emcp-tools' );
esc_html_e( 'Not available, requires an active Pro license.', 'emcp-tools' );
}
?>
</p>
+1 -1
View File
@@ -40,7 +40,7 @@ $emcp_upsell_url = function_exists( 'emcp_tools_upgrade_url' ) ? emcp_tools_
printf( esc_html__( '%s is an EMCP Pro feature', 'emcp-tools' ), esc_html( $emcp_upsell_feature ) );
?>
</h2>
<p><?php esc_html_e( 'Upgrade to EMCP Pro to unlock this feature along with the full Pro toolkit — AI Chat, the SEO & Accessibility toolkit, the Widget Builder, Brand Kits, premium prompts and templates, and more.', 'emcp-tools' ); ?></p>
<p><?php esc_html_e( 'Upgrade to EMCP Pro to unlock this feature along with the full Pro toolkit, AI Chat, the SEO & Accessibility toolkit, the Widget Builder, Brand Kits, premium prompts and templates, and more.', 'emcp-tools' ); ?></p>
<a class="button button-primary button-hero" href="<?php echo esc_url( $emcp_upsell_url ); ?>" target="_blank" rel="noopener noreferrer">
<?php esc_html_e( 'Upgrade to Pro', 'emcp-tools' ); ?>
</a>
+3 -3
View File
@@ -81,7 +81,7 @@ $emcp_tools_v1_url = $emcp_tools_v1_available ? EMCP_Tools_Pro_Prompts::v1
<div class="elementor-mcp-prompts-whatsnew-body">
<h3><?php esc_html_e( 'The prompts have been rewritten', 'emcp-tools' ); ?></h3>
<p>
<?php esc_html_e( 'Prompts no longer dictate a fixed, section-by-section layout. Each one now gives the AI a style guide, a design direction, the exact content, and hard standards — accessibility, real photography, consistent SVG icons, and a working contact form — then lets it design the page. Expect noticeably better, more distinctive results.', 'emcp-tools' ); ?>
<?php esc_html_e( 'Prompts no longer dictate a fixed, section-by-section layout. Each one now gives the AI a style guide, a design direction, the exact content, and hard standards, accessibility, real photography, consistent SVG icons, and a working contact form, then lets it design the page. Expect noticeably better, more distinctive results.', 'emcp-tools' ); ?>
</p>
<p>
<?php esc_html_e( 'They also work with any page builder: change the first line of a prompt from Elementor to Gutenberg, Bricks, or plain HTML/CSS, and the rest still applies.', 'emcp-tools' ); ?>
@@ -112,7 +112,7 @@ $emcp_tools_v1_url = $emcp_tools_v1_available ? EMCP_Tools_Pro_Prompts::v1
<div class="elementor-mcp-prompts-intro">
<h2><?php esc_html_e( 'Sample Prompts', 'emcp-tools' ); ?></h2>
<p class="description">
<?php esc_html_e( 'Ready-to-use landing page blueprints for AI agents. Copy any prompt below and paste it into your AI client (Claude, Cursor, etc.) — it will automatically build a complete Elementor page using MCP tools.', 'emcp-tools' ); ?>
<?php esc_html_e( 'Ready-to-use landing page blueprints for AI agents. Copy any prompt below and paste it into your AI client (Claude, Cursor, etc.), it will automatically build a complete Elementor page using MCP tools.', 'emcp-tools' ); ?>
</p>
</div>
@@ -291,7 +291,7 @@ $emcp_tools_v1_url = $emcp_tools_v1_available ? EMCP_Tools_Pro_Prompts::v1
<div class="elementor-mcp-prompts-cta">
<div class="elementor-mcp-prompts-cta-content">
<h3><?php esc_html_e( 'Unlock 50+ Premium Prompts', 'emcp-tools' ); ?></h3>
<p><?php esc_html_e( 'Industry-specific landing page blueprints across 10 categories — restaurants, dental clinics, law firms, photographers, wedding venues, and more. Auto-synced to your site when you upgrade.', 'emcp-tools' ); ?></p>
<p><?php esc_html_e( 'Industry-specific landing page blueprints across 10 categories, restaurants, dental clinics, law firms, photographers, wedding venues, and more. Auto-synced to your site when you upgrade.', 'emcp-tools' ); ?></p>
<a href="<?php echo esc_url( $emcp_tools_upgrade_url ); ?>" class="button button-primary elementor-mcp-prompts-cta-btn" target="_blank" rel="noopener noreferrer">
<svg viewBox="0 0 20 20" width="16" height="16" xmlns="http://www.w3.org/2000/svg"><path d="M9.049 2.927c.3-.921 1.603-.921 1.902 0l1.07 3.292a1 1 0 00.95.69h3.462c.969 0 1.371 1.24.588 1.81l-2.8 2.034a1 1 0 00-.364 1.118l1.07 3.292c.3.921-.755 1.688-1.54 1.118l-2.8-2.034a1 1 0 00-1.175 0l-2.8 2.034c-.784.57-1.838-.197-1.539-1.118l1.07-3.292a1 1 0 00-.364-1.118L2.98 8.72c-.783-.57-.38-1.81.588-1.81h3.461a1 1 0 00.951-.69l1.07-3.292z"/></svg>
<?php esc_html_e( 'Upgrade to Pro', 'emcp-tools' ); ?>
+2 -2
View File
@@ -197,7 +197,7 @@ $emcp_tools_upgrade_url = emcp_tools_upgrade_url();
class="button elementor-mcp-template-import"
data-category-slug="<?php echo esc_attr( $emcp_tools_cat_slug ); ?>"
data-template-slug="<?php echo esc_attr( $emcp_tools_t_slug ); ?>"
title="<?php esc_attr_e( 'Add to Elementor\'s Saved Templates library — insertable from the editor\'s Add Template picker on any page.', 'emcp-tools' ); ?>"
title="<?php esc_attr_e( 'Add to Elementor\'s Saved Templates library, insertable from the editor\'s Add Template picker on any page.', 'emcp-tools' ); ?>"
>
<?php esc_html_e( 'Import to Library', 'emcp-tools' ); ?>
</button>
@@ -239,7 +239,7 @@ $emcp_tools_upgrade_url = emcp_tools_upgrade_url();
<div class="elementor-mcp-prompts-cta">
<div class="elementor-mcp-prompts-cta-content">
<h3><?php esc_html_e( 'Unlock the Premium Templates Library', 'emcp-tools' ); ?></h3>
<p><?php esc_html_e( 'Ready-to-apply Elementor page templates across hero sections, services grids, pricing tables, testimonials, and more. One-click apply creates a new page with the full design — edit visually from there.', 'emcp-tools' ); ?></p>
<p><?php esc_html_e( 'Ready-to-apply Elementor page templates across hero sections, services grids, pricing tables, testimonials, and more. One-click apply creates a new page with the full design, edit visually from there.', 'emcp-tools' ); ?></p>
<a href="<?php echo esc_url( $emcp_tools_upgrade_url ); ?>" class="button button-primary elementor-mcp-prompts-cta-btn" target="_blank" rel="noopener noreferrer">
<svg viewBox="0 0 20 20" width="16" height="16" xmlns="http://www.w3.org/2000/svg"><path d="M9.049 2.927c.3-.921 1.603-.921 1.902 0l1.07 3.292a1 1 0 00.95.69h3.462c.969 0 1.371 1.24.588 1.81l-2.8 2.034a1 1 0 00-.364 1.118l1.07 3.292c.3.921-.755 1.688-1.54 1.118l-2.8-2.034a1 1 0 00-1.175 0l-2.8 2.034c-.784.57-1.838-.197-1.539-1.118l1.07-3.292a1 1 0 00-.364-1.118L2.98 8.72c-.783-.57-.38-1.81.588-1.81h3.461a1 1 0 00.951-.69l1.07-3.292z"/></svg>
<?php esc_html_e( 'Upgrade to Pro', 'emcp-tools' ); ?>
+2 -2
View File
@@ -13,7 +13,7 @@ if ( ! defined( 'ABSPATH' ) ) {
}
?>
<div class="wrap emcp-themer-php-wrap">
<h1><?php esc_html_e( 'EMCP Themer — PHP Templates', 'emcp-tools' ); ?></h1>
<h1><?php esc_html_e( 'EMCP Themer, PHP Templates', 'emcp-tools' ); ?></h1>
<p class="description">
<?php esc_html_e( 'PHP templates authored via MCP. Review and edit the code, then attach one to a template on its edit screen (Display Conditions box → “Render with PHP template”). A template only runs once attached.', 'emcp-tools' ); ?>
</p>
@@ -88,7 +88,7 @@ if ( ! defined( 'ABSPATH' ) ) {
<p><label for="emcp-themer-php-code"><strong><?php esc_html_e( 'Template code', 'emcp-tools' ); ?></strong></label></p>
<textarea id="emcp-themer-php-code" name="code" rows="20" class="large-text code" spellcheck="false" style="width:100%;font-family:Consolas,Monaco,monospace;"><?php echo esc_textarea( $detail['code'] ); ?></textarea>
<p class="description">
<?php esc_html_e( 'Emit markup with echo/heredoc — a closing PHP tag is not allowed. eval/exec/include, network calls and file writes are rejected on save. If this template is attached, saving re-validates and recompiles it.', 'emcp-tools' ); ?>
<?php esc_html_e( 'Emit markup with echo/heredoc, a closing PHP tag is not allowed. eval/exec/include, network calls and file writes are rejected on save. If this template is attached, saving re-validates and recompiles it.', 'emcp-tools' ); ?>
</p>
<p class="submit" style="margin-top:8px;">
+4 -4
View File
@@ -79,7 +79,7 @@ $emcp_tools_badge_labels = array(
<?php esc_html_e( 'Compact tool mode', 'emcp-tools' ); ?>
</span>
<span class="elementor-mcp-low-mode-desc">
<?php esc_html_e( 'Exposes 3 dispatcher tools (list-tools, get-tool-schema, call-tool) instead of every individual tool, so MCP clients that cap the tool count can still reach the whole surface. Your per-tool toggles below stay in effect — call-tool refuses any tool you disable. Reconnect your client after changing this.', 'emcp-tools' ); ?>
<?php esc_html_e( 'Exposes 3 dispatcher tools (list-tools, get-tool-schema, call-tool) instead of every individual tool, so MCP clients that cap the tool count can still reach the whole surface. Your per-tool toggles below stay in effect, call-tool refuses any tool you disable. Reconnect your client after changing this.', 'emcp-tools' ); ?>
</span>
</span>
</label>
@@ -104,7 +104,7 @@ $emcp_tools_badge_labels = array(
<?php esc_html_e( 'Themer PHP Templates (advanced)', 'emcp-tools' ); ?>
</span>
<span class="elementor-mcp-low-mode-desc">
<?php esc_html_e( 'Lets AI agents author raw PHP region templates (header/footer/single/archive) into a validated sandbox, which you then select on a template to take over its render. Off by default — enabling it also reveals the “PHP Templates” screen under the EMCP Themer menu and the per-template selector. The 5 MCP tools below still ship disabled until you enable them.', 'emcp-tools' ); ?>
<?php esc_html_e( 'Lets AI agents author raw PHP region templates (header/footer/single/archive) into a validated sandbox, which you then select on a template to take over its render. Off by default, enabling it also reveals the “PHP Templates” screen under the EMCP Themer menu and the per-template selector. The 5 MCP tools below still ship disabled until you enable them.', 'emcp-tools' ); ?>
</span>
</span>
</label>
@@ -115,7 +115,7 @@ $emcp_tools_badge_labels = array(
<?php if ( $emcp_tools_compact_mode ) : ?>
<div class="elementor-mcp-compact-banner">
<p>
<?php esc_html_e( 'Compact tool mode is active — your client sees only the 3 dispatcher tools (list-tools, get-tool-schema, call-tool). The per-tool toggles below still control what call-tool is allowed to run.', 'emcp-tools' ); ?>
<?php esc_html_e( 'Compact tool mode is active, your client sees only the 3 dispatcher tools (list-tools, get-tool-schema, call-tool). The per-tool toggles below still control what call-tool is allowed to run.', 'emcp-tools' ); ?>
</p>
</div>
<?php endif; ?>
@@ -249,7 +249,7 @@ $emcp_tools_badge_labels = array(
<?php if ( ! empty( $emcp_tools_category['pro'] ) && ! $emcp_tools_is_pro ) : ?>
<a class="elementor-mcp-cat-upgrade" href="<?php echo esc_url( $emcp_tools_upgrade_url ); ?>" target="_blank" rel="noopener noreferrer">
<span class="dashicons dashicons-star-filled" aria-hidden="true"></span>
<?php esc_html_e( 'Requires EMCP Pro — Upgrade', 'emcp-tools' ); ?>
<?php esc_html_e( 'Requires EMCP Pro, Upgrade', 'emcp-tools' ); ?>
</a>
<?php endif; ?>
<?php if ( empty( $emcp_tools_category['pro'] ) || $emcp_tools_is_pro ) : ?>
+4 -4
View File
@@ -30,7 +30,7 @@ $emcp_tools_wb_url = function_exists( 'emcp_tools_upgrade_url' ) ? emcp_tools_up
<span class="elementor-mcp-badge elementor-mcp-badge--pro">PRO</span>
</h2>
<p class="description">
<?php esc_html_e( 'Code your AI agent generated through the MCP tools — starting with custom Elementor widgets. Everything lives in an isolated sandbox under wp-content/uploads, never in your theme, core, or other plugins. Active widgets appear in the Elementor panel under "Custom (EMCP)".', 'emcp-tools' ); ?>
<?php esc_html_e( 'Code your AI agent generated through the MCP tools, starting with custom Elementor widgets. Everything lives in an isolated sandbox under wp-content/uploads, never in your theme, core, or other plugins. Active widgets appear in the Elementor panel under "Custom (EMCP)".', 'emcp-tools' ); ?>
</p>
</div>
</div>
@@ -185,7 +185,7 @@ $emcp_tools_wb_url = function_exists( 'emcp_tools_upgrade_url' ) ? emcp_tools_up
<span class="elementor-mcp-badge elementor-mcp-badge--free"><?php esc_html_e( 'FREE', 'emcp-tools' ); ?></span>
</h2>
<p class="description">
<?php esc_html_e( 'Run small pieces of PHP on your site — as a [emcp_snippet] shortcode or on a WordPress hook. An AI agent can draft snippets through the MCP tools, but they stay INACTIVE until you review and activate them here.', 'emcp-tools' ); ?>
<?php esc_html_e( 'Run small pieces of PHP on your site, as a [emcp_snippet] shortcode or on a WordPress hook. An AI agent can draft snippets through the MCP tools, but they stay INACTIVE until you review and activate them here.', 'emcp-tools' ); ?>
</p>
</div>
</div>
@@ -193,7 +193,7 @@ $emcp_tools_wb_url = function_exists( 'emcp_tools_upgrade_url' ) ? emcp_tools_up
<div class="notice notice-error inline" style="margin: 12px 0;">
<p>
<strong><?php esc_html_e( 'This runs real PHP on your site.', 'emcp-tools' ); ?></strong>
<?php esc_html_e( 'The validator blocks obviously dangerous code (shell/eval/file writes/network/obfuscation), but static analysis is a guardrail, not a guarantee — only activate code you have read and trust. Activation is the approval step; AI can only create inactive drafts.', 'emcp-tools' ); ?>
<?php esc_html_e( 'The validator blocks obviously dangerous code (shell/eval/file writes/network/obfuscation), but static analysis is a guardrail, not a guarantee, only activate code you have read and trust. Activation is the approval step; AI can only create inactive drafts.', 'emcp-tools' ); ?>
</p>
</div>
@@ -292,7 +292,7 @@ $emcp_tools_wb_url = function_exists( 'emcp_tools_upgrade_url' ) ? emcp_tools_up
<?php
printf(
/* translators: %d: number of warnings */
esc_html( _n( '%d validator warning — review the code', '%d validator warnings — review the code', $emcp_tools_swarn, 'emcp-tools' ) ),
esc_html( _n( '%d validator warning, review the code', '%d validator warnings, review the code', $emcp_tools_swarn, 'emcp-tools' ) ),
(int) $emcp_tools_swarn
);
?>
+1 -1
View File
@@ -149,7 +149,7 @@ class EMCP_Tools_Admin_Bar {
'id' => 'emcp-tools-mcp',
'title' => $dot . esc_html__( 'MCP', 'emcp-tools' ),
'href' => esc_url( $settings ),
'meta' => array( 'title' => esc_attr__( 'EMCP Tools — MCP status', 'emcp-tools' ) ),
'meta' => array( 'title' => esc_attr__( 'EMCP Tools, MCP status', 'emcp-tools' ) ),
) );
$wp_admin_bar->add_node( array(
+2 -2
View File
@@ -367,7 +367,7 @@ class EMCP_Tools_Bootstrap {
'<div class="notice notice-error"><p>%s</p></div>',
sprintf(
/* translators: %s: current PHP version */
esc_html__( 'EMCP Tools requires PHP 8.1 or higher. Your server is running PHP %s — please upgrade PHP to avoid silent Elementor write failures.', 'emcp-tools' ),
esc_html__( 'EMCP Tools requires PHP 8.1 or higher. Your server is running PHP %s, please upgrade PHP to avoid silent Elementor write failures.', 'emcp-tools' ),
esc_html( PHP_VERSION )
)
);
@@ -388,7 +388,7 @@ class EMCP_Tools_Bootstrap {
// ran above). Only fails if the bundled source is missing/corrupt — a
// broken build, not a user action.
if ( ! class_exists( '\WP\MCP\Core\McpAdapter' ) ) {
$missing[] = 'WordPress MCP Adapter (bundled — reinstall the plugin if this persists)';
$missing[] = 'WordPress MCP Adapter (bundled, reinstall the plugin if this persists)';
}
if ( ! empty( $missing ) ) {
+1 -1
View File
@@ -279,7 +279,7 @@ class EMCP_Tools_Change_Log {
private static function rollback_file_delete( array $rb ) {
$target = (string) ( $rb['target_path'] ?? '' );
if ( '' === $target || ! is_file( $target ) ) {
return true; // Already gone — nothing to undo.
return true; // Already gone, nothing to undo.
}
$safe = self::guard_target( $target );
if ( is_wp_error( $safe ) ) {
+1 -1
View File
@@ -153,7 +153,7 @@ class EMCP_Tools_Child_Theme_Builder {
*/
private static function functions_php( string $parent_name ): string {
return "<?php\n"
. '// ' . $parent_name . " child theme — generated by EMCP Tools.\n"
. '// ' . $parent_name . " child theme, generated by EMCP Tools.\n"
. "if ( ! defined( 'ABSPATH' ) ) { exit; }\n\n"
. "add_action( 'wp_enqueue_scripts', function () {\n"
. "\twp_enqueue_style( 'emcp-parent-style', get_template_directory_uri() . '/style.css' );\n"
+1 -1
View File
@@ -127,7 +127,7 @@ class EMCP_Tools_Content_Mirror {
}
if ( ! is_file( $dir . '/.gitignore' ) ) {
// Do NOT ignore json — this dir is meant to be committed. Keep a README hint.
@file_put_contents( $dir . '/README.txt', "EMCP content mirror — commit the .json files to version your page designs.\n" ); // phpcs:ignore
@file_put_contents( $dir . '/README.txt', "EMCP content mirror, commit the .json files to version your page designs.\n" ); // phpcs:ignore
}
$path = $dir . '/' . self::file_name( $type, $post_id, (string) $post->post_name );
+2 -2
View File
@@ -105,9 +105,9 @@ class EMCP_Tools_Kit_Backup_Store {
$stamp = current_time( 'Y-m-d H:i:s' );
$title = '' !== $label
/* translators: 1: brand kit label, 2: date/time */
? sprintf( __( 'Before "%1$s" — %2$s', 'emcp-tools' ), $label, $stamp )
? sprintf( __( 'Before "%1$s", %2$s', 'emcp-tools' ), $label, $stamp )
/* translators: %s: date/time */
: sprintf( __( 'Backup — %s', 'emcp-tools' ), $stamp );
: sprintf( __( 'Backup, %s', 'emcp-tools' ), $stamp );
$post_id = wp_insert_post(
array(
+3 -3
View File
@@ -142,7 +142,7 @@ class EMCP_Tools_PHP_Snippet_Store {
self::write_file( $dir . '/index.php', "<?php\n// Silence is golden.\n" );
self::write_file(
self::sandbox_dir() . '/.htaccess',
"# Generated by MCP Tools for Elementor — block direct PHP access.\n<FilesMatch \"\\.php$\">\n<IfModule mod_authz_core.c>\nRequire all denied\n</IfModule>\n<IfModule !mod_authz_core.c>\ndeny from all\n</IfModule>\n</FilesMatch>\n"
"# Generated by MCP Tools for Elementor, block direct PHP access.\n<FilesMatch \"\\.php$\">\n<IfModule mod_authz_core.c>\nRequire all denied\n</IfModule>\n<IfModule !mod_authz_core.c>\ndeny from all\n</IfModule>\n</FilesMatch>\n"
);
return true;
}
@@ -316,7 +316,7 @@ class EMCP_Tools_PHP_Snippet_Store {
$body = EMCP_Tools_PHP_Snippet_Validator::strip_tags( $code );
$func = self::func_name( $post_id );
$php = "<?php\n"
. "// EMCP PHP Snippet {$post_id} — generated from the snippet stored in the database.\n"
. "// EMCP PHP Snippet {$post_id}, generated from the snippet stored in the database.\n"
. "// Do not edit: edits are ignored and fail the integrity check.\n"
. "if ( ! function_exists( '{$func}' ) ) {\n"
. "\tfunction {$func}() {\n"
@@ -364,7 +364,7 @@ class EMCP_Tools_PHP_Snippet_Store {
$validation = EMCP_Tools_PHP_Snippet_Validator::validate( $code );
if ( ! $validation['valid'] || ! $validation['safe'] ) {
update_post_meta( $post_id, self::META_VALIDATION, wp_slash( (string) wp_json_encode( $validation ) ) );
return new WP_Error( 'activation_blocked', __( 'Cannot activate — the snippet fails validation. Fix the flagged code and try again.', 'emcp-tools' ), array( 'validation' => $validation ) );
return new WP_Error( 'activation_blocked', __( 'Cannot activate, the snippet fails validation. Fix the flagged code and try again.', 'emcp-tools' ), array( 'validation' => $validation ) );
}
$written = self::write_executable( $post_id, $code );
if ( is_wp_error( $written ) ) {
+7 -7
View File
@@ -85,13 +85,13 @@ class EMCP_Tools_PHP_Snippet_Validator {
'socket_create' => 'Opens a network socket.',
'socket_connect' => 'Connects a network socket.',
// Obfuscation decoders (the #1 malware signal; rarely needed in a snippet).
'base64_decode' => 'Decodes hidden data — a common malware obfuscation.',
'gzinflate' => 'Decompresses hidden data — a common malware obfuscation.',
'gzuncompress' => 'Decompresses hidden data — a common malware obfuscation.',
'gzdecode' => 'Decompresses hidden data — a common malware obfuscation.',
'str_rot13' => 'Decodes hidden data — a common malware obfuscation.',
'convert_uudecode' => 'Decodes hidden data — a common malware obfuscation.',
'hex2bin' => 'Decodes hidden data — a common malware obfuscation.',
'base64_decode' => 'Decodes hidden data, a common malware obfuscation.',
'gzinflate' => 'Decompresses hidden data, a common malware obfuscation.',
'gzuncompress' => 'Decompresses hidden data, a common malware obfuscation.',
'gzdecode' => 'Decompresses hidden data, a common malware obfuscation.',
'str_rot13' => 'Decodes hidden data, a common malware obfuscation.',
'convert_uudecode' => 'Decodes hidden data, a common malware obfuscation.',
'hex2bin' => 'Decodes hidden data, a common malware obfuscation.',
// Runtime / environment manipulation.
'dl' => 'Loads a PHP extension at runtime.',
'putenv' => 'Changes environment variables.',
+1 -1
View File
@@ -182,7 +182,7 @@ class EMCP_Tools_Pixabay_Client {
$code = (int) wp_remote_retrieve_response_code( $response );
if ( 400 === $code || 401 === $code ) {
// Pixabay returns 400 for a bad/missing key or invalid params.
return new \WP_Error( 'invalid_key', __( 'Pixabay rejected the request — check the API key on EMCP Tools → Connection.', 'emcp-tools' ) );
return new \WP_Error( 'invalid_key', __( 'Pixabay rejected the request, check the API key on EMCP Tools → Connection.', 'emcp-tools' ) );
}
if ( 429 === $code ) {
return new \WP_Error( 'rate_limited', __( 'Pixabay rate limit reached (100 requests/minute). Try again shortly.', 'emcp-tools' ) );
+1 -1
View File
@@ -181,7 +181,7 @@ class EMCP_Tools_System_Kit_Writer {
return new WP_Error(
'invalid_color',
/* translators: %s: system color slot name */
sprintf( __( 'The "%s" slot has an invalid hex color. Aborting — a brand kit replaces the whole palette or none of it.', 'emcp-tools' ), $slot )
sprintf( __( 'The "%s" slot has an invalid hex color. Aborting, a brand kit replaces the whole palette or none of it.', 'emcp-tools' ), $slot )
);
}
+2 -2
View File
@@ -150,7 +150,7 @@ class EMCP_Tools_Widget_Store {
self::write_file( $widgets_dir . '/index.php', "<?php\n// Silence is golden.\n" );
self::write_file(
$dir . '/.htaccess',
"# Generated by MCP Tools for Elementor — block direct PHP; allow css/js assets.\n<FilesMatch \"\\.php$\">\n<IfModule mod_authz_core.c>\nRequire all denied\n</IfModule>\n<IfModule !mod_authz_core.c>\ndeny from all\n</IfModule>\n</FilesMatch>\n"
"# Generated by MCP Tools for Elementor, block direct PHP; allow css/js assets.\n<FilesMatch \"\\.php$\">\n<IfModule mod_authz_core.c>\nRequire all denied\n</IfModule>\n<IfModule !mod_authz_core.c>\ndeny from all\n</IfModule>\n</FilesMatch>\n"
);
return true;
@@ -538,7 +538,7 @@ class EMCP_Tools_Widget_Store {
'activation_blocked',
sprintf(
/* translators: %s: error message */
__( 'Cannot activate — the widget errors when Elementor builds it: %s. Fix the spec and update the widget.', 'emcp-tools' ),
__( 'Cannot activate, the widget errors when Elementor builds it: %s. Fix the spec and update the widget.', 'emcp-tools' ),
$rt->get_error_message()
)
);
@@ -37,7 +37,7 @@ class EMCP_Tools_Agent_Skills_Module extends EMCP_Tools_Module {
}
public function description(): string {
return __( 'Expose the bundled skills to connected AI agents at runtime — the list-skills / get-skill tools plus a Skills catalog in the discovery context. Turn off to remove that injection (the Skills download on the Skills tab is unaffected).', 'emcp-tools' );
return __( 'Expose the bundled skills to connected AI agents at runtime, the list-skills / get-skill tools plus a Skills catalog in the discovery context. Turn off to remove that injection (the Skills download on the Skills tab is unaffected).', 'emcp-tools' );
}
public function tier(): string {
+1 -1
View File
@@ -31,7 +31,7 @@ class EMCP_Tools_Brand_Kits_Module extends EMCP_Tools_Module {
}
public function description(): string {
return __( 'One-click color + typography kits for your site — bundled free kits plus the premium library.', 'emcp-tools' );
return __( 'One-click color + typography kits for your site, bundled free kits plus the premium library.', 'emcp-tools' );
}
public function tier(): string {
+1 -1
View File
@@ -31,7 +31,7 @@ class EMCP_Tools_Prompts_Module extends EMCP_Tools_Module {
}
public function description(): string {
return __( 'Ready-to-use AI prompt blueprints for building pages — bundled samples plus the premium library.', 'emcp-tools' );
return __( 'Ready-to-use AI prompt blueprints for building pages, bundled samples plus the premium library.', 'emcp-tools' );
}
public function tier(): string {
+1 -1
View File
@@ -32,7 +32,7 @@ class EMCP_Tools_Templates_Module extends EMCP_Tools_Module {
}
public function description(): string {
return __( 'Ready-to-apply Elementor page templates — one-click creates a full page you edit visually.', 'emcp-tools' );
return __( 'Ready-to-apply Elementor page templates, one-click creates a full page you edit visually.', 'emcp-tools' );
}
public function tier(): string {
@@ -41,7 +41,7 @@ class EMCP_Tools_SVG_Support_Module extends EMCP_Tools_Module {
}
public function description(): string {
return __( 'Safely allow SVG uploads to the Media Library. Every SVG is sanitized (scripts, event handlers and external references stripped) before it is saved. Mainly for sites without Elementor — Elementor already allows SVG uploads for authorized users.', 'emcp-tools' );
return __( 'Safely allow SVG uploads to the Media Library. Every SVG is sanitized (scripts, event handlers and external references stripped) before it is saved. Mainly for sites without Elementor, Elementor already allows SVG uploads for authorized users.', 'emcp-tools' );
}
public function tier(): string {
@@ -83,7 +83,7 @@ class EMCP_Tools_Performance_Server_Audit {
public function evaluate_opcache( bool $enabled ): array {
return $enabled
? EMCP_Tools_Performance_Finding::make( 'opcache', 'server', 'PHP OPcache', 'pass', true, 'OPcache is enabled.' )
: EMCP_Tools_Performance_Finding::make( 'opcache', 'server', 'PHP OPcache', 'warning', false, 'OPcache is disabled.', 'Enable the Zend OPcache extension — it caches compiled PHP and dramatically reduces request time.' );
: EMCP_Tools_Performance_Finding::make( 'opcache', 'server', 'PHP OPcache', 'warning', false, 'OPcache is disabled.', 'Enable the Zend OPcache extension, it caches compiled PHP and dramatically reduces request time.' );
}
public function evaluate_object_cache( bool $persistent ): array {
@@ -104,7 +104,7 @@ class EMCP_Tools_Performance_Server_Audit {
return EMCP_Tools_Performance_Finding::make( 'wp_debug', 'config', 'WP_DEBUG', 'pass', false, 'WP_DEBUG is off.' );
}
if ( 'production' === $environment ) {
return EMCP_Tools_Performance_Finding::make( 'wp_debug', 'config', 'WP_DEBUG', 'warning', true, 'WP_DEBUG is ON in production.', 'Turn off WP_DEBUG on production — debug logging and notices add overhead and leak information.' );
return EMCP_Tools_Performance_Finding::make( 'wp_debug', 'config', 'WP_DEBUG', 'warning', true, 'WP_DEBUG is ON in production.', 'Turn off WP_DEBUG on production, debug logging and notices add overhead and leak information.' );
}
return EMCP_Tools_Performance_Finding::make( 'wp_debug', 'config', 'WP_DEBUG', 'info', true, sprintf( 'WP_DEBUG is on (environment: %s).', $environment ) );
}
@@ -134,7 +134,7 @@ class EMCP_Tools_Performance_Server_Audit {
$human = $this->human_bytes( $bytes );
$value = array( 'bytes' => $bytes, 'top' => $top_options );
if ( $bytes >= self::AUTOLOAD_CRIT_BYTES ) {
return EMCP_Tools_Performance_Finding::make( 'autoload_size', 'database', 'Autoloaded options', 'critical', $value, sprintf( 'Autoloaded options total %s — loaded on every request.', $human ), 'Find and disable autoload for the largest offenders (often stale plugin caches). See the listed options.' );
return EMCP_Tools_Performance_Finding::make( 'autoload_size', 'database', 'Autoloaded options', 'critical', $value, sprintf( 'Autoloaded options total %s, loaded on every request.', $human ), 'Find and disable autoload for the largest offenders (often stale plugin caches). See the listed options.' );
}
if ( $bytes >= self::AUTOLOAD_WARN_BYTES ) {
return EMCP_Tools_Performance_Finding::make( 'autoload_size', 'database', 'Autoloaded options', 'warning', $value, sprintf( 'Autoloaded options total %s.', $human ), 'Trim autoloaded options above ~1 MB; large autoload bloats every request.' );
@@ -60,7 +60,7 @@ class EMCP_Tools_Security_Hardening_Audit {
$scheme = strtolower( (string) wp_parse_url( $home_url, PHP_URL_SCHEME ) );
return 'https' === $scheme
? EMCP_Tools_Security_Finding::make( 'harden_https', 'hardening', 'HTTPS', 'pass', $home_url, 'The site URL uses HTTPS.' )
: EMCP_Tools_Security_Finding::make( 'harden_https', 'hardening', 'HTTPS', 'warning', $home_url, 'The site is not served over HTTPS.', 'Install a TLS certificate and move Site Address to https:// — plain HTTP exposes logins and cookies.' );
: EMCP_Tools_Security_Finding::make( 'harden_https', 'hardening', 'HTTPS', 'warning', $home_url, 'The site is not served over HTTPS.', 'Install a TLS certificate and move Site Address to https://, plain HTTP exposes logins and cookies.' );
}
/**
@@ -78,7 +78,7 @@ class EMCP_Tools_Security_Integrity_Audit {
$hasher = static function ( string $relpath ): ?string {
$full = ABSPATH . $relpath;
if ( 0 === strpos( $relpath, 'wp-content/' ) ) {
return null; // Excluded — treat as present to suppress findings.
return null; // Excluded, treat as present to suppress findings.
}
if ( ! is_file( $full ) ) {
return null;
@@ -19,7 +19,7 @@ if ( ! defined( 'ABSPATH' ) ) {
*/
class EMCP_Tools_Security_Malware_Audit {
const MAX_FILE_BYTES = 2097152; // 2 MB — skip larger files.
const MAX_FILE_BYTES = 2097152; // 2 MB, skip larger files.
const MAX_LINE_BYTES = 65536; // Cap the slice fed to regexes (ReDoS / backtrack-limit guard).
const MAX_FILES = 2000;
const MAX_FILES_CEILING = 20000;
@@ -334,7 +334,7 @@ class EMCP_Tools_Security_Malware_Audit {
'label' => 'Obfuscated code execution',
'pattern' => '/\b(eval|assert|create_function)\s*\(.*\b(base64_decode|gzinflate|gzuncompress|str_rot13|strrev|convert_uudecode)\s*\(/i',
'status' => 'critical',
'message' => 'A code-execution call wrapping a decoder (e.g. eval(base64_decode(...))) — a classic malware pattern.',
'message' => 'A code-execution call wrapping a decoder (e.g. eval(base64_decode(...))), a classic malware pattern.',
'recommendation' => 'Treat this file as compromised: review it, restore a clean copy from source control or a fresh download, and rotate credentials.',
),
array(
@@ -19,7 +19,7 @@ if ( ! defined( 'ABSPATH' ) ) {
class EMCP_Tools_Security_Software_Audit {
const MAX_ABANDONED_LOOKUPS = 30;
const ABANDONED_CACHE_TTL = 43200; // 12 hours — cache per-slug closed/open status.
const ABANDONED_CACHE_TTL = 43200; // 12 hours, cache per-slug closed/open status.
public function evaluate_core_update( bool $available, string $current, string $new ): array {
return $available
@@ -167,7 +167,7 @@ class EMCP_Tools_Security_Software_Audit {
$info = plugins_api( 'plugin_information', array( 'slug' => $slug, 'fields' => array( 'sections' => false ) ) );
if ( is_wp_error( $info ) ) {
continue; // Not on wordpress.org, or API down — don't flag, don't cache.
continue; // Not on wordpress.org, or API down, don't flag, don't cache.
}
$state = ! empty( $info->closed ) ? 'closed' : 'open';
set_transient( $cache_key, $state, self::ABANDONED_CACHE_TTL );
@@ -315,7 +315,7 @@ class EMCP_Tools_Themer_Blocks {
* @return array<int,array{value:int,label:string}>
*/
private static function menu_choices(): array {
$out = array( array( 'value' => 0, 'label' => __( '— Auto (first menu) —', 'emcp-tools' ) ) );
$out = array( array( 'value' => 0, 'label' => __( ', Auto (first menu) , ', 'emcp-tools' ) ) );
$menus = function_exists( 'wp_get_nav_menus' ) ? wp_get_nav_menus() : array();
foreach ( $menus as $menu ) {
$out[] = array( 'value' => (int) $menu->term_id, 'label' => $menu->name );
+5 -5
View File
@@ -224,11 +224,11 @@ class EMCP_Tools_Themer_CPT {
$theme = function_exists( 'wp_get_theme' ) ? wp_get_theme()->get( 'Name' ) : '';
echo '<div class="notice notice-success"><p>' . sprintf(
/* translators: %s: theme name */
esc_html__( 'EMCP Themer: your theme (%s) is directly supported — standalone headers & footers inject cleanly. Body templates (single/archive/search/404) work on every theme.', 'emcp-tools' ),
esc_html__( 'EMCP Themer: your theme (%s) is directly supported, standalone headers & footers inject cleanly. Body templates (single/archive/search/404) work on every theme.', 'emcp-tools' ),
esc_html( (string) $theme )
) . '</p></div>';
} else {
echo '<div class="notice notice-warning"><p>' . esc_html__( 'EMCP Themer: body templates (single/archive/search/404) work on every theme. For standalone header/footer replacement your theme is not directly supported — add emcp_themer_location( \'header\' ) / emcp_themer_location( \'footer\' ) to your theme, or enable the full-page-takeover fallback (set the emcp_tools_module_themer_force_render option to 1).', 'emcp-tools' ) . '</p></div>';
echo '<div class="notice notice-warning"><p>' . esc_html__( 'EMCP Themer: body templates (single/archive/search/404) work on every theme. For standalone header/footer replacement your theme is not directly supported, add emcp_themer_location( \'header\' ) / emcp_themer_location( \'footer\' ) to your theme, or enable the full-page-takeover fallback (set the emcp_tools_module_themer_force_render option to 1).', 'emcp-tools' ) . '</p></div>';
}
}
@@ -275,7 +275,7 @@ class EMCP_Tools_Themer_CPT {
echo '<div class="notice notice-warning"><p><strong>' . esc_html__( 'EMCP Themer: some templates may have the wrong type', 'emcp-tools' ) . '</strong></p><ul style="list-style:disc;margin-left:22px;">';
foreach ( $flagged as $id => $f ) {
printf(
'<li><a href="%1$s"><strong>%2$s</strong></a> — %3$s</li>',
'<li><a href="%1$s"><strong>%2$s</strong></a>, %3$s</li>',
esc_url( (string) get_edit_post_link( $id ) ),
esc_html( $f['title'] ),
esc_html( $f['reason'] )
@@ -293,12 +293,12 @@ class EMCP_Tools_Themer_CPT {
*/
private static function type_mismatch_reason( int $id, string $type ): ?string {
if ( '' === $type ) {
return __( 'no template type is set, so it will not render — open it and choose a type', 'emcp-tools' );
return __( 'no template type is set, so it will not render, open it and choose a type', 'emcp-tools' );
}
// Content signal: a header/footer holding body-only dynamic elements.
if ( in_array( $type, array( 'header', 'footer' ), true ) && self::has_body_elements( $id ) ) {
/* translators: %s: template type label (Header / Footer) */
return sprintf( __( 'this %s template contains post/archive content elements (title, content, or a posts loop) — those belong in a Single or Archive template', 'emcp-tools' ), ucfirst( $type ) );
return sprintf( __( 'this %s template contains post/archive content elements (title, content, or a posts loop), those belong in a Single or Archive template', 'emcp-tools' ), ucfirst( $type ) );
}
// Title signal: the name names a different type than the one assigned.
$expected = self::type_hint_from_title( (string) get_the_title( $id ) );
+8 -8
View File
@@ -36,7 +36,7 @@ class EMCP_Tools_Themer_Metabox {
public function add(): void {
add_meta_box(
'emcp-themer-conditions',
__( 'EMCP Themer — Display Conditions', 'emcp-tools' ),
__( 'EMCP Themer, Display Conditions', 'emcp-tools' ),
array( $this, 'render' ),
EMCP_Tools_Themer_CPT::POST_TYPE,
'normal',
@@ -99,7 +99,7 @@ class EMCP_Tools_Themer_Metabox {
'chooseGroup' => __( 'Choose…', 'emcp-tools' ),
'all' => __( 'All', 'emcp-tools' ),
'searchType' => __( 'Type 1+ characters…', 'emcp-tools' ),
'noBuilder' => __( 'This template type applies automatically — no display conditions needed.', 'emcp-tools' ),
'noBuilder' => __( 'This template type applies automatically, no display conditions needed.', 'emcp-tools' ),
'proHint' => __( 'Upgrade to EMCP Pro for Exclude rules, per-page / per-category / per-author targeting, and priority.', 'emcp-tools' ),
'remove' => __( 'Remove', 'emcp-tools' ),
),
@@ -120,7 +120,7 @@ class EMCP_Tools_Themer_Metabox {
array(
'templates' => $templates,
'i18n' => array(
'none' => __( '— None (use builder content) —', 'emcp-tools' ),
'none' => __( ', None (use builder content) , ', 'emcp-tools' ),
'chooseType' => __( 'Choose a template type first to list matching PHP templates.', 'emcp-tools' ),
'noMatch' => __( 'No PHP templates match this type yet. Ask your AI agent to create one.', 'emcp-tools' ),
),
@@ -201,7 +201,7 @@ JS;
echo '<div class="emcp-themer-field" style="flex:1 1 260px;min-width:240px;">';
echo '<p style="margin-top:0;"><label for="emcp-themer-type"><strong>' . esc_html__( 'Template type', 'emcp-tools' ) . '</strong> <span style="color:#d63638">*</span></label><br>';
echo '<select id="emcp-themer-type" name="emcp_themer_type" class="emcp-themer-type-select" required style="width:100%;max-width:340px;">';
printf( '<option value="" %s>%s</option>', selected( $type, '', false ), esc_html__( '— Choose a template type —', 'emcp-tools' ) );
printf( '<option value="" %s>%s</option>', selected( $type, '', false ), esc_html__( ', Choose a template type , ', 'emcp-tools' ) );
foreach ( EMCP_Tools_Themer_CPT::TYPES as $t ) {
printf( '<option value="%1$s" %2$s>%3$s</option>', esc_attr( $t ), selected( $type, $t, false ), esc_html( $type_labels[ $t ] ?? ucfirst( $t ) ) );
}
@@ -222,7 +222,7 @@ JS;
$attached,
$has_type ? '' : 'display:none;'
);
printf( '<option value="0">%s</option>', esc_html__( '— None (use builder content) —', 'emcp-tools' ) );
printf( '<option value="0">%s</option>', esc_html__( ', None (use builder content) , ', 'emcp-tools' ) );
if ( $has_type ) {
foreach ( self::eligible_templates( $type ) as $tpl ) {
printf(
@@ -250,13 +250,13 @@ JS;
$conflicts = self::find_conflicts( (int) $post->ID, $type, $cond );
if ( ! empty( $conflicts ) ) {
echo '<div class="notice notice-warning inline emcp-themer-conflict" style="margin:4px 0 14px;padding:8px 12px;">';
echo '<p style="margin:.35em 0;"><strong>' . esc_html__( 'Conflict', 'emcp-tools' ) . '</strong> — ';
echo '<p style="margin:.35em 0;"><strong>' . esc_html__( 'Conflict', 'emcp-tools' ) . '</strong>: ';
echo esc_html(
sprintf(
/* translators: 1: count, 2: template type label */
_n(
'%1$d other %2$s template targets an overlapping condition. Only one template can render a given page — the resolver picks the most specific rule, then the highest priority, then the newest.',
'%1$d other %2$s templates target overlapping conditions. Only one template can render a given page — the resolver picks the most specific rule, then the highest priority, then the newest.',
'%1$d other %2$s template targets an overlapping condition. Only one template can render a given page, the resolver picks the most specific rule, then the highest priority, then the newest.',
'%1$d other %2$s templates target overlapping conditions. Only one template can render a given page, the resolver picks the most specific rule, then the highest priority, then the newest.',
count( $conflicts ),
'emcp-tools'
),
@@ -56,7 +56,7 @@ class EMCP_Tools_Themer_PHP_Admin {
// PHP mode with linting — same editor core WordPress uses for theme/plugin files.
$settings = wp_enqueue_code_editor( array( 'type' => 'text/x-php' ) );
if ( false === $settings ) {
return; // User disabled syntax highlighting — the plain textarea still works.
return; // User disabled syntax highlighting, the plain textarea still works.
}
wp_add_inline_script(
'code-editor',
@@ -347,7 +347,7 @@ class EMCP_Tools_Themer_PHP_Store {
$validation = EMCP_Tools_PHP_Snippet_Validator::validate( $code );
if ( ! $validation['valid'] || ! $validation['safe'] ) {
update_post_meta( $id, self::META_VALIDATION, wp_slash( (string) wp_json_encode( $validation ) ) );
return new WP_Error( 'compile_blocked', __( 'Cannot compile — the template fails validation.', 'emcp-tools' ), array( 'validation' => $validation ) );
return new WP_Error( 'compile_blocked', __( 'Cannot compile, the template fails validation.', 'emcp-tools' ), array( 'validation' => $validation ) );
}
$ensured = self::ensure_dir();
@@ -358,7 +358,7 @@ class EMCP_Tools_Themer_PHP_Store {
$body = EMCP_Tools_PHP_Snippet_Validator::strip_tags( $code );
$func = self::func_name( $id );
$php = "<?php\n"
. "// EMCP Theme PHP Template {$id} — generated; edits fail the integrity check.\n"
. "// EMCP Theme PHP Template {$id}, generated; edits fail the integrity check.\n"
. "if ( ! function_exists( '{$func}' ) ) {\n"
. "\tfunction {$func}() {\n"
. $body . "\n"
@@ -171,7 +171,7 @@ abstract class EMCP_Tools_Themer_Widget_Base extends \Elementor\Widget_Base {
/** Available nav menus for the menu picker. */
private function menu_options(): array {
$out = array( '0' => __( '— Auto (first menu) —', 'emcp-tools' ) );
$out = array( '0' => __( ', Auto (first menu) , ', 'emcp-tools' ) );
$menus = function_exists( 'wp_get_nav_menus' ) ? wp_get_nav_menus() : array();
foreach ( $menus as $menu ) {
$out[ (string) $menu->term_id ] = $menu->name;
+1 -1
Submodule pro updated: ea41e3e270...faa3b13165
+34 -34
View File
@@ -1,84 +1,84 @@
**Page builder:** Elementor
# Chrome & Shine Auto Spa — premium car wash & detailing landing page
# Chrome & Shine Auto Spa, premium car wash & detailing landing page
Design and build a **complete, production-quality landing page** for "Chrome & Shine Auto Spa", a premium car wash and auto detailing center. **You own the design.** If web search is available, research 2–3 genuinely well-designed car-wash/auto-detailing sites first and form a point of view; then decide the layout, composition, and section design yourself. Do not fall back to a generic centered-hero + three-identical-cards template — bring editorial composition, asymmetry, scale contrast, and rhythm. **Vary your hero composition to fit this brand specifically — in particular, do not reach for the overused "one large photo with a smaller photo card overlapping its corner" hero unless it genuinely serves this design; choose an opener you would not use for a coffee shop or a law firm.** The sections below define **what the page must communicate, not how it must look.**
Design and build a **complete, production-quality landing page** for "Chrome & Shine Auto Spa", a premium car wash and auto detailing center. **You own the design.** If web search is available, research 2–3 genuinely well-designed car-wash/auto-detailing sites first and form a point of view; then decide the layout, composition, and section design yourself. Do not fall back to a generic centered-hero + three-identical-cards template, bring editorial composition, asymmetry, scale contrast, and rhythm. **Vary your hero composition to fit this brand specifically, in particular, do not reach for the overused "one large photo with a smaller photo card overlapping its corner" hero unless it genuinely serves this design; choose an opener you would not use for a coffee shop or a law firm.** The sections below define **what the page must communicate, not how it must look.**
## Style guide (constraints, not layout)
**Palette — flat colors only, no gradients:**
**Palette, flat colors only, no gradients:**
| Role | Value |
|---|---|
| Surface, light | `#FFFFFF` (bright white) |
| Surface, dark bands | `#153C8C` (deep cobalt) |
| Ink / headings | `#0D2352` |
| Accent — use sparingly: one word, prices, buttons, icons | `#19B7E0` (cyan) |
| Accent, use sparingly: one word, prices, buttons, icons | `#19B7E0` (cyan) |
| Muted body text | `#5C6B80` |
| Hairlines / dividers | `#E3E9F2` |
**Typography:** Display = **Archivo Black** (fallback: Anton) — heavy, industrial, confident; go bold and big on headlines, uppercase works. Body = **Inter**, 16–18px, relaxed line-height (~1.7–1.8). Short uppercase eyebrows with wide letter-spacing fit the punchy service tone.
**Typography:** Display = **Archivo Black** (fallback: Anton), heavy, industrial, confident; go bold and big on headlines, uppercase works. Body = **Inter**, 16–18px, relaxed line-height (~1.7–1.8). Short uppercase eyebrows with wide letter-spacing fit the punchy service tone.
**Feel:** fast, gleaming, punchy. Water-droplet and wet-paint photography supply the shine — no effects needed. Bold scale contrast between huge headlines and tight supporting copy; crisp hairline dividers over box shadows; sharp or barely-rounded corners.
**Feel:** fast, gleaming, punchy. Water-droplet and wet-paint photography supply the shine, no effects needed. Bold scale contrast between huge headlines and tight supporting copy; crisp hairline dividers over box shadows; sharp or barely-rounded corners.
**Motion:** energetic but disciplined — quick entrance reveals and image hovers. Use only what the chosen builder provides natively; **no custom JavaScript and no hand-rolled motion CSS.**
**Motion:** energetic but disciplined, quick entrance reveals and image hovers. Use only what the chosen builder provides natively; **no custom JavaScript and no hand-rolled motion CSS.**
## Design signature (make this page distinct — adapt as you see fit)
## Design signature (make this page distinct, adapt as you see fit)
These are compositional cues, not layout mechanics. Use them to give this car wash its own look rather than a generic template:
- **Hero:** open on a full-width horizontal band of a car mid-wash with water caught in motion, one enormous single-word headline set across it and the actions tucked into a corner — a wide, fast, cinematic strip, never a photo with a smaller photo card overlapping its corner.
- **Wash packages:** present the three tiers as a bold comparison ladder with Premium Detail visibly enlarged as the anchor and prices in the cyan accent — engineered, not a card dump.
- **Hero:** open on a full-width horizontal band of a car mid-wash with water caught in motion, one enormous single-word headline set across it and the actions tucked into a corner, a wide, fast, cinematic strip, never a photo with a smaller photo card overlapping its corner.
- **Wash packages:** present the three tiers as a bold comparison ladder with Premium Detail visibly enlarged as the anchor and prices in the cyan accent, engineered, not a card dump.
- **Big numerals:** run the proof stats (15,000+, 4.9★, 100%) as huge industrial figures on the cobalt band so credibility lands instantly.
- **Shape language:** sharp or barely-rounded corners, crisp hairline dividers, and cyan used only on actions and prices.
## Sections — what each must communicate (design them your way)
## Sections, what each must communicate (design them your way)
1. **Hero** — a gleaming first impression; the brand name, a confident headline, a supporting line about hand wash + detailing + walk-ins welcome, primary + secondary actions (book / call), hours at a glance.
2. **Proof bar** — the four trust stats below, presented as instant credibility.
3. **Services** — the four core services below with their descriptions; each should read as a distinct craft, not four identical boxes.
4. **Wash packages** — the three tiers below with full inclusion lists and prices; make Premium Detail the clear anchor choice.
5. **About** — 10 years of craftsmanship, trained and certified technicians, eco-friendly products, fully insured & bonded, satisfaction guaranteed.
6. **Add-ons** — the three upgrade services below with prices; frame them as easy yeses at booking time.
7. **Unlimited Wash Club** — the membership pitch: wash as often as you want, plans from $39/month; one strong join action.
8. **Testimonials** — the three customer quotes below with names and vehicles.
9. **Book your wash** — a working booking-request form (name, phone, email, vehicle type, service or package, preferred date/time) built with the builder's native form element, with the phone number given equal prominence as an alternative. If the builder has no form element available, use a clearly labeled call/email call-to-action instead of omitting this.
10. **Visit + Footer** — address, hours, phone, email, "open 7 days a week", a map if the builder supports one; then brand + tagline, quick links, contact, copyright.
1. **Hero**: a gleaming first impression; the brand name, a confident headline, a supporting line about hand wash + detailing + walk-ins welcome, primary + secondary actions (book / call), hours at a glance.
2. **Proof bar**: the four trust stats below, presented as instant credibility.
3. **Services**: the four core services below with their descriptions; each should read as a distinct craft, not four identical boxes.
4. **Wash packages**: the three tiers below with full inclusion lists and prices; make Premium Detail the clear anchor choice.
5. **About**: 10 years of craftsmanship, trained and certified technicians, eco-friendly products, fully insured & bonded, satisfaction guaranteed.
6. **Add-ons**: the three upgrade services below with prices; frame them as easy yeses at booking time.
7. **Unlimited Wash Club**: the membership pitch: wash as often as you want, plans from $39/month; one strong join action.
8. **Testimonials**: the three customer quotes below with names and vehicles.
9. **Book your wash**: a working booking-request form (name, phone, email, vehicle type, service or package, preferred date/time) built with the builder's native form element, with the phone number given equal prominence as an alternative. If the builder has no form element available, use a clearly labeled call/email call-to-action instead of omitting this.
10. **Visit + Footer**: address, hours, phone, email, "open 7 days a week", a map if the builder supports one; then brand + tagline, quick links, contact, copyright.
## Content facts (use these verbatim)
**Proof stats:** 15,000+ cars washed monthly · 4.9★ Google rating · 100% hand wash · eco-friendly products
**Services:** Exterior Hand Wash — premium hand wash with pH-balanced foam, wheel cleaning, and towel dry · Full Detail — complete interior and exterior detail including clay bar, polish, and wax · Ceramic Coating — long-lasting nano-ceramic protection for ultimate paint defense · Interior Deep Clean — steam cleaning, leather conditioning, carpet extraction, odor removal
**Services:** Exterior Hand Wash, premium hand wash with pH-balanced foam, wheel cleaning, and towel dry · Full Detail, complete interior and exterior detail including clay bar, polish, and wax · Ceramic Coating, long-lasting nano-ceramic protection for ultimate paint defense · Interior Deep Clean, steam cleaning, leather conditioning, carpet extraction, odor removal
**Packages:** Express Wash — $25 — exterior hand wash, wheel clean, tire shine, windows, air freshener · Premium Detail — $89 — full exterior wash, interior vacuum & wipe, dashboard treatment, tire dressing, scent treatment · Ultimate Spa — $179 — everything in Premium plus clay bar, paint correction, ceramic spray sealant, leather conditioning, engine bay clean
**Packages:** Express Wash, $25, exterior hand wash, wheel clean, tire shine, windows, air freshener · Premium Detail, $89, full exterior wash, interior vacuum & wipe, dashboard treatment, tire dressing, scent treatment · Ultimate Spa, $179, everything in Premium plus clay bar, paint correction, ceramic spray sealant, leather conditioning, engine bay clean
**Add-ons:** Headlight Restoration — $45 — crystal-clear headlights for better visibility and appearance · Paint Protection Film — from $299 — invisible shield against rock chips, scratches, and UV damage · Odor Elimination — $35 — ozone treatment to completely neutralize stubborn odors
**Add-ons:** Headlight Restoration, $45, crystal-clear headlights for better visibility and appearance · Paint Protection Film, from $299, invisible shield against rock chips, scratches, and UV damage · Odor Elimination, $35, ozone treatment to completely neutralize stubborn odors
**Membership:** Unlimited Wash Club — plans starting at $39/month
**Membership:** Unlimited Wash Club, plans starting at $39/month
**Testimonials:** "My Tesla has never looked this good. The ceramic coating is absolutely worth it." — Mark R., Tesla Model 3 · "I've tried every car wash in town. Chrome & Shine is hands down the best." — Sarah K., BMW X5 · "The interior deep clean saved my seats after a road trip with two kids and a dog!" — James T., Honda Odyssey
**Testimonials:** "My Tesla has never looked this good. The ceramic coating is absolutely worth it.", Mark R., Tesla Model 3 · "I've tried every car wash in town. Chrome & Shine is hands down the best.", Sarah K., BMW X5 · "The interior deep clean saved my seats after a road trip with two kids and a dog!", James T., Honda Odyssey
**Visit:** 2280 Coastal Hwy, San Diego, CA · Mon–Sat 7AM–7PM, Sun 8AM–5PM · open 7 days a week · (555) 789-2345 · wash@chromeandshine.com · est. 2016
## Standards (non-negotiable)
### Accessibility — WCAG 2.1 AA
- Text contrast ≥ 4.5:1 (≥ 3:1 for large text) against its actual background — check text over photos and dark bands.
### Accessibility, WCAG 2.1 AA
- Text contrast ≥ 4.5:1 (≥ 3:1 for large text) against its actual background, check text over photos and dark bands.
- Exactly one H1; headings descend logically (no skipped levels for styling reasons).
- Descriptive alt text on every image; meaningful link/button labels (never "click here").
- Comfortable touch targets on buttons and links; readable body size (≥ 16px); every form field has a visible, associated label.
### Images — real photography in every slot
### Images, real photography in every slot
- Source real photos from **Unsplash, Pexels, or Pixabay** (use the available stock-image search/sideload tools if present, otherwise direct source URLs). Never leave an image slot empty; never use placeholder URLs.
- Curate for a consistent crisp, high-gloss grade of water droplets and wet paint that matches the palette; pick images that share a mood, not six random car photos.
### Icons — one consistent SVG set
- Use inline SVG icons in a single consistent style — Lucide/Feather spec: `viewBox="0 0 24 24"`, `fill="none"`, `stroke="currentColor"`, `stroke-width="1.75"`, round caps/joins. Generate what you need (droplet, car, sparkle, shield, spray, clock, star, check, map-pin…).
### Icons, one consistent SVG set
- Use inline SVG icons in a single consistent style, Lucide/Feather spec: `viewBox="0 0 24 24"`, `fill="none"`, `stroke="currentColor"`, `stroke-width="1.75"`, round caps/joins. Generate what you need (droplet, car, sparkle, shield, spray, clock, star, check, map-pin…).
- Do not use the builder's bundled icon font/library, and no emoji as icons.
### Builder-native construction
- Build with the chosen builder's **native elements/widgets/blocks** and its native styling, spacing, and motion features. Inspect the builder's available elements and their options first if tools for that exist.
- Raw HTML/custom code embedded inside a visual builder is a **last resort** for a micro-detail that is genuinely impossible natively — never for whole sections. (If the chosen target IS plain HTML/CSS, write clean semantic HTML with modern CSS instead.)
- Raw HTML/custom code embedded inside a visual builder is a **last resort** for a micro-detail that is genuinely impossible natively, never for whole sections. (If the chosen target IS plain HTML/CSS, write clean semantic HTML with modern CSS instead.)
### Completeness — a half-built page is a failure
- Build and **fully populate one section at a time**: real headline, real copy, every package with its full inclusion list and price, every add-on and stat placed, every quote written, every image placed — then move on. Never scaffold empty containers to "fill later."
### Completeness, a half-built page is a failure
- Build and **fully populate one section at a time**: real headline, real copy, every package with its full inclusion list and price, every add-on and stat placed, every quote written, every image placed, then move on. Never scaffold empty containers to "fill later."
- When finished, walk the entire page and fix any empty element, placeholder text, missing image, or contrast failure. Publish as a **draft**.
+32 -32
View File
@@ -1,55 +1,55 @@
**Page builder:** Elementor
# BrightSmile Dental — family dental clinic landing page
# BrightSmile Dental, family dental clinic landing page
Design and build a **complete, production-quality landing page** for "BrightSmile Dental", a modern family dental clinic. **You own the design.** If web search is available, research 2–3 genuinely well-designed dental clinic sites first and form a point of view; then decide the layout, composition, and section design yourself. Do not fall back to a generic centered-hero + three-identical-cards template — bring editorial composition, asymmetry, scale contrast, and rhythm. **Vary your hero composition to fit this brand specifically — in particular, do not reach for the overused "one large photo with a smaller photo card overlapping its corner" hero unless it genuinely serves this design; choose an opener you would not use for a coffee shop or a law firm.** The sections below define **what the page must communicate, not how it must look.**
Design and build a **complete, production-quality landing page** for "BrightSmile Dental", a modern family dental clinic. **You own the design.** If web search is available, research 2–3 genuinely well-designed dental clinic sites first and form a point of view; then decide the layout, composition, and section design yourself. Do not fall back to a generic centered-hero + three-identical-cards template, bring editorial composition, asymmetry, scale contrast, and rhythm. **Vary your hero composition to fit this brand specifically, in particular, do not reach for the overused "one large photo with a smaller photo card overlapping its corner" hero unless it genuinely serves this design; choose an opener you would not use for a coffee shop or a law firm.** The sections below define **what the page must communicate, not how it must look.**
## Style guide (constraints, not layout)
**Palette — flat colors only, no gradients:**
**Palette, flat colors only, no gradients:**
| Role | Value |
|---|---|
| Surface, light | `#FFFFFF` (bright white) |
| Surface, dark bands | `#1C2B3A` (navy slate) |
| Ink / headings | `#16283A` |
| Accent — use sparingly: one word, prices, buttons, icons | `#2BB3C0` (aqua sky) |
| Accent, use sparingly: one word, prices, buttons, icons | `#2BB3C0` (aqua sky) |
| Muted body text | `#52657A` |
| Hairlines / dividers | `#E3EAF0` |
**Typography:** Display = **Plus Jakarta Sans** (fallback: Nunito Sans) — rounded-but-professional, friendly without being childish; headlines in sentence or title case, never shouting. Body = **Inter**, 16–18px, relaxed line-height (~1.7–1.8). Small uppercase eyebrows with wide letter-spacing work well.
**Typography:** Display = **Plus Jakarta Sans** (fallback: Nunito Sans), rounded-but-professional, friendly without being childish; headlines in sentence or title case, never shouting. Body = **Inter**, 16–18px, relaxed line-height (~1.7–1.8). Small uppercase eyebrows with wide letter-spacing work well.
**Feel:** fresh reassurance — bright, hygienic, friendly-professional. Calm should come from whitespace and clean type, not effects. Hairline dividers over box shadows; soft 10px radii on images and cards.
**Feel:** fresh reassurance, bright, hygienic, friendly-professional. Calm should come from whitespace and clean type, not effects. Hairline dividers over box shadows; soft 10px radii on images and cards.
**Motion:** gentle and restrained — subtle entrance reveals and image hovers. Use only what the chosen builder provides natively; **no custom JavaScript and no hand-rolled motion CSS.**
**Motion:** gentle and restrained, subtle entrance reveals and image hovers. Use only what the chosen builder provides natively; **no custom JavaScript and no hand-rolled motion CSS.**
## Design signature (make this page distinct — adapt as you see fit)
## Design signature (make this page distinct, adapt as you see fit)
These are compositional cues, not layout mechanics. Use them to give this clinic its own look rather than a generic template:
- **Hero:** lead with a single calm, high-trust photograph (a real, warm dentist-and-patient moment) with the headline and a compact **"Book a visit"** card set alongside it — a composed, uncluttered opener, not stacked or overlapping image cards.
- **Services:** present as a clean, scannable index — a quiet list with hairline dividers or an asymmetric two-column layout — rather than six identical shadowed cards.
- **Hero:** lead with a single calm, high-trust photograph (a real, warm dentist-and-patient moment) with the headline and a compact **"Book a visit"** card set alongside it, a composed, uncluttered opener, not stacked or overlapping image cards.
- **Services:** present as a clean, scannable index, a quiet list with hairline dividers or an asymmetric two-column layout, rather than six identical shadowed cards.
- **Reassurance:** turn the trust stats into oversized, friendly numerals on the navy band so the proof reads at a glance.
- **Shape language:** consistent soft radii and generous daylight whitespace; let the aqua accent appear only on actions and a few key numbers.
## Sections — what each must communicate (design them your way)
## Sections, what each must communicate (design them your way)
1. **Hero** — warmth and competence for the whole family; the brand name, a memorable headline, a supporting line, primary + secondary actions, and the fact that new patients and same-day appointments are welcome.
2. **Trust strip** — the four differentiators (years of experience, happy patients, same-day appointments, insurance accepted).
3. **Services** — all 6 services below with short, reassuring descriptions covering routine to emergency care.
4. **About the practice** — why visiting is actually pleasant: modern office, gentle approach, latest technology, family-owned — anchored by the three stats.
5. **Team** — the four dentists below with name, specialty, and a one-line bio each; write the bios yourself.
6. **Pricing & insurance** — the three transparent offers below plus the accepted-insurance line, so cost anxiety is answered head-on.
7. **Testimonials** — three distinct, believable patient quotes with names; write them yourself.
8. **Book a visit** — a working appointment-request form (full name, phone, email, preferred date/time, reason for visit, optional message) built with the builder's native form element, with the phone number given equal prominence as an alternative. If the builder has no form element available, use a clearly labeled call/email call-to-action instead of omitting this.
9. **Visit** — address, hours, phone, email, the 24/7 emergency line, and a map if the builder supports one.
10. **Footer** — brand + tagline, quick links, contact, copyright.
1. **Hero**: warmth and competence for the whole family; the brand name, a memorable headline, a supporting line, primary + secondary actions, and the fact that new patients and same-day appointments are welcome.
2. **Trust strip**: the four differentiators (years of experience, happy patients, same-day appointments, insurance accepted).
3. **Services**: all 6 services below with short, reassuring descriptions covering routine to emergency care.
4. **About the practice**: why visiting is actually pleasant: modern office, gentle approach, latest technology, family-owned, anchored by the three stats.
5. **Team**: the four dentists below with name, specialty, and a one-line bio each; write the bios yourself.
6. **Pricing & insurance**: the three transparent offers below plus the accepted-insurance line, so cost anxiety is answered head-on.
7. **Testimonials**: three distinct, believable patient quotes with names; write them yourself.
8. **Book a visit**: a working appointment-request form (full name, phone, email, preferred date/time, reason for visit, optional message) built with the builder's native form element, with the phone number given equal prominence as an alternative. If the builder has no form element available, use a clearly labeled call/email call-to-action instead of omitting this.
9. **Visit**: address, hours, phone, email, the 24/7 emergency line, and a map if the builder supports one.
10. **Footer**: brand + tagline, quick links, contact, copyright.
## Content facts (use these verbatim)
**Services:** General Dentistry — checkups, cleanings, fillings · Cosmetic Dentistry — whitening, veneers, bonding · Orthodontics — braces, Invisalign, retainers · Dental Implants — single, bridge, full arch · Pediatric Dentistry — kid-friendly care, sealants · Emergency Dental — same-day pain relief and repair
**Services:** General Dentistry, checkups, cleanings, fillings · Cosmetic Dentistry, whitening, veneers, bonding · Orthodontics, braces, Invisalign, retainers · Dental Implants, single, bridge, full arch · Pediatric Dentistry, kid-friendly care, sealants · Emergency Dental, same-day pain relief and repair
**Team:** Dr. Sarah Mitchell — General & Cosmetic Dentistry · Dr. James Chen — Orthodontics · Dr. Priya Patel — Pediatric Dentistry · Dr. Michael Torres — Oral Surgery
**Team:** Dr. Sarah Mitchell, General & Cosmetic Dentistry · Dr. James Chen, Orthodontics · Dr. Priya Patel, Pediatric Dentistry · Dr. Michael Torres, Oral Surgery
**Pricing:** New Patient Special — exam, X-rays, cleaning, treatment plan — $99 · Teeth Whitening — professional whitening, custom trays, touch-up kit — $299 · Dental Implant — consultation, implant + crown, follow-up care — from $1,499
**Pricing:** New Patient Special, exam, X-rays, cleaning, treatment plan, $99 · Teeth Whitening, professional whitening, custom trays, touch-up kit, $299 · Dental Implant, consultation, implant + crown, follow-up care, from $1,499
**Insurance:** We accept most major insurance plans including Delta Dental, Cigna, Aetna, MetLife, and more.
@@ -59,24 +59,24 @@ These are compositional cues, not layout mechanics. Use them to give this clinic
## Standards (non-negotiable)
### Accessibility — WCAG 2.1 AA
- Text contrast ≥ 4.5:1 (≥ 3:1 for large text) against its actual background — check text over photos and dark bands.
### Accessibility, WCAG 2.1 AA
- Text contrast ≥ 4.5:1 (≥ 3:1 for large text) against its actual background, check text over photos and dark bands.
- Exactly one H1; headings descend logically (no skipped levels for styling reasons).
- Descriptive alt text on every image; meaningful link/button labels (never "click here").
- Comfortable touch targets on buttons and links; readable body size (≥ 16px); every form field has a visible, associated label.
### Images — real photography in every slot
### Images, real photography in every slot
- Source real photos from **Unsplash, Pexels, or Pixabay** (use the available stock-image search/sideload tools if present, otherwise direct source URLs). Never leave an image slot empty; never use placeholder URLs.
- Curate for a consistent bright, airy, daylight grade that matches the palette; pick images that share a mood, not six random dental photos.
### Icons — one consistent SVG set
- Use inline SVG icons in a single consistent style — Lucide/Feather spec: `viewBox="0 0 24 24"`, `fill="none"`, `stroke="currentColor"`, `stroke-width="1.75"`, round caps/joins. Generate what you need (tooth, smile, shield-plus, sparkle, calendar, clock, phone, map-pin…).
### Icons, one consistent SVG set
- Use inline SVG icons in a single consistent style, Lucide/Feather spec: `viewBox="0 0 24 24"`, `fill="none"`, `stroke="currentColor"`, `stroke-width="1.75"`, round caps/joins. Generate what you need (tooth, smile, shield-plus, sparkle, calendar, clock, phone, map-pin…).
- Do not use the builder's bundled icon font/library, and no emoji as icons.
### Builder-native construction
- Build with the chosen builder's **native elements/widgets/blocks** and its native styling, spacing, and motion features. Inspect the builder's available elements and their options first if tools for that exist.
- Raw HTML/custom code embedded inside a visual builder is a **last resort** for a micro-detail that is genuinely impossible natively — never for whole sections. (If the chosen target IS plain HTML/CSS, write clean semantic HTML with modern CSS instead.)
- Raw HTML/custom code embedded inside a visual builder is a **last resort** for a micro-detail that is genuinely impossible natively, never for whole sections. (If the chosen target IS plain HTML/CSS, write clean semantic HTML with modern CSS instead.)
### Completeness — a half-built page is a failure
- Build and **fully populate one section at a time**: real headline, real copy, every service described, every dentist named with a bio, every price stated, every quote written, every image placed — then move on. Never scaffold empty containers to "fill later."
### Completeness, a half-built page is a failure
- Build and **fully populate one section at a time**: real headline, real copy, every service described, every dentist named with a bio, every price stated, every quote written, every image placed, then move on. Never scaffold empty containers to "fill later."
- When finished, walk the entire page and fix any empty element, placeholder text, missing image, or contrast failure. Publish as a **draft**.
+30 -30
View File
@@ -1,53 +1,53 @@
**Page builder:** Elementor
# Velvet & Co. — upscale hair salon landing page
# Velvet & Co., upscale hair salon landing page
Design and build a **complete, production-quality landing page** for "Velvet & Co.", an upscale hair salon and styling studio. **You own the design.** If web search is available, research 2–3 genuinely well-designed salon/beauty-studio sites first and form a point of view; then decide the layout, composition, and section design yourself. Do not fall back to a generic centered-hero + three-identical-cards template — bring editorial composition, asymmetry, scale contrast, and rhythm. **Vary your hero composition to fit this brand specifically — in particular, do not reach for the overused "one large photo with a smaller photo card overlapping its corner" hero unless it genuinely serves this design; choose an opener you would not use for a coffee shop or a law firm.** The sections below define **what the page must communicate, not how it must look.**
Design and build a **complete, production-quality landing page** for "Velvet & Co.", an upscale hair salon and styling studio. **You own the design.** If web search is available, research 2–3 genuinely well-designed salon/beauty-studio sites first and form a point of view; then decide the layout, composition, and section design yourself. Do not fall back to a generic centered-hero + three-identical-cards template, bring editorial composition, asymmetry, scale contrast, and rhythm. **Vary your hero composition to fit this brand specifically, in particular, do not reach for the overused "one large photo with a smaller photo card overlapping its corner" hero unless it genuinely serves this design; choose an opener you would not use for a coffee shop or a law firm.** The sections below define **what the page must communicate, not how it must look.**
## Style guide (constraints, not layout)
**Palette — flat colors only, no gradients:**
**Palette, flat colors only, no gradients:**
| Role | Value |
|---|---|
| Surface, light | `#F7F1EE` (ivory blush) |
| Surface, dark bands | `#171215` (noir) |
| Ink / headings | `#1E181B` |
| Accent — use sparingly: one word, prices, buttons, icons | `#B34A5E` (deep rose) |
| Accent, use sparingly: one word, prices, buttons, icons | `#B34A5E` (deep rose) |
| Muted body text | `#8A7A7E` |
| Hairlines / dividers | `#E8DCD6` |
**Typography:** Display = **Libre Caslon Display** (fallback: Cormorant Garamond) — refined, editorial serif with fashion-magazine poise; elegant weight, never heavy. Body = **Inter**, 16–18px, relaxed line-height (~1.7–1.8). Slim uppercase eyebrows with very wide letter-spacing read like a masthead.
**Typography:** Display = **Libre Caslon Display** (fallback: Cormorant Garamond), refined, editorial serif with fashion-magazine poise; elegant weight, never heavy. Body = **Inter**, 16–18px, relaxed line-height (~1.7–1.8). Slim uppercase eyebrows with very wide letter-spacing read like a masthead.
**Feel:** chic, glossy, fashion-editorial. Polished portraiture and salon photography carry the glamour; the ivory-blush surface stays quiet so hair is the hero. Refined hairline dividers over box shadows; restrained radii; luxury lives in whitespace and typographic precision.
**Motion:** graceful and understated — soft entrance reveals and image hovers. Use only what the chosen builder provides natively; **no custom JavaScript and no hand-rolled motion CSS.**
**Motion:** graceful and understated, soft entrance reveals and image hovers. Use only what the chosen builder provides natively; **no custom JavaScript and no hand-rolled motion CSS.**
## Design signature (make this page distinct — adapt as you see fit)
## Design signature (make this page distinct, adapt as you see fit)
These are compositional cues, not layout mechanics. Use them to give this hair salon its own look rather than a generic template:
- **Hero:** treat the opener as a stacked editorial masthead — the salon name set huge like a fashion-magazine cover title, a thin rule, the "Where Beauty Meets Artistry" promise beneath, with a single polished portrait held to one side; a cover, not a photo with a smaller photo card on its corner.
- **Services:** compose the six services as a designed price menu — an elegant column with hairline dividers and right-aligned starting prices — rather than a card grid.
- **Hero:** treat the opener as a stacked editorial masthead, the salon name set huge like a fashion-magazine cover title, a thin rule, the "Where Beauty Meets Artistry" promise beneath, with a single polished portrait held to one side; a cover, not a photo with a smaller photo card on its corner.
- **Services:** compose the six services as a designed price menu, an elegant column with hairline dividers and right-aligned starting prices, rather than a card grid.
- **Masthead rules:** carry slim uppercase eyebrows and hairline rules through every section like a running magazine masthead.
- **Shape language:** restrained radii, quiet ivory-blush whitespace, and deep rose reserved for one word, prices, and actions.
## Sections — what each must communicate (design them your way)
## Sections, what each must communicate (design them your way)
1. **Hero** — instant editorial glamour; the brand name, the "Where Beauty Meets Artistry" promise, a supporting line, primary + secondary actions (book appointment / view services).
2. **Services** — all 6 services below with descriptions and starting prices; treat the service list as a designed price menu, not a card dump.
3. **About** — the salon's story and craft philosophy, anchored by the three proof stats: 12+ years · 25K+ clients · 8 expert stylists.
4. **The team** — the four stylists below with their specialties; each should feel individually bookable.
5. **Portfolio** — a curated gallery of finished work that proves the craft; consistency of grade matters more than quantity.
6. **Retail** — the salon carries professional product lines below; convey take-the-salon-home quality.
7. **Testimonials** — three distinct, believable client quotes with names; write them yourself.
8. **Call to action** — one strong closing moment: your best hair day awaits, book the transformation.
9. **Book an appointment** — a working appointment-request form (name, phone, email, service, preferred stylist, preferred date/time) built with the builder's native form element, with the phone number given equal prominence as an alternative. If the builder has no form element available, use a clearly labeled call/email call-to-action instead of omitting this.
10. **Visit + Footer** — address, hours, phone, email, a map if the builder supports one; then brand + tagline, quick links, contact, copyright.
1. **Hero**: instant editorial glamour; the brand name, the "Where Beauty Meets Artistry" promise, a supporting line, primary + secondary actions (book appointment / view services).
2. **Services**: all 6 services below with descriptions and starting prices; treat the service list as a designed price menu, not a card dump.
3. **About**: the salon's story and craft philosophy, anchored by the three proof stats: 12+ years · 25K+ clients · 8 expert stylists.
4. **The team**: the four stylists below with their specialties; each should feel individually bookable.
5. **Portfolio**: a curated gallery of finished work that proves the craft; consistency of grade matters more than quantity.
6. **Retail**: the salon carries professional product lines below; convey take-the-salon-home quality.
7. **Testimonials**: three distinct, believable client quotes with names; write them yourself.
8. **Call to action**: one strong closing moment: your best hair day awaits, book the transformation.
9. **Book an appointment**: a working appointment-request form (name, phone, email, service, preferred stylist, preferred date/time) built with the builder's native form element, with the phone number given equal prominence as an alternative. If the builder has no form element available, use a clearly labeled call/email call-to-action instead of omitting this.
10. **Visit + Footer**: address, hours, phone, email, a map if the builder supports one; then brand + tagline, quick links, contact, copyright.
## Content facts (use these verbatim)
**Services:** Cut & Style — precision cut with consultation, wash, and finish — from $65 · Color & Highlights — full color or dimensional highlights — from $120 · Balayage & Ombré — hand-painted, lived-in color — from $180 · Keratin Treatment — smoothing treatment for frizz-free weeks — from $250 · Bridal & Special Occasion — trials, updos, and day-of styling — from $150 · Blowout & Styling — wash, blowout, and finish — from $45
**Services:** Cut & Style, precision cut with consultation, wash, and finish, from $65 · Color & Highlights, full color or dimensional highlights, from $120 · Balayage & Ombré, hand-painted, lived-in color, from $180 · Keratin Treatment, smoothing treatment for frizz-free weeks, from $250 · Bridal & Special Occasion, trials, updos, and day-of styling, from $150 · Blowout & Styling, wash, blowout, and finish, from $45
**Team:** Mia Chen — Color Specialist · James Hart — Creative Director · Sofia Reyes — Bridal Expert · Aisha Patel — Texture & Curls
**Team:** Mia Chen, Color Specialist · James Hart, Creative Director · Sofia Reyes, Bridal Expert · Aisha Patel, Texture & Curls
**Stats:** 12+ years · 25K+ happy clients · 8 expert stylists
@@ -59,24 +59,24 @@ These are compositional cues, not layout mechanics. Use them to give this hair s
## Standards (non-negotiable)
### Accessibility — WCAG 2.1 AA
- Text contrast ≥ 4.5:1 (≥ 3:1 for large text) against its actual background — check text over photos and dark bands.
### Accessibility, WCAG 2.1 AA
- Text contrast ≥ 4.5:1 (≥ 3:1 for large text) against its actual background, check text over photos and dark bands.
- Exactly one H1; headings descend logically (no skipped levels for styling reasons).
- Descriptive alt text on every image; meaningful link/button labels (never "click here").
- Comfortable touch targets on buttons and links; readable body size (≥ 16px); every form field has a visible, associated label.
### Images — real photography in every slot
### Images, real photography in every slot
- Source real photos from **Unsplash, Pexels, or Pixabay** (use the available stock-image search/sideload tools if present, otherwise direct source URLs). Never leave an image slot empty; never use placeholder URLs.
- Curate for a consistent glossy, fashion-editorial grade of portraiture and salon interiors that matches the palette; pick images that share a mood, not six random salon photos.
### Icons — one consistent SVG set
- Use inline SVG icons in a single consistent style — Lucide/Feather spec: `viewBox="0 0 24 24"`, `fill="none"`, `stroke="currentColor"`, `stroke-width="1.75"`, round caps/joins. Generate what you need (scissors, sparkle, calendar, clock, crown, heart, star, map-pin…).
### Icons, one consistent SVG set
- Use inline SVG icons in a single consistent style, Lucide/Feather spec: `viewBox="0 0 24 24"`, `fill="none"`, `stroke="currentColor"`, `stroke-width="1.75"`, round caps/joins. Generate what you need (scissors, sparkle, calendar, clock, crown, heart, star, map-pin…).
- Do not use the builder's bundled icon font/library, and no emoji as icons.
### Builder-native construction
- Build with the chosen builder's **native elements/widgets/blocks** and its native styling, spacing, and motion features. Inspect the builder's available elements and their options first if tools for that exist.
- Raw HTML/custom code embedded inside a visual builder is a **last resort** for a micro-detail that is genuinely impossible natively — never for whole sections. (If the chosen target IS plain HTML/CSS, write clean semantic HTML with modern CSS instead.)
- Raw HTML/custom code embedded inside a visual builder is a **last resort** for a micro-detail that is genuinely impossible natively, never for whole sections. (If the chosen target IS plain HTML/CSS, write clean semantic HTML with modern CSS instead.)
### Completeness — a half-built page is a failure
- Build and **fully populate one section at a time**: real headline, real copy, every service with its starting price, every stylist named, every quote written, every image placed — then move on. Never scaffold empty containers to "fill later."
### Completeness, a half-built page is a failure
- Build and **fully populate one section at a time**: real headline, real copy, every service with its starting price, every stylist named, every quote written, every image placed, then move on. Never scaffold empty containers to "fill later."
- When finished, walk the entire page and fix any empty element, placeholder text, missing image, or contrast failure. Publish as a **draft**.
+30 -30
View File
@@ -1,53 +1,53 @@
**Page builder:** Elementor
# SecureKey Locksmith & Security — local service business landing page
# SecureKey Locksmith & Security, local service business landing page
Design and build a **complete, production-quality landing page** for "SecureKey Locksmith & Security", a licensed local locksmith and home-security service. This prompt is the versatile, adapt-me template — swap the business name, services, and palette to fit any local service company. **You own the design.** If web search is available, research 2–3 genuinely well-designed local-service/trades sites first and form a point of view; then decide the layout, composition, and section design yourself. Do not fall back to a generic centered-hero + three-identical-cards template — bring editorial composition, asymmetry, scale contrast, and rhythm. **Vary your hero composition to fit this brand specifically — in particular, do not reach for the overused "one large photo with a smaller photo card overlapping its corner" hero unless it genuinely serves this design; choose an opener you would not use for a coffee shop or a law firm.** The sections below define **what the page must communicate, not how it must look.**
Design and build a **complete, production-quality landing page** for "SecureKey Locksmith & Security", a licensed local locksmith and home-security service. This prompt is the versatile, adapt-me template, swap the business name, services, and palette to fit any local service company. **You own the design.** If web search is available, research 2–3 genuinely well-designed local-service/trades sites first and form a point of view; then decide the layout, composition, and section design yourself. Do not fall back to a generic centered-hero + three-identical-cards template, bring editorial composition, asymmetry, scale contrast, and rhythm. **Vary your hero composition to fit this brand specifically, in particular, do not reach for the overused "one large photo with a smaller photo card overlapping its corner" hero unless it genuinely serves this design; choose an opener you would not use for a coffee shop or a law firm.** The sections below define **what the page must communicate, not how it must look.**
## Style guide (constraints, not layout)
**Palette — flat colors only, no gradients:**
**Palette, flat colors only, no gradients:**
| Role | Value |
|---|---|
| Surface, light | `#FBFAF8` (warm white) |
| Surface, dark bands | `#232830` (deep slate) |
| Ink / headings | `#232830` |
| Accent — use sparingly: one word, key numbers, buttons, icons | `#D97706` (warm amber) |
| Accent, use sparingly: one word, key numbers, buttons, icons | `#D97706` (warm amber) |
| Muted body text | `#6E6A63` |
| Hairlines / dividers | `#E8E5DF` |
**Typography:** Display = **Inter Tight** (fallback: Inter) — clean, confident grotesque; strong weights and tight leading on headlines, real scale contrast between levels. Body = **Inter**, 16–18px, relaxed line-height (~1.6–1.7). Small uppercase eyebrows with wide letter-spacing are on-brand.
**Typography:** Display = **Inter Tight** (fallback: Inter), clean, confident grotesque; strong weights and tight leading on headlines, real scale contrast between levels. Body = **Inter**, 16–18px, relaxed line-height (~1.6–1.7). Small uppercase eyebrows with wide letter-spacing are on-brand.
**Feel:** clean, confident, timeless — the least themed of designs, but never bland: editorial composition, deliberate scale contrast, generous whitespace. Hairline dividers over box shadows; modest radii (6–10px). Trust is the product — let numbers, plain language, and real work photos carry it.
**Feel:** clean, confident, timeless, the least themed of designs, but never bland: editorial composition, deliberate scale contrast, generous whitespace. Hairline dividers over box shadows; modest radii (6–10px). Trust is the product, let numbers, plain language, and real work photos carry it.
**Motion:** minimal and purposeful — subtle entrance reveals only; a service business should feel steady, not showy. Use only what the chosen builder provides natively; **no custom JavaScript and no hand-rolled motion CSS.**
**Motion:** minimal and purposeful, subtle entrance reveals only; a service business should feel steady, not showy. Use only what the chosen builder provides natively; **no custom JavaScript and no hand-rolled motion CSS.**
## Design signature (make this page distinct — adapt as you see fit)
## Design signature (make this page distinct, adapt as you see fit)
These are compositional cues, not layout mechanics. Use them to give this service business its own look rather than a generic template:
- **Hero:** a type-first opener with no photo above the fold — a trust-forward headline and the phone number as the focal element, carried by deliberate scale and a strong horizontal rule system rather than a hero image.
- **Hero:** a type-first opener with no photo above the fold, a trust-forward headline and the phone number as the focal element, carried by deliberate scale and a strong horizontal rule system rather than a hero image.
- **Services:** present the six services as a plain-language problem index on hairline dividers, so a customer finds their problem in seconds, rather than six identical shadowed cards.
- **Trust numerals:** set the four stats as oversized numerals on the deep-slate band so the proof reads instantly.
- **Shape language:** modest 6–10px radii and generous whitespace; let the warm-amber accent appear only on actions and a few key numbers.
## Sections — what each must communicate (design them your way)
## Sections, what each must communicate (design them your way)
1. **Hero** — immediate competence and reachability; the brand name, a trust-forward headline, a line about fast licensed 24/7 lockout and security service, the phone number as a primary action plus a free-quote request, and the rating proof (5.0 ★ · 3,100+ jobs).
2. **Trust stats** — the four numbers that close the deal: 18+ years experience · 3,100+ jobs completed · 24/7 emergency service · 100% satisfaction rate.
3. **Services** — all 6 services below with a short plain-language description of each; a customer should find their problem in seconds.
4. **Why SecureKey** — the company story in brief: locally owned, licensed & insured, background-checked technicians, upfront pricing, and a workmanship guarantee.
5. **Recent work** — proof of real completed jobs across homes, cars, and commercial sites.
6. **Pricing** — the 3 service plans below with prices and inclusions; Standard flagged as most popular.
7. **Testimonials** — three distinct, believable customer quotes with names; write them yourself.
8. **Request a free quote** — a working quote-request form (full name, phone, email, service needed, service address, preferred time) built with the builder's native form element, with the phone number given equal prominence as an alternative. If the builder has no form element available, use a clearly labeled call/email call-to-action instead of omitting this.
9. **Contact** — phone, email, address, office hours, 24/7 emergency line, service area, and a map if the builder supports one.
10. **Footer** — brand + tagline, quick links, contact, copyright.
1. **Hero**: immediate competence and reachability; the brand name, a trust-forward headline, a line about fast licensed 24/7 lockout and security service, the phone number as a primary action plus a free-quote request, and the rating proof (5.0 ★ · 3,100+ jobs).
2. **Trust stats**: the four numbers that close the deal: 18+ years experience · 3,100+ jobs completed · 24/7 emergency service · 100% satisfaction rate.
3. **Services**: all 6 services below with a short plain-language description of each; a customer should find their problem in seconds.
4. **Why SecureKey**: the company story in brief: locally owned, licensed & insured, background-checked technicians, upfront pricing, and a workmanship guarantee.
5. **Recent work**: proof of real completed jobs across homes, cars, and commercial sites.
6. **Pricing**: the 3 service plans below with prices and inclusions; Standard flagged as most popular.
7. **Testimonials**: three distinct, believable customer quotes with names; write them yourself.
8. **Request a free quote**: a working quote-request form (full name, phone, email, service needed, service address, preferred time) built with the builder's native form element, with the phone number given equal prominence as an alternative. If the builder has no form element available, use a clearly labeled call/email call-to-action instead of omitting this.
9. **Contact**: phone, email, address, office hours, 24/7 emergency line, service area, and a map if the builder supports one.
10. **Footer**: brand + tagline, quick links, contact, copyright.
## Content facts (use these verbatim)
**Services:** Emergency Lockout — home, car, or office, on-site within the hour · Lock Repair & Rekeying — worn, jammed, or post-move locks made secure · Key Cutting & Duplication — standard, high-security, and transponder car keys · Smart Lock Installation — keypad, fingerprint, and app-controlled deadbolts · Home Security Setup — cameras, sensors, and monitored alarm systems · Commercial Access Control — master-key systems, keypads, and panic hardware
**Services:** Emergency Lockout, home, car, or office, on-site within the hour · Lock Repair & Rekeying, worn, jammed, or post-move locks made secure · Key Cutting & Duplication, standard, high-security, and transponder car keys · Smart Lock Installation, keypad, fingerprint, and app-controlled deadbolts · Home Security Setup, cameras, sensors, and monitored alarm systems · Commercial Access Control, master-key systems, keypads, and panic hardware
**Pricing plans:** Basic — $79 — service call, single lockout or rekey, 30-day warranty · Standard — $139 (Most Popular) — full service call, rekey up to 3 locks, security walk-through, 1-year warranty · Premium — $239 — priority scheduling, whole-home lock & entry-point audit, one smart-lock install, 2-year warranty
**Pricing plans:** Basic, $79, service call, single lockout or rekey, 30-day warranty · Standard, $139 (Most Popular), full service call, rekey up to 3 locks, security walk-through, 1-year warranty · Premium, $239, priority scheduling, whole-home lock & entry-point audit, one smart-lock install, 2-year warranty
**Stats:** 18+ years experience · 3,100+ jobs completed · 24/7 emergency service · 100% satisfaction rate · 5.0 ★ average rating
@@ -55,24 +55,24 @@ These are compositional cues, not layout mechanics. Use them to give this servic
## Standards (non-negotiable)
### Accessibility — WCAG 2.1 AA
- Text contrast ≥ 4.5:1 (≥ 3:1 for large text) against its actual background — check text over photos and dark bands.
### Accessibility, WCAG 2.1 AA
- Text contrast ≥ 4.5:1 (≥ 3:1 for large text) against its actual background, check text over photos and dark bands.
- Exactly one H1; headings descend logically (no skipped levels for styling reasons).
- Descriptive alt text on every image; meaningful link/button labels (never "click here").
- Comfortable touch targets on buttons and links; readable body size (≥ 16px); every form field has a visible, associated label.
### Images — real photography in every slot
### Images, real photography in every slot
- Source real photos from **Unsplash, Pexels, or Pixabay** (use the available stock-image search/sideload tools if present, otherwise direct source URLs). Never leave an image slot empty; never use placeholder URLs.
- Curate for a consistent clean, natural grade that matches the palette; pick photos of real technicians and real finished work that share one mood, not six random locksmith photos.
### Icons — one consistent SVG set
- Use inline SVG icons in a single consistent style — Lucide/Feather spec: `viewBox="0 0 24 24"`, `fill="none"`, `stroke="currentColor"`, `stroke-width="1.75"`, round caps/joins. Generate what you need (key, lock, shield-check, clock, phone, map-pin, star, badge-dollar…).
### Icons, one consistent SVG set
- Use inline SVG icons in a single consistent style, Lucide/Feather spec: `viewBox="0 0 24 24"`, `fill="none"`, `stroke="currentColor"`, `stroke-width="1.75"`, round caps/joins. Generate what you need (key, lock, shield-check, clock, phone, map-pin, star, badge-dollar…).
- Do not use the builder's bundled icon font/library, and no emoji as icons.
### Builder-native construction
- Build with the chosen builder's **native elements/widgets/blocks** and its native styling, spacing, and motion features. Inspect the builder's available elements and their options first if tools for that exist.
- Raw HTML/custom code embedded inside a visual builder is a **last resort** for a micro-detail that is genuinely impossible natively — never for whole sections. (If the chosen target IS plain HTML/CSS, write clean semantic HTML with modern CSS instead.)
- Raw HTML/custom code embedded inside a visual builder is a **last resort** for a micro-detail that is genuinely impossible natively, never for whole sections. (If the chosen target IS plain HTML/CSS, write clean semantic HTML with modern CSS instead.)
### Completeness — a half-built page is a failure
- Build and **fully populate one section at a time**: real headline, real copy, every service described, every plan with its price and inclusions, every quote written, every image placed — then move on. Never scaffold empty containers to "fill later."
### Completeness, a half-built page is a failure
- Build and **fully populate one section at a time**: real headline, real copy, every service described, every plan with its price and inclusions, every quote written, every image placed, then move on. Never scaffold empty containers to "fill later."
- When finished, walk the entire page and fix any empty element, placeholder text, missing image, or contrast failure. Publish as a **draft**.
+12 -12
View File
@@ -1,11 +1,11 @@
Publish a local news article as a standalone Elementor page — with Google News-grade structured data and image SEO — on a site whose header and footer already exist as Elementor templates.
Publish a local news article as a standalone Elementor page, with Google News-grade structured data and image SEO, on a site whose header and footer already exist as Elementor templates.
This playbook is for hyperlocal news / community sites that publish articles as Pages (not Posts), where every article must be eligible for Google Discover, Top Stories and rich results from day one.
## CRITICAL LAYOUT RULES
- Create the page with `template=elementor_canvas` — the site chrome comes from template shortcodes, not the theme.
- Create the page with `template=elementor_canvas`, the site chrome comes from template shortcodes, not the theme.
- The page is exactly ONE top-level container with THREE children: header shortcode, article HTML widget, footer shortcode.
- The top-level container MUST be full-bleed. Pass ALL of these settings to add-container — Elementor's defaults (boxed width, padding, gaps) show as white strips around the header/footer on Canvas pages:
- The top-level container MUST be full-bleed. Pass ALL of these settings to add-container, Elementor's defaults (boxed width, padding, gaps) show as white strips around the header/footer on Canvas pages:
```json
{
@@ -20,24 +20,24 @@ This playbook is for hyperlocal news / community sites that publish articles as
```
## PAGE STRUCTURE
1. `create-page` — title = the headline, `status=publish`, `template=elementor_canvas`.
1. `create-page`, title = the headline, `status=publish`, `template=elementor_canvas`.
2. `add-container` with the full-bleed settings above.
3. `add-shortcode` → `[elementor-template id="HEADER_ID"]`
4. `add-html` → the complete article (markup + one `<style>` block + JSON-LD, see below).
5. `add-shortcode` → `[elementor-template id="FOOTER_ID"]`
6. Set the URL slug and SEO meta afterwards via REST (`POST /wp/v2/pages/{id}`).
> **Prerequisite for the REST meta steps:** Yoast's `_yoast_wpseo_*` keys are protected, so a vanilla site silently ignores them over REST — first register them (`register_post_meta(..., ['show_in_rest'=>true])`) or opt them into the `emcp_tools_content_allowed_protected_meta` filter. See #82.
> **Prerequisite for the REST meta steps:** Yoast's `_yoast_wpseo_*` keys are protected, so a vanilla site silently ignores them over REST, first register them (`register_post_meta(..., ['show_in_rest'=>true])`) or opt them into the `emcp_tools_content_allowed_protected_meta` filter. See #82.
## ARTICLE HTML (inside the single HTML widget)
- Semantic broadsheet structure: `<article>` → lead block (kicker, single `<h1>`, byline row with `<time datetime="YYYY-MM-DD">`, standfirst paragraph, hero `<figure>` with `<figcaption>` crediting the photographer) → body paragraphs → `<h2>` sections → gallery figures → footer block (disclaimer + "send us your story" contact line).
- Exactly one `<h1>`; sections use `<h2>` — never skip levels.
- Exactly one `<h1>`; sections use `<h2>`, never skip levels.
- Hero `<img>`: explicit `width`/`height` attributes + `fetchpriority="high"`. Every other image: `loading="lazy" decoding="async"` + explicit dimensions.
- Alt text on every image, written as a factual description of the photo (it must match the alt text set in the Media Library).
- Attribution discipline for anything unconfirmed: attribute claims to their source and mark them ("A local resident said… This has not been independently verified"). Never state a cause of an incident as fact before officials confirm it.
- Internal links: link earlier related articles in the body with descriptive anchors, and link the news index page. Cross-link back from the older article if this is a follow-up.
### Social embeds (Instagram/X) — lazy-load them
### Social embeds (Instagram/X), lazy-load them
Never load embed scripts at page load; they wreck LCP. Render the plain `<blockquote class="instagram-media">` markup and inject the script only when the embed scrolls near the viewport:
```html
@@ -58,10 +58,10 @@ Never load embed scripts at page load; they wreck LCP. Render the plain `<blockq
</script>
```
## IMAGE SEO — DO THIS FOR EVERY ARTICLE
1. From the best original photo, generate three crops at exactly **1200px wide: 16:9 (1200×675), 4:3 (1200×900), 1:1 (1200×1200)** — Google Discover and Top Stories want all three ratios at ≥1200px. Mild upscale from 1080px originals is acceptable.
## IMAGE SEO, DO THIS FOR EVERY ARTICLE
1. From the best original photo, generate three crops at exactly **1200px wide: 16:9 (1200×675), 4:3 (1200×900), 1:1 (1200×1200)**: Google Discover and Top Stories want all three ratios at ≥1200px. Mild upscale from 1080px originals is acceptable.
2. Descriptive, keyword-bearing filenames: `<subject>-<town>-16x9.jpg`, not `IMG_4021.jpg`.
3. Upload via REST (`POST /wp/v2/media`, raw body + `Content-Disposition`), then set `alt_text`, `caption`, and a `slug` that will NOT collide with the page slug (suffix attachment slugs with `-photo` — WordPress otherwise gives the attachment the clean slug and your page gets `-2`).
3. Upload via REST (`POST /wp/v2/media`, raw body + `Content-Disposition`), then set `alt_text`, `caption`, and a `slug` that will NOT collide with the page slug (suffix attachment slugs with `-photo`, WordPress otherwise gives the attachment the clean slug and your page gets `-2`).
4. Set the 16:9 crop as the page's featured image: `POST /wp/v2/pages/{id} {"featured_media": <id>}`.
5. **Yoast gotcha:** the featured image does NOT refresh Yoast's og:image (the indexable keeps the first content image). Set it explicitly: postmeta `_yoast_wpseo_opengraph-image` = crop URL and `_yoast_wpseo_opengraph-image-id` = attachment ID. Verify `og:image` on the live page afterwards.
@@ -69,7 +69,7 @@ Never load embed scripts at page load; they wreck LCP. Render the plain `<blockq
Requirements that rich-results validation actually enforces:
- `headline` ≤ 110 characters.
- `datePublished`/`dateModified` as full ISO-8601 **with timezone** (`2026-07-11T12:45:00+01:00`).
- `image` as an **array of ImageObject** — the three 1200px crops first, then the hero and key gallery shots, each with `url`, `width`, `height`, `caption`.
- `image` as an **array of ImageObject**: the three 1200px crops first, then the hero and key gallery shots, each with `url`, `width`, `height`, `caption`.
- `publisher.logo` as an ImageObject **with width and height**.
- `inLanguage`, `isAccessibleForFree: true`, `articleSection`, `mainEntityOfPage`, and `about` as a `Place` naming the location.
@@ -77,7 +77,7 @@ Requirements that rich-results validation actually enforces:
- SEO title ≤ 60 chars, primary keyword early; meta description 150–160 chars; focus keyword set.
- Slug pattern: `<town>-<subject>` (e.g. `anytown-high-street-fire`), lowercase, no dates.
## KEEP THE SITE FRESH — ROTATION
## KEEP THE SITE FRESH, ROTATION
The homepage "latest news" block and the news index page each show a lead card plus compact rows. When a new article publishes: newest article becomes the lead card (image, meta line, headline, teaser, CTA), the previous lead demotes to the first row, oldest row drops off the homepage (the index keeps all).
## VERIFY BEFORE CALLING IT DONE
+28 -28
View File
@@ -1,51 +1,51 @@
**Page builder:** Elementor
# Alex Rivera — web developer portfolio landing page
# Alex Rivera, web developer portfolio landing page
Design and build a **complete, production-quality portfolio landing page** for "Alex Rivera", a full-stack web developer and UI designer. **You own the design.** If web search is available, research 2–3 genuinely well-designed developer / designer portfolio sites first and form a point of view; then decide the layout, composition, and section design yourself. Do not fall back to a generic centered-hero + three-identical-cards template — this is a dark, code-forward portfolio: sharp and technical, a code aesthetic without the tired clichés, work presented with real editorial rhythm. **Vary your hero composition to fit this brand specifically — in particular, do not reach for the overused "one large photo with a smaller photo card overlapping its corner" hero unless it genuinely serves this design; choose an opener you would not use for a coffee shop or a law firm.** The sections below define **what the page must communicate, not how it must look.**
Design and build a **complete, production-quality portfolio landing page** for "Alex Rivera", a full-stack web developer and UI designer. **You own the design.** If web search is available, research 2–3 genuinely well-designed developer / designer portfolio sites first and form a point of view; then decide the layout, composition, and section design yourself. Do not fall back to a generic centered-hero + three-identical-cards template, this is a dark, code-forward portfolio: sharp and technical, a code aesthetic without the tired clichés, work presented with real editorial rhythm. **Vary your hero composition to fit this brand specifically, in particular, do not reach for the overused "one large photo with a smaller photo card overlapping its corner" hero unless it genuinely serves this design; choose an opener you would not use for a coffee shop or a law firm.** The sections below define **what the page must communicate, not how it must look.**
## Style guide (constraints, not layout)
**Palette — flat colors only, no gradients:**
**Palette, flat colors only, no gradients:**
| Role | Value |
|---|---|
| Surface, base | `#0D1117` (near-black) |
| Surface, raised bands | `#161B22` (raised panel) |
| Ink / headings | `#E6EDF3` (near-white) |
| Accent — use sparingly: eyebrows, labels, links, buttons | `#3FDF8C` (terminal green) |
| Accent, use sparingly: eyebrows, labels, links, buttons | `#3FDF8C` (terminal green) |
| Muted body text | `#8B949E` (slate gray) |
| Hairlines / dividers | `#242C36` (subtle border) |
**Typography:** Display = **Inter Tight** (fallback: Inter) — tight, modern sans, bold headlines, lowercase is on-brand. Accents/labels/eyebrows/code in **JetBrains Mono** (fallback: monospace) — the mono is the signature; use it for eyebrows, tech labels, stats, and any code UI. Body = **Inter**, 16–17px, line-height ~1.8. Small uppercase mono eyebrows in terminal green.
**Typography:** Display = **Inter Tight** (fallback: Inter), tight, modern sans, bold headlines, lowercase is on-brand. Accents/labels/eyebrows/code in **JetBrains Mono** (fallback: monospace), the mono is the signature; use it for eyebrows, tech labels, stats, and any code UI. Body = **Inter**, 16–17px, line-height ~1.8. Small uppercase mono eyebrows in terminal green.
**Feel:** sharp, technical, code-aesthetic — crisp 4px radii, subtle 1px borders instead of shadows, generous whitespace on a dark canvas. The green accent is scarce and deliberate; the mono type does the personality. No neon overload, no glow soup.
**Feel:** sharp, technical, code-aesthetic, crisp 4px radii, subtle 1px borders instead of shadows, generous whitespace on a dark canvas. The green accent is scarce and deliberate; the mono type does the personality. No neon overload, no glow soup.
**Motion:** crisp and restrained — subtle entrance reveals and small hovers; a terminal/code detail may animate if the builder does it natively. Use only what the chosen builder provides natively; **no custom JavaScript and no hand-rolled motion CSS.**
**Motion:** crisp and restrained, subtle entrance reveals and small hovers; a terminal/code detail may animate if the builder does it natively. Use only what the chosen builder provides natively; **no custom JavaScript and no hand-rolled motion CSS.**
## Design signature (make this page distinct — adapt as you see fit)
## Design signature (make this page distinct, adapt as you see fit)
These are compositional cues, not layout mechanics. Use them to give this portfolio its own look rather than a generic template:
- **Hero:** a terminal / code-panel lockup — the intro and headline paired with a small, real code-block object-literal about the developer, rendered as a mono panel with a 1px border, lowercase and precise; a code aesthetic without the tired clichés, no glow soup.
- **Projects:** an alternating editorial rhythm where each project block shifts weight and its tech tags read as mono chips — never a uniform three-up grid.
- **Hero:** a terminal / code-panel lockup, the intro and headline paired with a small, real code-block object-literal about the developer, rendered as a mono panel with a 1px border, lowercase and precise; a code aesthetic without the tired clichés, no glow soup.
- **Projects:** an alternating editorial rhythm where each project block shifts weight and its tech tags read as mono chips, never a uniform three-up grid.
- **Mono signature:** let JetBrains Mono carry the eyebrows, tech labels, and stats so the type does the personality; keep the terminal-green accent scarce.
- **Shape language:** crisp 4px radii, subtle 1px borders instead of shadows, generous whitespace on the dark canvas.
## Sections — what each must communicate (design them your way)
## Sections, what each must communicate (design them your way)
1. **Hero** — instant sense of who and what; a friendly intro line, a headline about building digital experiences people love, a one-line specialty (React, Node.js, creative web), primary "view my work" + secondary "download CV" actions, and social links. A code-block visual (a small object-literal about the developer) is on-brand as a companion to the copy.
2. **Tech stack** — the working toolkit as a clean labeled strip: the eight technologies below.
3. **Projects** — the featured work below, each with its type tag, name, a short outcome-focused description, its tech tags, and a "view project" link; alternate the rhythm so it reads editorial, not grid-monotonous.
4. **Services** — the four ways Alex helps below, each with one plain-language line.
5. **About** — crafting code with creativity (two short bio paragraphs) plus the three stats below.
6. **Testimonials** — three distinct, believable client quotes with names and role/company; write them yourself.
7. **Contact / CTA** — one strong closing moment: have a project in mind? A working contact form (name, email, project type, budget range, message) built with the builder's native form element, with the email address given equal prominence as an alternative, plus social links. If the builder has no form element available, use a clearly labeled email call-to-action instead of omitting this.
8. **Footer** — designed & built by Alex Rivera, and copyright.
1. **Hero**: instant sense of who and what; a friendly intro line, a headline about building digital experiences people love, a one-line specialty (React, Node.js, creative web), primary "view my work" + secondary "download CV" actions, and social links. A code-block visual (a small object-literal about the developer) is on-brand as a companion to the copy.
2. **Tech stack**: the working toolkit as a clean labeled strip: the eight technologies below.
3. **Projects**: the featured work below, each with its type tag, name, a short outcome-focused description, its tech tags, and a "view project" link; alternate the rhythm so it reads editorial, not grid-monotonous.
4. **Services**: the four ways Alex helps below, each with one plain-language line.
5. **About**: crafting code with creativity (two short bio paragraphs) plus the three stats below.
6. **Testimonials**: three distinct, believable client quotes with names and role/company; write them yourself.
7. **Contact / CTA**: one strong closing moment: have a project in mind? A working contact form (name, email, project type, budget range, message) built with the builder's native form element, with the email address given equal prominence as an alternative, plus social links. If the builder has no form element available, use a clearly labeled email call-to-action instead of omitting this.
8. **Footer**: designed & built by Alex Rivera, and copyright.
## Content facts (use these verbatim)
**Tech stack:** React · Node.js · TypeScript · Figma · Next.js · Tailwind CSS · PostgreSQL · AWS
**Projects:** SaaS Analytics Dashboard — Web App — a real-time analytics platform with custom charting and role-based access · Mobile Fitness App — Mobile UI — a colorful, motivating tracker with clean onboarding · Boutique E-Commerce — Web App — an elegant storefront with a fast, frictionless checkout
**Projects:** SaaS Analytics Dashboard, Web App, a real-time analytics platform with custom charting and role-based access · Mobile Fitness App, Mobile UI, a colorful, motivating tracker with clean onboarding · Boutique E-Commerce, Web App, an elegant storefront with a fast, frictionless checkout
**Services:** Frontend Development · Backend & APIs · UI/UX Design · Performance Optimization
@@ -55,24 +55,24 @@ These are compositional cues, not layout mechanics. Use them to give this portfo
## Standards (non-negotiable)
### Accessibility — WCAG 2.1 AA
- Text contrast ≥ 4.5:1 (≥ 3:1 for large text) against its actual background — check text over photos and raised panels.
### Accessibility, WCAG 2.1 AA
- Text contrast ≥ 4.5:1 (≥ 3:1 for large text) against its actual background, check text over photos and raised panels.
- Exactly one H1; headings descend logically (no skipped levels for styling reasons).
- Descriptive alt text on every image; meaningful link/button labels (never "click here").
- Comfortable touch targets on buttons and links; readable body size (≥ 16px); every form field has a visible, associated label.
### Images — real photography in every slot
### Images, real photography in every slot
- Source real photos from **Unsplash, Pexels, or Pixabay** (use the available stock-image search/sideload tools if present, otherwise direct source URLs). Never leave an image slot empty; never use placeholder URLs.
- Curate for a cohesive dark-theme grade that matches the palette; project mockups and a portrait that share one crisp, technical mood, not eight random tech shots.
### Icons — one consistent SVG set
- Use inline SVG icons in a single consistent style — Lucide/Feather spec: `viewBox="0 0 24 24"`, `fill="none"`, `stroke="currentColor"`, `stroke-width="1.75"`, round caps/joins. Generate what you need (code brackets, layout-grid, rocket, palette, terminal, globe, github, mail…).
### Icons, one consistent SVG set
- Use inline SVG icons in a single consistent style, Lucide/Feather spec: `viewBox="0 0 24 24"`, `fill="none"`, `stroke="currentColor"`, `stroke-width="1.75"`, round caps/joins. Generate what you need (code brackets, layout-grid, rocket, palette, terminal, globe, github, mail…).
- Do not use the builder's bundled icon font/library, and no emoji as icons.
### Builder-native construction
- Build with the chosen builder's **native elements/widgets/blocks** and its native styling, spacing, and motion features. Inspect the builder's available elements and their options first if tools for that exist.
- Raw HTML/custom code embedded inside a visual builder is a **last resort** for a micro-detail that is genuinely impossible natively — never for whole sections. (If the chosen target IS plain HTML/CSS, write clean semantic HTML with modern CSS instead.)
- Raw HTML/custom code embedded inside a visual builder is a **last resort** for a micro-detail that is genuinely impossible natively, never for whole sections. (If the chosen target IS plain HTML/CSS, write clean semantic HTML with modern CSS instead.)
### Completeness — a half-built page is a failure
- Build and **fully populate one section at a time**: real headline, real copy, every technology labeled, every project with its description and tech tags, every service line, every stat, every quote written, every image placed — then move on. Never scaffold empty containers to "fill later."
### Completeness, a half-built page is a failure
- Build and **fully populate one section at a time**: real headline, real copy, every technology labeled, every project with its description and tech tags, every service line, every stat, every quote written, every image placed, then move on. Never scaffold empty containers to "fill later."
- When finished, walk the entire page and fix any empty element, placeholder text, missing image, or contrast failure. Publish as a **draft**.
+20 -20
View File
@@ -175,40 +175,40 @@ The plugin enforces WordPress capability checks on every tool. Read operations r
= 3.5.1 =
A redesigned AI Chat (local models, vision, skills) + three connectivity/builder fixes.
* Added: AI Chat gets a full-width two-column settings screen (built-in controls + default model on the left, providers on the right). (Pro)
* Added: AI Chat providers DeepSeek, Moonshot, Z.ai, plus local runtimes Ollama and LM Studio — auto-detected on localhost, no API key needed. Refresh-models button; remembers your last provider/model. (Pro)
* Added: AI Chat providers DeepSeek, Moonshot, Z.ai, plus local runtimes Ollama and LM Studio, auto-detected on localhost, no API key needed. Refresh-models button; remembers your last provider/model. (Pro)
* Added: AI Chat loads the EMCP agent skills, and handles images for text-only models via a local vision model or a built-in dependency-free image reader. (Pro)
* Changed: AI Chat hardened for small local models — lean tool schemas, capped tool results, empty-turn recovery, reasoning-stream capture, one-page-per-request and background-format guidance; list-skills is now a compact catalog. (Pro)
* Fixed: #99 — a different MCP-adapter version bundled by another active plugin (e.g. Rank Math) caused "McpServerError: Session terminated"; EMCP now serves the whole WP\MCP namespace from its bundled copy so the server starts regardless of load order.
* Fixed: #97 — duplicating a v4 atomic element now re-mints its local style classes so the copy keeps its styling.
* Fixed: #98 — verify-and-fallback when Elementor's document save reports success but persists nothing (CLI/proxy), so edits aren't silently lost.
* Fixed: backgrounds emitted as a nested/array shorthand now render — the factory coerces them to Elementor's flat keys and adds the required classic activator (the "sideloaded photo doesn't show" cause).
* Changed: AI Chat hardened for small local models, lean tool schemas, capped tool results, empty-turn recovery, reasoning-stream capture, one-page-per-request and background-format guidance; list-skills is now a compact catalog. (Pro)
* Fixed: #99, a different MCP-adapter version bundled by another active plugin (e.g. Rank Math) caused "McpServerError: Session terminated"; EMCP now serves the whole WP\MCP namespace from its bundled copy so the server starts regardless of load order.
* Fixed: #97, duplicating a v4 atomic element now re-mints its local style classes so the copy keeps its styling.
* Fixed: #98, verify-and-fallback when Elementor's document save reports success but persists nothing (CLI/proxy), so edits aren't silently lost.
* Fixed: backgrounds emitted as a nested/array shorthand now render, the factory coerces them to Elementor's flat keys and adds the required classic activator (the "sideloaded photo doesn't show" cause).
* Fixed: build-page no longer silently drops shorthand nodes (accepts them, returns warnings); sideload-image resolves api.unsplash.com download URLs with actionable errors; clearer web-fetch SSL/timeout errors.
= 3.5.0 =
Forms + SEO plugin integrations — 15 plugins over MCP.
* Added: Forms integrations — two dispatcher tools per plugin (read/write) for 8 form builders: Contact Form 7 (free) plus WPForms, Gravity Forms, Fluent Forms, Ninja Forms, Formidable, MetForm, and SureForms (Pro). Read forms, fields, notifications and entries (submissions); manage entries. Each pair registers only when its plugin is active; reads on by default, writes off by default; delete-entry needs confirm:true. CF7 has no entries; WPForms entry ops need WPForms Pro.
* Added: SEO integrations — two dispatcher tools per plugin (read/write) for 7 SEO plugins: Slim SEO (free) plus Yoast SEO, Rank Math, All in One SEO, SEOPress, The SEO Framework, and SureRank (Pro). Read/write the SEO metadata each plugin stores (title, description, canonical, robots, focus keyword, Open Graph / Twitter) for posts and terms, over one unified field vocabulary, plus schema-read where supported. Distinct from the SEO & Accessibility toolkit (which audits/generates).
* Changed: Pro tool sections (WooCommerce, SEO & Accessibility, Widget Builder) now always appear on free builds — Pro-badged, disabled, with an Upgrade link — so it's clear what Pro unlocks.
Forms + SEO plugin integrations, 15 plugins over MCP.
* Added: Forms integrations, two dispatcher tools per plugin (read/write) for 8 form builders: Contact Form 7 (free) plus WPForms, Gravity Forms, Fluent Forms, Ninja Forms, Formidable, MetForm, and SureForms (Pro). Read forms, fields, notifications and entries (submissions); manage entries. Each pair registers only when its plugin is active; reads on by default, writes off by default; delete-entry needs confirm:true. CF7 has no entries; WPForms entry ops need WPForms Pro.
* Added: SEO integrations, two dispatcher tools per plugin (read/write) for 7 SEO plugins: Slim SEO (free) plus Yoast SEO, Rank Math, All in One SEO, SEOPress, The SEO Framework, and SureRank (Pro). Read/write the SEO metadata each plugin stores (title, description, canonical, robots, focus keyword, Open Graph / Twitter) for posts and terms, over one unified field vocabulary, plus schema-read where supported. Distinct from the SEO & Accessibility toolkit (which audits/generates).
* Changed: Pro tool sections (WooCommerce, SEO & Accessibility, Widget Builder) now always appear on free builds, Pro-badged, disabled, with an Upgrade link, so it's clear what Pro unlocks.
* Changed: Dashboard "Your usage" is now a 4-card activity panel (Usage, History, Most used, Sandbox). The Plugins tab is grouped by category (Dynamic Content, E-Commerce, Forms, SEO).
* New agent skills: emcp-plugins/forms and emcp-plugins/seo (Pro).
= 3.4.2 =
WooCommerce integration + template & prompt usage insights.
* Added: WooCommerce Pro integration — two tools (woo-read / woo-write) over the full wc/v3 surface (~120 operations): products, variations, orders, refunds, customers, coupons, reports, settings, shipping, taxes, webhooks, system status. Runs through WooCommerce's own REST (HPOS-safe). Reads on by default; writes off by default; refunds/deletes/batch require confirm:true. Registers only when WooCommerce is active.
* Added: Meta Box integration (community PR #96) — two tools (metabox-read / metabox-write) to read/write Meta Box custom-field values and discover field groups over MCP. Values only (no delete, no field authoring); registers only when Meta Box is active; metabox-read on by default, metabox-write off. Thanks @Mrshahidali420.
* Added: History tab — delete individual entries and a "Clear all" button to wipe the change ledger. Confirm-gated; deleting an entry that can still be rolled back warns you first.
* Fixed: the Affiliate Program link in the header led to "you are not allowed to access this page" on fresh installs — Freemius only registers that page after the user opts in or skips, so the link now shows only when the page exists.
* Added: WooCommerce Pro integration, two tools (woo-read / woo-write) over the full wc/v3 surface (~120 operations): products, variations, orders, refunds, customers, coupons, reports, settings, shipping, taxes, webhooks, system status. Runs through WooCommerce's own REST (HPOS-safe). Reads on by default; writes off by default; refunds/deletes/batch require confirm:true. Registers only when WooCommerce is active.
* Added: Meta Box integration (community PR #96), two tools (metabox-read / metabox-write) to read/write Meta Box custom-field values and discover field groups over MCP. Values only (no delete, no field authoring); registers only when Meta Box is active; metabox-read on by default, metabox-write off. Thanks @Mrshahidali420.
* Added: History tab, delete individual entries and a "Clear all" button to wipe the change ledger. Confirm-gated; deleting an entry that can still be rolled back warns you first.
* Fixed: the Affiliate Program link in the header led to "you are not allowed to access this page" on fresh installs, Freemius only registers that page after the user opts in or skips, so the link now shows only when the page exists.
* Added: "Used N times" usage counts on Pro template cards, plus a "Your usage" widget on the Dashboard (templates you've applied and prompts you've copied on this site + the globally-popular templates). Lightweight, non-blocking telemetry (license + site only; no page content, customer data, or AI conversations are ever sent).
= 3.4.1 =
OAuth sign-in for MCP clients + atomic-element and compact-mode fixes.
* Added: OAuth sign-in for AI clients (free). Claude and other MCP clients connect through a standard OAuth 2.1 flow — approve access from your WordPress login, no Application Password to copy. Self-contained authorization server (discovery, dynamic client registration, PKCE, admin-only consent, short-lived + refresh tokens); the token acts as the authorizing admin, and Application Passwords keep working alongside it. HTTPS-gated, on by default when available.
* Added: OAuth sign-in for AI clients (free). Claude and other MCP clients connect through a standard OAuth 2.1 flow, approve access from your WordPress login, no Application Password to copy. Self-contained authorization server (discovery, dynamic client registration, PKCE, admin-only consent, short-lived + refresh tokens); the token acts as the authorizing admin, and Application Passwords keep working alongside it. HTTPS-gated, on by default when available.
* Added: Connection tab authentication-method chooser (OAuth vs Application password) with per-client setup steps (mcp-add command, custom-connector walkthrough with one-click deep links, or config-file snippet), a Connected apps list with Revoke, and a Claude.ai client.
* Fixed: OAuth discovery 404 — MCP clients request the resource-scoped well-known path (RFC 9728), which 404'd and broke OAuth with real clients; now served.
* Fixed: get-page-snapshot returned zeros on atomic (v4) pages (#91) — now extracts atomic heading/paragraph/button/image content.
* Fixed: atomic style-class writes were persisted but never applied (#92) — writes now reference the class on the element.
* Fixed: OAuth discovery 404, MCP clients request the resource-scoped well-known path (RFC 9728), which 404'd and broke OAuth with real clients; now served.
* Fixed: get-page-snapshot returned zeros on atomic (v4) pages (#91), now extracts atomic heading/paragraph/button/image content.
* Fixed: atomic style-class writes were persisted but never applied (#92), writes now reference the class on the element.
* Fixed: compact tool mode surfaced 6 tools instead of 3 (core context abilities now fold into the dispatcher catalog).
* Fixed: subdirectory WordPress installs (e.g. example.com/subdir) were unreachable via the Node proxy (#93) — it ignored the URL path, building every request against the domain root; the proxy now honors the site base path (root installs unchanged) and returns a JSON-RPC error instead of corrupting stdio framing on a non-JSON upstream body. Proxy 1.9.1. Thanks @carstenbaumhoegger.
* Fixed: subdirectory WordPress installs (e.g. example.com/subdir) were unreachable via the Node proxy (#93), it ignored the URL path, building every request against the domain root; the proxy now honors the site base path (root installs unchanged) and returns a JSON-RPC error instead of corrupting stdio framing on a non-JSON upstream body. Proxy 1.9.1. Thanks @carstenbaumhoegger.
* Changed: renamed the Codex connection card to "ChatGPT App" and corrected its OAuth setup to the app's in-app MCP flow (Add server → Streamable HTTP → Save → Authenticate → Approve). The Application Password (config.toml) method is unchanged.
= 3.4.0 =
@@ -217,7 +217,7 @@ New Themes domain, WP-CLI tools, SVG uploads, and 50 starter templates.
* Added: WP-CLI tools (4 tools, disabled-by-default, manage_options): run-wp-cli (synchronous), dispatch-wp-cli (background job), get-wp-cli-job, list-wp-cli-jobs. A command validator blocks destructive/host-level commands (eval, shell, db query/import/export/reset, config set, package/cli mutations) and dangerous flags; runs in-process when available, else via a spawned shell. Every run is recorded to the change ledger.
* Added: SVG Uploads module (opt-in): allow sanitized SVG/SVGZ uploads to the Media Library. Every uploaded SVG is scrubbed with enshrined/svg-sanitize (scripts, event handlers, external references removed) and rejected if it can't be made safe. Fixes the common programmatic-upload failure where WordPress mis-detects SVG as a text file.
* Added: get-block-schema now surfaces Spectra's shared-helper attributes so Gutenberg/Spectra blocks report their real attribute names and defaults.
* Changed: search-images and sideload-image are now core WordPress tools — they register on any site regardless of Elementor (they only need a stock-provider API key and the Media Library).
* Changed: search-images and sideload-image are now core WordPress tools, they register on any site regardless of Elementor (they only need a stock-provider API key and the Media Library).
= 3.3.0 =
Foundation release: understand a page from one call, undo any change, and reuse existing content.
+1 -1
View File
@@ -65,7 +65,7 @@ final class McpbBuilderTest extends TestCase {
public function test_display_name_remains_per_host(): void {
$m = $this->manifest( 'https://example.com' );
$this->assertSame( 'EMCP Tools — example.com', $m['display_name'] );
$this->assertSame( 'EMCP Tools - example.com', $m['display_name'] );
}
public function test_credentials_are_embedded_in_env(): void {