Design & media

SiteAgent Elementor Studio

Try it

Helps with WordPress + Elementor work via the elementor-mcp MCP server — building new pages, editing existing ones, inspecting site state, or exploring what's possible. Auto-detects Elementor Pro (native Form, Theme Builder, Loop Grid, Popups, Dynamic Tags, Sticky/Motion vs free-tier workarounds) AND the page engine (classic vs Elementor 4 atomic/V4 — atomic uses add-flexbox/add-atomic-* tools since classic writes don't persist on a V4 page). Detects ACF + Crocoblock/JetEngine for dynamic-data binding (Tier-0; bind ACF via Pro dynamic tags, place Jet widgets via add-widget with runtime-verified types). On atomic (V4) sites, authors the Elementor 4 design system — Global Classes, Variables (design tokens), and per-element Interactions — and recovers from the fork's schema-in-error and governance responses. Asks what the user wants before acting. Use when the user references the Elementor MCP, invokes `/siteagent-elementor-studio`, or runs `mcp__elementor__elementor-mcp-*` tools. Also co

What it does

You are operating against a WordPress site with the **elementor-mcp** server ( — our fork, Elementor 4.x-correct) connected via the WordPress MCP Adapter. This skill captures everything I learned the hard way the first time through, so subsequent sessions start at expertise level.

The skill document

SiteAgent Elementor Studio Skill

You are operating against a WordPress site with the elementor-mcp server (https://github.com/Digitizers/elementor-mcp — our fork, Elementor 4.x-correct) connected via the WordPress MCP Adapter. This skill captures everything I learned the hard way the first time through, so subsequent sessions start at expertise level.

🛑 First Action Protocol — ASK BEFORE DOING

When this skill is invoked, do not start running tools. Ask the user what they want first.

Shell / setup actions run only on explicit user confirmation. The bundled setup-elementor-mcp.sh (which shells out, runs Local's WP-CLI, downloads the plugin, and writes a credentialed .mcp.json) is never run automatically — offer it and wait for the user to say yes.

If the user's invocation message already contains a clear task — "build me a hero section from index.html", "show me my current global colors", "change the burgundy to navy" — proceed with that task directly.

Otherwise (invocations like /siteagent-elementor-studio alone, or "use the Elementor MCP" with no follow-up), respond with this menu and wait for the user to pick:

What would you like to do with your Elementor site?

  1. Build       — create new pages or sections from a design
  2. Edit        — change something on an existing page
  3. Reference   — inspect current state (pages, colors, fonts, content)
  4. Explore     — show me what's possible / what can the MCP do here

Do not silently default to "build" — that's the most destructive action and forces a path the user may not want. Wait for the user to choose 1/2/3/4 (or describe their task in their own words) before invoking any MCP tool other than the harmless read-only ones at the bottom of this section.

Read-only "smoke test" calls that are always safe to run

When the user picks any option, you can run these before asking follow-up questions, since they help frame the next response:

  • mcp__elementor__elementor-mcp-list-pages — confirms auth + lists what's there
  • mcp__elementor__elementor-mcp-get-global-settings — current colors/fonts kit

That's it for unprompted tool calls. Anything that creates, modifies, or deletes data requires the user to have explicitly asked for it.

When this skill applies

  • The user mentions Elementor MCP, types /siteagent-elementor-studio, or says "use the Elementor MCP"
  • A .mcp.json in the project registers an MCP server pointing at wp-json/mcp/elementor-mcp-server
  • The user asks to build, edit, inspect, or troubleshoot an Elementor page
  • Tools beginning with mcp__elementor__elementor-mcp-* are available

First-session setup (when MCP not yet connected)

Engine: this skill drives our fork Digitizers/elementor-mcp (v1.24.0, up to 118 tools, Elementor 4.x-correct), a single self-contained plugin — no Freemius, no phone-home. Never run it alongside the paid "MCP Tools for Elementor (Premium)" — same class names → fatal. Details + switch commands → references/engine-and-premium.md.

If the user has a WordPress site but no .mcp.json and no elementor MCP loaded:

  1. Check whether they're using Local-by-Flywheel or a live host. Setup paths differ.
  2. Run the bundled setup script at ~/.claude/scripts/setup-elementor-mcp.sh — it handles plugin install, auth wiring, and .mcp.json generation interactively for both flavors.
    bash ~/.claude/scripts/setup-elementor-mcp.sh
    
  3. After the script completes, instruct the user to quit and reopen Claude Code in the project directory so the new .mcp.json is picked up.
  4. On reopen, the deferred MCP tools will be exposed via ToolSearch — load the ones you need with select: queries.

If something fails, see "Setup gotchas" below.

Working session conventions

Always do this first

mcp__elementor__elementor-mcp-list-pages   # confirms auth + lists existing pages
mcp__elementor__elementor-mcp-get-global-settings   # see existing colors/fonts kit
mcp__elementor__elementor-mcp-get-container-schema  # ground truth on flex_* key names

🎯 Detect Pro vs Free FIRST — it changes which path you take

Before building anything, determine whether Elementor Pro is active. The whole skill branches on this: with Pro you use native widgets (Form, Theme Builder, Loop Grid, Popups, Dynamic Tags, Sticky/Motion); without it you use the free-tier workarounds documented further down (Fluent Forms, UAE/HFE, HTML for motion).

How to detect — by tool availability (the definitive Pro signal):

The elementor-mcp server exposes Pro tools conditionally. When Pro is active the tool list grows from ~74 to ~100+ tools and Pro-only tools appear. Check whether these exist in your available mcp__elementor__elementor-mcp-* tools:

  • add-form — present ⇒ Pro active
  • create-theme-template — present ⇒ Pro active
  • add-loop-grid / add-loop-carousel — present ⇒ Pro active
  • create-popup, set-dynamic-tag — present ⇒ Pro active

If none of those Pro tools are exposed, treat the site as Free and use the workarounds. The tool-presence check above is the definitive Pro-vs-Free signal — prefer it. (detect-elementor-version is reliable on current builds — the old v1.5.0 schema bug is fixed — but it reports atomic/version support, not a Pro flag, so it doesn't settle Pro-vs-Free on its own.)

Record the verdict once ("Pro detected" / "Free only") and state it to the user up front, then follow the matching branch in every section below. Each "Forms", "Header/Footer", and motion section is written as If Pro → … / If Free → …. Don't mix paths.

🧬 Also detect the ENGINE — classic vs atomic (Elementor 4 / V4)

There's a second axis that changes everything: the page engine. Elementor 4 introduced the atomic / V4 engine (e-flexbox, e-div-block, atomic widgets, a typed-prop $$type data model). Classic widget writes do not persist on an atomic page — they silently appear to do nothing. So before building, decide classic vs atomic.

How to detect — by atomic tool availability (preferred):

The atomic tools register only when the site is on the atomic engine. Check your available tools:

  • add-flexbox, add-div-block, add-atomic-heading / add-atomic-button / …, add-atomic-widget / update-atomic-widget present ⇒ atomic (V4) engine
  • Only classic add-container / add-heading / … present ⇒ classic engine

You may also call detect-elementor-version (reliable on current releases — it returns whether atomic is supported). The old v1.5.0 schema bug is fixed; still, tool-presence is the most direct signal.

⚠️ Antigravity / tight tool caps. Antigravity caps MCP tools at ~100. The full Pro+atomic set is ~113, so atomic tools can get truncated and never reach the client — making a V4 site look like "writes don't persist". Fix: enable the MCP plugin's Low-tools mode (WP Admin → MCP Tools screen). Its curated essentials set includes the 5 atomic essentials (detect-elementor-version, add-atomic-widget, update-atomic-widget, add-flexbox, add-div-block) and stays under the cap.

🐞 Known root cause (older MCP builds). Elementor often runs atomic as an opt-in experiment while ELEMENTOR_VERSION still reads 3.x. MCP builds that gate atomic on version_compare(ELEMENTOR_VERSION,'4.0.0','>=') therefore never register the atomic tools on those sites. Fixed upstream by detecting via the experiment/module. If atomic tools are missing on a clearly-V4 site, update the elementor-mcp plugin (or confirm the V4 experiment is on under Elementor → Settings → Features).

Record both axes: e.g. "Pro + atomic", "Pro + classic", "Free + classic". The Pro/Free axis picks Form vs Fluent Forms etc.; the engine axis picks classic vs atomic widget tools (next section).

The container schema is large (~50KB). Read it once, then write down the keys you'll use in your reply text so you don't need to re-fetch it. Critical keys:

  • flex_direction, flex_justify_content, flex_align_items, flex_gap, flex_wrap — note the flex_ prefix on justify/align (issue #32 was about these being written under wrong keys in older versions)
  • content_width: "boxed"|"full" + boxed_width: {unit, size, sizes}
  • min_height: {unit, size, sizes} — use unit vh for full-screen heroes
  • padding/margin: {unit, top, right, bottom, left, isLinked}isLinked: false when sides differ
  • background_background: "classic"|"gradient"|"video" — must be set first or other background_* keys are ignored
  • background_overlay_* — separate parallel set for overlays. background_overlay_opacity: {unit:"px", size: 0.5} (yes, the unit is px even for opacity — quirk of the schema)

Widget call convention — flat params, NOT nested in settings

This bit me hard the first time. The add-* shortcut tools take their settings as top-level parameters, not inside a settings: {} object:

// ✓ CORRECT
mcp__elementor__elementor-mcp-add-heading({
  post_id: 11,
  parent_id: "abc123",
  title: "where estates are entrusted",
  header_size: "h1",
  title_color: "#FFFFFF",
  typography_typography: "custom",       // ← required to enable typography
  typography_font_family: "Cormorant Garamond",
  typography_font_size: {size: 110, unit: "px"},
  typography_font_weight: "300",
  typography_line_height: {size: 0.98, unit: "em"},
})

// ✗ WRONG — silently fails or returns "title is required"
mcp__elementor__elementor-mcp-add-heading({
  post_id: 11,
  parent_id: "abc123",
  settings: {title: "...", typography_font_family: "..."}
})

add-container is the exception — it takes a settings: {} object. Don't generalize from one to the other.

Always set typography_typography: "custom"

Without this, the other typography_* keys are ignored. Same applies to css_filters_css_filter: "custom" for image filters, etc. — these "enable" flags are how Elementor knows you want to override defaults.

Italic emphasis pattern

Display headings often need a single italic-emphasized word. Don't use a separate widget — just inline `` in the title:

title: "A quiet practice for an uncommon clientele."

Cormorant Garamond and most luxury serifs have italic variants that auto-load when `` appears. Confirm via the rendered page; if italics fail, the global typography needs the italic variant explicitly enabled.

Responsive values — suffix keys (classic) vs. variants (atomic)

Elementor stores a responsive control's per-breakpoint values under suffixed keys. The base (desktop) value has no suffix; each breakpoint appends its own suffix to the same base control key, with the same value shape as desktop:

// classic widget — tablet/mobile overrides of the same control:
mcp__elementor__elementor-mcp-add-heading({
  post_id, parent_id,
  title: "...",
  typography_font_size: {size: 110, unit: "px"},          // desktop (base, no suffix)
  typography_font_size_tablet: {size: 72, unit: "px"},    // tablet
  typography_font_size_mobile: {size: 44, unit: "px"},    // mobile
  align: "left", align_tablet: "center",                  // alignment per breakpoint
})

The suffix set is breakpoint-dependent — do NOT hardcode an incomplete list. It derives from Elementor's active breakpoints (add_responsive_control()), so beyond _tablet / _mobile a site may expose _widescreen, _laptop, _tablet_extra, _mobile_extra, or custom ones. Read the site's breakpoints rather than assuming; the fork passes any _ key through as long as `` is a real control.

Atomic (V4) is different — no suffix keys. On a V4 page responsive lives in a style definition's variants array, keyed by a breakpoint meta (desktop = base, then tablet/mobile/custom) — Global Classes take a variants param, local styles add variant entries. Never put _tablet/_mobile suffix keys on atomic elements. See references/atomic-v4.md and references/design-system-crud.md.

The widget-vs-HTML decision — DEFAULT TO NATIVE WIDGETS

🚨 CRITICAL ANTI-PATTERN — read this first.

Do NOT paste an entire HTML page into one HTML widget. Do NOT build a homepage that is "1 container with 3 HTML widgets inside." That is not building with Elementor — that is using Elementor as a wrapper around a static webpage. The user cannot edit it in the Elementor visual editor, cannot reuse the design tokens, and cannot iterate on it without going back to source code.

If you find yourself thinking "I'll just dump this section as HTML, it's faster," STOP. Break it into native widgets.

Always default to native widgets

For every section the user wants, build it from native Elementor widgets:

  • Headingsadd-heading widget (supports inline `` for italic emphasis)
  • Body copyadd-text-editor widget
  • Imagesadd-image widget (NOT an `` tag inside an HTML widget)
  • Buttons / CTAsadd-button widget (NOT an `` styled as a button)
  • Layout / spacingadd-container with proper flex_* settings (NOT ``s with CSS flex)
  • Listsadd-icon-list widget
  • Tabsadd-tabs widget
  • Accordions / FAQsadd-accordion widget
  • Forms → Fluent Forms shortcode via add-shortcode widget
  • Nav menu in headers → UAE Nav Menu widget (uael-nav-menu)

When HTML widget IS allowed (narrow list — exceptions only)

Only reach for an HTML widget in these specific cases. Anything not on this list goes through native widgets.

  1. Tab/accordion content with rich layout. add-tabs only accepts tab_content as a string of HTML, so a multi-card grid inside a tab MUST be HTML. (But the wrapping Tabs widget itself is still native.)
  2. Decorative-only flourishes with no native equivalent — a thin gold rule with a CSS-pseudo-element flourish, an animated underline that grows on hover, a gradient overlay on a child element. Even then, prefer to pair it with a native widget rather than replacing one.
  3. Form HTML as a flagged placeholder when no real form plugin is wired up yet — and you must explicitly tell the user "form is visual only, doesn't capture submissions."
  4. Site-wide CSS overrides scoped to a specific Elementor element ID (e.g., styling the tab strip of an add-tabs widget that the widget controls don't expose). These should be small style blocks, not whole sections of markup.

What about card grids of 4+ items?

Earlier versions of this skill said "use one HTML widget for card grids — it's faster than 50 widget calls." That advice was wrong because it led to non-editable pages.

The correct path for card grids:

  • Build the first card with native widgets (Container → Image → Heading → Text Editor → Button)
  • Use duplicate-element to copy it 3+ more times
  • Use update-element to change the copy/image on each duplicate
  • Wrap them in a parent Container with flex_direction: row and flex_wrap: wrap

This is more widget calls, yes, but the result is a real Elementor card grid the user can edit, restyle globally, or reuse as a template.

If Pro is active and the cards are driven by posts/CPT/products (a blog feed, portfolio, listings), prefer the native Loop Grid (add-loop-grid) instead — see the Loop Grid section below. The duplicate-element pattern is still the right answer for a fixed set of bespoke, non-dynamic cards on either tier.

Cross-widget styling — `


The `f8d1545` is the `element_id` returned when you created the tabs widget. Always grab and remember these IDs — they're the only stable selector across page reloads.

> ⚠️ **An HTML widget used for cross-widget styling MUST contain only ``.** If you find yourself adding HTML markup *(divs, anchors, spans with text content)* alongside the style block, you're falling back into the anti-pattern at the top of this section. Stop. That markup belongs in native widgets.

## Building on Elementor 4 (atomic / V4)

Elementor 4 uses an atomic/V4 data model — classic widget writes don't persist on a V4 page. Detect the engine first (see core detection above), then use the atomic tool family. **Full atomic model, tool family, and build order → load `references/atomic-v4.md`.**

> **Atomic local styles wiring.** Atomic styling attaches through **two coupled pieces** —
> `settings.classes` (a typed list of class ids the element wears) **and** a separate
> top-level `styles` map holding each class's definition. Every id in `settings.classes`
> must resolve to a local `styles` entry or a Global Class `g-` id, or it styles nothing.
> The dedicated helpers build the local `styles` map for you **at creation**. Note
> `update-atomic-widget` merges `settings` only — it **cannot** write the top-level `styles`
> map, so restyle by (re)creating with a style-capable helper / universal `add-atomic-widget`,
> or point `settings.classes` at a Global Class. Full pattern → `references/atomic-v4.md`.

### Converting a classic (V3) design to atomic (V4)

There's no in-place migrator — you **rebuild** the design on a fresh V4 page with atomic
tools (classic/atomic never mix). Tool map, `$$type` rules, styling parity, and a worked
example → **load `references/v3-to-v4-conversion.md`.**

## Elementor 4 design system — Global Classes, Variables, Interactions (v1.14+)

On an **atomic (V4)** site the fork exposes CRUD for the shared design system, so you can
author reusable styling instead of re-styling every element:

- **Global Classes** (reusable style bundles): `create-global-class`, `update-global-class`,
  `delete-global-class`, `apply-global-class` (+ read `list-global-classes`).
- **Variables** (color/font/size design tokens): `list-variables`, `get-variable`,
  `create-variable`, `edit-variable`, `delete-variable`, `restore-variable`.
- **Interactions** (per-element scroll/hover/click animations): `list-interactions`,
  `add-interaction`, `edit-interaction`, `delete-interaction`.

`restore-variable` (undo a soft-deleted token) and `edit-interaction` (id-addressable
in-place animation edit) are **fork-superset** capabilities the editor path doesn't offer.
All writes need `manage_options`; these tools register only when the atomic engine +
matching experiments are on. **Full tool shapes, params, Pro gating, caps, and when to use
each → load `references/design-system-crud.md`.**

## When a write fails — errors & recovery

The fork's errors are built for self-correction; read them, don't just relay them:

- **Wrong widget name** → `invalid_widget_type` / `widget_not_found` carry `Did you mean:`
  suggestions **inline in the message** — pick the nearest and retry (no second lookup).
- **Bad atomic settings** → `save_rejected` embeds the atomic type's **prop schema** inline
  — correct the settings and retry in one round trip.
- **Numeric/slider values** → `get-widget-schema` now returns `minimum`/`maximum`/`multipleOf`
  and slider `unit` enums — clamp to them before writing.
- **Governance (opt-in, only with the SiteAgent worker):** `governance_grant_required` /
  `governance_grant_invalid` mean the write needs a gateway-minted approval grant (you can't
  self-fix — the user must approve); `governance_render_failed` means the write broke the page
  and **was reverted** (don't blindly re-send — fix the cause); `governance_rollback_failed`
  means the revert itself failed and the page may be **partially written** — **stop and
  escalate** with the snapshot id.

**Full recovery playbook (all error codes, retry semantics, range hints) → load
`references/error-recovery.md`.**

## Brand kit — intake & tokens

Triggers when the user is setting up a new client / brand, or says "set up the
brand". Establishes the design tokens every later build references **by name, never
raw hex/font**. Full schema + tool shapes: [`references/brand-kit.md`](references/brand-kit.md).

The 8 color tokens (`brand, accent, heading, text, bg, surface, muted, border`) and
2 font tokens (`heading-font, body-font`) map to **named Elementor custom globals**.

Flow:

1. **Gather** the brand — 8 colors (hex), 2 font families, logo — from the user's
   brief, a Figma file, or by asking. If fewer than 8 colors are given, derive the
   rest (`surface` = tint of `bg`; `muted` = lower-contrast `text`; `border` = light
   grey) and state the derivation.
2. **Apply** — `update-global-colors` with the 8 `{_id, title, color}` entries, then
   `update-global-typography` with the 2 font entries. (Exact payloads in the
   reference.)
3. **Record** the token→value map back to the user so recipes and later edits reuse it.
4. **Verify** — `get-global-settings` shows the 8 colors + 2 fonts by name.

After intake, **bind widget colors to these globals** (or use the recorded token
value when setting directly). Introducing an ad-hoc hex/font mid-build breaks brand
consistency — don't.

## Recipe library

Reusable, brand-token-driven build sequences for common sections. **Before building a
section, consult the matching recipe** and bind everything to the brand tokens (see
"Brand kit" above). Classic-first; apply the recipe's Pro/V4 variant note when those
engines are active. Full trees + token bindings: [`references/recipes.md`](references/recipes.md).

Available recipes: **Hero**, **Services grid**, **Split (image + text)**, **Stats
band**, **Testimonials**, **CTA band**, **Contact**, **FAQ**, **Pricing**, **Logos
strip**. (The library grows — add a recipe when a new section type recurs.)

Recipes reuse the rest of this skill's rules (native widgets not HTML dumps,
`duplicate-element`/Loop Grid for grids, flat-param convention) — they don't restate
them.

## When the user asks to BUILD — building order

**Studio voice default:** clean, confident, conversion-focused; real copy (no lorem); accessible contrast; consistent spacing scale. Per-client tone comes from the matched vertical (see `references/verticals/`).

**Vertical routing:** if the client matches a known vertical, load its pack first for voice + design system + section flow: `references/verticals/{dental,salon,car-wash,local-business,portfolio}.md`. No match → proceed with the studio voice default + the recipe library.

> Use this section only when the user has explicitly asked you to build something. Do not run this flow on a bare `/siteagent-elementor-studio` invocation.

For a new page, build top-down section by section, in small commits, verifying after each:

1. **Brand kit** — if the brand tokens aren't set yet, run the brand-kit intake flow (see "Brand kit — intake & tokens" above) to establish the named global colors/typography. If already set, confirm via `get-global-settings`.
2. `create-page({title, status: "publish", template: "elementor_canvas"})` — Canvas template removes theme header/footer chrome so your design is the only thing on the page
3. (Via WP-CLI) Set as static front page: `wp option update show_on_front page; wp option update page_on_front `
4. Build sections — **use the matching recipe from the Recipe library** (outer container → inner boxed container, max-width ~1360px → content), bound to brand tokens
5. After each section: `get-page-structure(post_id)` to verify nesting, or just curl the front page
6. **Pause for human review** before building header/footer (which use Header Footer Elementor templates, a different flow)

## When the user asks to EDIT

Approach existing pages surgically — don't rebuild what you don't have to:

1. `list-pages` to find the page they're editing
2. `get-page-structure(post_id)` to see the current widget tree and grab element IDs
3. For a specific element they describe ("the hero headline", "the third listing card"), use `find-element` if needed, then `update-element` with only the fields that change
4. Verify the edit by re-reading `get-page-structure` or curling the rendered page
5. **Never delete a section unless they explicitly ask** — even when restructuring. Use `move-element` or `update-element` first.

## When the user asks to REFERENCE / INSPECT

Read-only tools, no writes. Useful for "show me", "tell me", "what's", "list" requests:

- `list-pages` — what pages exist
- `get-global-settings` — colors, typography, layout settings
- `get-page-structure(post_id)` — what's on a page
- `get-element-settings(element_id)` — exact settings of one widget
- `find-element(post_id, ...)` — locate a widget by content/type

Format the response as a clear summary, not a JSON dump. The user wants understanding, not raw data.

## When the user asks to EXPLORE / "what can you do?"

Give a short menu *(don't dump all 75 tools)*. Point them at the four modes from the First Action Protocol with concrete examples:

- *"Build a homepage from this HTML mockup"* → mode 1
- *"Make the hero text 20% smaller"* → mode 2
- *"Show me what colors are currently set globally"* → mode 3
- *"What pages exist on the site?"* → mode 3

Then ask which mode they want.

## Header/Footer notes

Theme Builder (Pro) is the preferred header/footer path; UAE/HFE is the free fallback. **Full patterns (Theme Builder vs UAE/HFE, nav menu, site-wide header/footer) → load `references/header-footer.md`.**
## Pro-only widgets & features

When Pro is active, native widgets beat HTML: Loop Grid/Carousel, Popups, Dynamic Tags, Sticky header + Motion Effects. **Full per-feature guidance → load `references/pro-widgets.md`.**
## Dynamic data stacks — ACF & Crocoblock/JetEngine (Tier-0)

Bind ACF via Pro dynamic tags; place Jet widgets via `add-widget` with runtime-verified types. Tier-0 scope only. **Full ACF + Crocoblock/JetEngine guidance → load `references/dynamic-data.md`.**
## Forms

If Pro → native Form widget (preferred). If free → Fluent Forms (fallback). **Full form guidance (native Form settings, Fluent Forms class map, alternatives) → load `references/forms.md`.**
## Setup gotchas (what bit me last time)

- **The application password's *label* is not the username.** A user creates an Application Password and gives it a name like "Claude MCP", but the actual WP username remains `admin` or `test` or whatever they set up. If `curl -u "ClaudeMCP:..."` returns 401, try `curl -u "admin:..."` or check `GET /wp-json/wp/v2/users` to find the real slug.
- **Local-by-Flywheel `wp-config.php` says `DB_HOST=localhost`** but the real MySQL is on a per-site Unix socket. WP-CLI fails with "Error establishing a database connection" until you pass `-d mysqli.default_socket=/path/to/mysqld.sock`. The setup script handles this; if doing it manually, find the socket via `find ~/Library/Application\ Support/Local/run -name mysqld.sock`.
- **Neither MCP plugin is on wordpress.org.** Cannot install via REST API by slug — must download zips from GitHub Releases.
- **The elementor-mcp release zipball has an ugly auto-generated folder name** (`Digitizers-elementor-mcp-/`). WordPress uses the folder name as the plugin slug. Repack with a clean `elementor-mcp/` folder before installing.
- **Claude Code only loads `.mcp.json` at startup** — after writing one, the user must quit and reopen.
- **The `detect-elementor-version` tool errored with a schema validation bug** in v1.5.0 (`elementor_pro_version` null vs. schema `string`). Fixed in current builds and useful for the classic-vs-atomic check — but for the plain auth-works smoke test, `list-pages` is still the simplest.
- **Atomic (V4) tools missing on a V4 site?** Older MCP builds gate atomic-tool registration on `ELEMENTOR_VERSION >= 4.0.0`, but Elementor runs atomic as an experiment while the constant still reads `3.x` — so the tools never register and classic writes silently don't persist. Update the elementor-mcp plugin (the detection now keys off the atomic experiment/module), and on tight tool caps (Antigravity) enable **Low-tools mode** so the 5 atomic essentials stay exposed. See the engine-detection section up top.

## Live-host vs Local differences

**Local-by-Flywheel:** Plugin install via the bundled WP-CLI binary at `/Applications/Local.app/Contents/Resources/extraResources/bin/wp-cli/posix/wp` with PHP at `~/Library/Application Support/Local/lightning-services/php-*/bin/darwin-arm64/bin/php` and the per-site MySQL socket. The setup script automates all of this.

**Live host (cPanel/Cloudways/Kinsta/etc.):** Plugin install via WP Admin → Plugins → Add New → Upload Plugin (manual upload of the two zips). Auth is the same — REST API + Application Password. **MCP URL** changes to `https:///wp-json/mcp/elementor-mcp-server`. **Important:** if the live site is HTTPS (it should be), make sure curl/Claude Code can reach it from your local machine — some hosts block non-browser User-Agents on `/wp-json/`. The setup script's "live" path tests this with a single curl before writing `.mcp.json`.

## Tool-loading discipline

The MCP exposes ~75 deferred tools. Don't load them all at once — fetch schemas lazily as you build:

- **First call:** `list-pages` (no schema needed — pre-loaded by ToolSearch when triggered)
- **Before building containers:** load `get-container-schema`, `add-container`, `update-container`
- **Before placing widgets:** load `add-heading`, `add-text-editor`, `add-button`, `add-image`, `add-html` in one batch
- **Before specific widgets:** load `add-tabs`, `add-icon-list`, `add-divider`, `add-spacer` as needed

Use `ToolSearch` query format `select:tool1,tool2,tool3` to load multiple in one call.

## What the MCP **cannot** do (set expectations)

- Install plugins or themes (use WP-CLI or WP Admin instead) — including **Elementor Pro itself** (paid, not on wp.org; the kit only *detects* it)
- Set the static front page (use `wp option update`)
- Build a custom header/footer on Elementor Free without the HFE plugin *(with Pro, use native Theme Builder via `create-theme-template`)*
- Auto-translate arbitrary HTML/CSS into Elementor widgets — you read the source design and emit widget calls
- Pixel-perfect parity with hand-coded HTML — Elementor's flexbox container model is the ceiling *(Pro adds CSS Grid containers, raising it)*

**Pro features are NOT a limitation when Pro is active** — Form widget, Theme Builder, Loop Grid, Popups, Dynamic Tags, and Sticky/Motion are all driven natively (see the Pro sections above). They're only unavailable on the Free tier, where the documented workarounds apply.

## Optional companion tooling — `wordpress-api-pro` (content/SEO/commerce ops)

> **These are separate, opt-in tools — not a capability of this skill.** This skill drives the Elementor MCP only. The companion toolkit below is a *different* project with its **own credentials and permissions**, and you should reach for it **only when the user explicitly asks** for one of the content/SEO/media/ACF/WooCommerce tasks it covers. Do not silently invoke it, and do not treat its abilities as automatically available here.

The Elementor MCP is for **building and editing page structure** (containers, widgets, Pro widgets). It does **not** cover bulk content ops, media-library uploads, SEO metadata, custom fields, or WooCommerce. When the user asks for one of those, a sibling toolkit — **[`wordpress-api-pro`](https://github.com/Digitizers/wordpress-api-pro)** (Python REST scripts, App-Password auth) — fills the gaps. It authenticates with **its own environment-based credentials** (`WP_URL` / `WP_USERNAME` / `WP_APP_PASSWORD`), separate from this skill's `.mcp.json`, though it can target the same site.

This skill is one stage of the studio toolbox (audit → build → content → host → ads). **Full handoffs + a "where am I" router → load `references/lifecycle.md`.**

**When the user explicitly asks for one of these, `wordpress-api-pro` is the right tool instead of the MCP:**

| Task | Script |
|---|---|
| Upload an actual image/file to the media library (then feed its URL/ID to an Elementor Image widget) | `upload_media.py` |
| Read/write SEO meta (Rank Math / Yoast) | `seo_meta.py` |
| Read/write ACF or JetEngine custom fields | `acf_fields.py` / `jetengine_fields.py` |
| List/create/update WooCommerce products | `woo_products.py` |
| Bulk content changes across many posts or **multiple sites** (dry-run first) | `batch_update.py`, `wp.sh` |
| Plain post/page CRUD outside Elementor | `create_post.py` / `update_post.py` / `get_post.py` / `list_posts.py` |

**Division of labor:** build the page with the MCP → upload media + set SEO meta + wire custom fields/products with `wordpress-api-pro`. Both touch `_elementor_data`, but prefer the **MCP** for structured Elementor edits and reserve `wordpress-api-pro`'s `elementor_content.py` for scripted/batch field tweaks.

> Setup: the scripts need Python 3.8+ and `requests` (`pip install requests`, or a venv). Auth via `WP_URL` / `WP_USERNAME` / `WP_APP_PASSWORD` env vars, or `config/sites.json` for multi-site. See that repo's `SKILL.md`.

## Quick reference — the build flow that works *(mode 1 only)*

> Use this flow only after the user has explicitly chosen "Build" or asked to build a new site/page. Do **not** run this flow as a default response to `/siteagent-elementor-studio` — see the First Action Protocol at the top.

  1. setup-elementor-mcp.sh # one-time, ~3 minutes
  2. Quit + reopen Claude Code # picks up .mcp.json
  3. list-pages # confirm auth
  4. get-global-settings # see current kit
  5. update-global-colors + typography
  6. create-page (Elementor Canvas template)
  7. Set as front page via WP-CLI
  8. Build sections top-down, one at a time
  9. After each: get-page-structure or curl the front page
  10. Pause for human review before header/footer

When working from a designed HTML mockup, map the source design to Elementor like this:

- **Brand colors** → `update-global-colors`
- **Brand fonts** → `update-global-typography`
- **Section copy** → `add-heading` + `add-text-editor` widgets
- **Card grids (4+ identical items)** → build one card with native widgets, then `duplicate-element` and `update-element` per copy
- **Tabs/accordions** → native `add-tabs`/`add-accordion` widgets *(HTML allowed inside `tab_content` strings only — see anti-pattern section)*
- **Forms** → real Fluent Forms shortcode via `add-shortcode` widget *(see Fluent Forms section)*
- **Headers/footers** → `elementor-hf` post type with UAE Nav Menu widget for nav

> 🚨 **Final reminder:** Default to native widgets. The HTML widget is only for the four narrow cases listed in the anti-pattern section. Never paste a complete page section as raw HTML — the user must be able to edit the result inside Elementor.

Related skills

Stores durable facts in a categorized, plain-markdown vault on disk, alongside your agent's built-in memory.

by Iván1 installs

Find why your productivity system keeps failing, then apply the smallest fix — capacity math, bottleneck routing, durable local notes.

by Iván1 installs

Fetch raw ad creative, app, ranking, and revenue data from AdMapix as structured JSON.

by fly0pants

Read and write Excel workbooks, worksheets, ranges, tables, and charts in OneDrive through Microsoft Graph with managed OAuth.

by byungkyu800 installs42 stars

More from benkalsky

Browse all skills

Manage Cloudways servers and apps through the official Cloudways MCP, with confirmation-gated writes and per-account isolation.

by benkalsky12 installs1 stars

Manage WordPress sites via REST API with explicit approvals, dry-run batches, and plugin/CPT tooling.

by benkalsky62 installs2 stars

Manage Hostinger VPS, hosting, domains, DNS, Reach email, and billing through the official hostinger-api-mcp server.

by benkalsky7 installs1 stars

Production-grade operational guide for creating, managing, optimizing, and documenting Meta (Facebook/Instagram) ad campaigns via the Facebook Ads MCP. Use w...

by benkalsky19 installs1 stars