Skip to main content
Glama

elementor-live

An MCP server that lets coding agents (Claude Code, Kimi Code, Cursor, any MCP client) work on a live Elementor editor tab like an IDE. The agent sees what you select, changes the canvas through Elementor's own command API ($e.run) inside your Chrome tab — so every change is one undo step, Ctrl+Z works, and nothing fights over the database — moves a ghost cursor so you can follow along, takes pixel-accurate screenshots of what it built, and receives prompts you type directly in the editor with Alt+Space.

The loop:

open_editor → get_canvas_skeleton → inspect_element → mutate_canvas → capture_vision → (design_lint) → save_document
                        ↑                                                                  │
                        └──────────── wait_for_user_prompt (Alt+Space in the editor) ──────┘

There is no WordPress plugin. The server drives a dedicated Chrome window over the Chrome DevTools Protocol (Playwright) and injects a small overlay (cursor, speech bubble, HUD, prompt box) into the editor page.

Requirements

  • Node.js ≥ 20

  • Google Chrome installed (the system Chrome is used; no browser download)

  • A WordPress site with Elementor ≥ 3.x and a user who can edit pages. Tested on Elementor 4.3.5 with the Atomic editor enabled.

Related MCP server: visual-browser-agent

Install

git clone https://github.com/ArchiDoxx/elementor-live.git
cd elementor-live
npm install
npm run build
npx elementor-live doctor

doctor prints the configuration, checks that Chrome is found and the profile directory is writable, and whether ELIVE_WP_URL responds (and authenticates, if REST credentials are set).

First run

ELIVE_WP_URL=https://your-site.example npx elementor-live open

A Chrome window with its own profile (~/.elementor-live/profile) opens. Log in to WordPress once — the login persists in that profile — and open a page with "Edit with Elementor". You can also pass a post ID or an editor URL: npx elementor-live open 42.

Close it with Ctrl+C before you start your MCP client: the profile can only be used by one Chrome process at a time. The MCP server launches the same profile on the first tool call. To keep using a Chrome you started yourself, start it with --remote-debugging-port=9222 and set ELIVE_CDP_URL=http://localhost:9222.

MCP client configuration

Use the absolute path to dist/cli.js of your checkout. More snippets: docs/mcp-clients.md.

Claude Code

claude mcp add elementor-live -e ELIVE_WP_URL=https://your-site.example -- node /abs/path/elementor-live/dist/cli.js

Kimi Code, Cursor and other JSON-configured clients (mcpServers):

{
  "mcpServers": {
    "elementor-live": {
      "command": "node",
      "args": ["/abs/path/elementor-live/dist/cli.js"],
      "env": {
        "ELIVE_WP_URL": "https://your-site.example"
      }
    }
  }
}

The server speaks MCP over stdio; it logs only to stderr.

Tools

Tool

Input

Output

canvas_status

–

connected, editorUrl, postId, title, documentType, elementorVersion, atomicEnabled, selection, lastSeq

open_editor

postId or url

same as canvas_status

get_canvas_skeleton

maxDepth?, includeSettings?, since?

compressed tree {id, elType, widgetType, label, atomic?, children}, selection, nodeCount, events, lastSeq

inspect_element

id

non-default settings, controlsHint, box, computed styles, text, scrollOverflowX, parentChain, children

list_widgets

filter?

[{type, title, categories, atomic}]

get_widget_controls

widgetType

control definitions (type, default, options capped at 30)

mutate_canvas

ops[], label?

per-op results {ok, id?, error?, code?}, historyGrouped, historyTitle

save_document

mode: draft | publish | update

ok, status

capture_vision

id?, device?

PNG image + {box, bytes}

design_lint

id?, grid? (default 4)

findings [{id, rule, severity, detail, suggestion}], checked

agent_say

message, id?, mood?

ok

set_hud

score?, delta?, note?

ok

get_user_activity

since?

events since seq, lastSeq

wait_for_user_prompt

timeoutSec? (default 120, max 600)

{text, selection, seq} or {timeout: true}

wp_rest

method, path, body?

JSON (only when REST credentials are configured)

mutate_canvas operations:

{ "op": "create", "parentId": "c1a2b3", "at": 0, "elType": "widget", "widgetType": "heading", "settings": { "title": "Hello" } }
{ "op": "update_settings", "id": "h4d5e6", "settings": { "title_color": "#1a1a1a" } }
{ "op": "move", "id": "h4d5e6", "parentId": "c7f8g9", "at": 1 }
{ "op": "delete", "id": "b1c2d3" }
{ "op": "duplicate", "id": "c1a2b3" }
{ "op": "set_custom_css", "id": "c1a2b3", "css": "selector { scroll-margin-top: 80px; }" }

All ops of one call form a single undo step. Element IDs come only from get_canvas_skeleton or from create/duplicate results; within one batch, "$0", "$1", … as id/parentId refer to the element created by that op index, so a container and its children fit into one undo step. A failing op does not stop the others.

Errors are returned as tool results with isError: true and a JSON body { "error", "code", "hint" }, e.g. not_connected, no_editor_tab, element_not_found, invalid_ops.

The server also provides the prompt elementor_workflow (a short guide for the agent) and the resource elementor-live://status.

Tag-team with Alt+Space

  1. Ask your agent to "work with me in the editor" — it calls wait_for_user_prompt.

  2. In the Elementor editor, select an element and press Alt+Space. Type what you want ("make this a two-column layout with an image on the right") and press Enter.

  3. The agent receives your text plus the current selection, makes the change (one undo step), shows a speech bubble at the element, and waits for the next prompt.

Edits you make yourself show up in get_user_activity, so the agent can re-read an element before touching it again.

Example prompts

  • "Open page 42 in Elementor and describe its structure."

  • "Build a hero section at the top: full-width container, heading, short text, two buttons side by side. Then show me a screenshot."

  • "The selected section looks cramped on mobile — check it with design_lint and capture_vision on mobile, then propose fixes."

  • "Rename every 'Learn more' button on this page to 'Read the case study'."

  • "Create a draft page 'Pricing' via wp_rest and open it in the editor."

Environment variables

Variable

Default

Meaning

ELIVE_WP_URL

–

Base URL of the WordPress site; needed for open_editor({postId}) and open without arguments

ELIVE_PROFILE_DIR

~/.elementor-live/profile

Chrome profile directory (keeps the WordPress login)

ELIVE_CDP_URL

–

Attach to a running Chrome (e.g. http://localhost:9222) instead of launching one

ELIVE_HEADLESS

false

1/true runs Chrome headless (CI only; you will not see the editor)

ELIVE_WP_USER

–

WordPress user for wp_rest

ELIVE_WP_APP_PASSWORD

–

Application Password for wp_rest (Users → Profile → Application Passwords)

wp_rest is registered only when ELIVE_WP_URL, ELIVE_WP_USER and ELIVE_WP_APP_PASSWORD are all set. It is meant for posts, media and options; it refuses writes containing _elementor_data, because the open document must only be changed through the editor.

Development

bash dev/bootstrap.sh   # WSL/Linux with Docker: WordPress + MariaDB + wp-cli on http://localhost:8080, Elementor and a test page
npm test                # unit tests (vitest)
npm run typecheck
npm run e2e             # Playwright flows against the dev instance (real $e.run, undo, save, screenshot)
npx tsx e2e/mcp-smoke.ts  # acceptance run: built server over stdio, driven by a real MCP client
npm run dev             # run the CLI from source via tsx

The dev instance's admin login defaults to admin / admin (ELIVE_DEV_USER, ELIVE_DEV_PASS, ELIVE_DEV_URL override it).

Limitations

  • Elementor V4 atomic elements (e-flexbox, e-heading, …) are shown in the skeleton and can be created, but their style props cannot be edited via settings yet (unsupported_atomic). Prefer classic widgets inside container elements.

  • set_custom_css writes custom_css, which needs Elementor Pro.

  • One editor tab and one document at a time; theme-builder documents are not targeted.

  • No voice input.

  • Long wait_for_user_prompt calls depend on your client's request timeout; use a smaller timeoutSec if your client aborts early.

Architecture

See the design spec: docs/superpowers/specs/2026-10-26-elementor-live-design.md (German). In short: src/server.ts and src/tools/* (MCP layer), src/browser/* (Playwright session, $e.run snippets, overlay), src/core/* (pure skeleton/ops/lint logic), src/wp/rest.ts (optional REST client).

License

MIT — see LICENSE.

Available Tools

14 tools
agent_saySpeak in the editorA

Show a short speech bubble from the agent inside the user's editor, anchored at an element (id, the ghost cursor moves there) or at the top of the canvas. Use it to explain what you are about to change, to point at lint findings, or to ask the user something while they watch the canvas. Keep messages under ~200 characters. mood tints the bubble: neutral, happy, thinking, warning, sassy.

Inputs: message (required), id, mood.

canvas_statusEditor statusA
Read-only

Report the state of the live Elementor editor tab: whether a session is connected, the editor URL, postId, document title and type, Elementor version, whether V4 atomic elements are enabled, the current user selection (element IDs) and lastSeq of the activity buffer. Call this first in every session; if connected is false or the call fails with not_connected/no_editor_tab, call open_editor. The first call may start the dedicated Chrome window.

Inputs: no named parameters.

capture_visionCapture screenshotA
Read-only

Take a pixel-accurate PNG screenshot of one element (id) or of the whole preview viewport (no id), as rendered in the live editor; the ghost cursor moves to the element first so the user sees what you look at. device switches the editor's responsive mode (desktop, tablet, mobile) before capturing. Use it after every meaningful mutation to verify the visual result instead of assuming it. Returns the image plus {box, bytes}.

Inputs: id, device.

design_lintDesign lintA
Read-only

Check rendered geometry for common layout problems: spacing off the grid (grid, default 4 px), children overflowing their parent, overlapping siblings, headings that wrap, horizontal scroll, tap targets smaller than 44 px, empty containers. With id only that element and its descendants are reported. Findings are suggestions: show them to the user (agent_say with id) and only fix what the user wants — do not silently override deliberate design choices.

Inputs: id, grid.

get_canvas_skeletonCanvas skeletonA
Read-only

Return the compressed element tree of the open document: {id, elType, widgetType, label, atomic?, children} per node, plus the current user selection, nodeCount, recent user events (selections, edits, Alt+Space prompts, saves) and lastSeq. This is the only source of element IDs besides create results — never invent IDs. Pass since= to receive only newer events; without it the last 20 events are returned. includeSettings=true adds non-default settings per node (more tokens); use inspect_element for a single element instead. maxDepth limits nesting for very large pages.

Inputs: since, maxDepth, includeSettings.

get_user_activityUser activityA
Read-only

Return what the user did in the editor since a sequence number: selections (ids), edits (change events with the history title and ids), Alt+Space prompts (text), saves and navigations, plus lastSeq. Pass since= to get only new events; re-read get_canvas_skeleton or inspect_element for elements the user changed before mutating them, so you do not overwrite their edits.

Inputs: since.

get_widget_controlsWidget controlsA
Read-only

Return the control definitions (setting keys) of a widget type or elType, e.g. "heading", "button" or "container": name, type, label, default, options (capped at 30), section, tab, responsive. Call this before writing settings you have not seen in inspect_element — never guess setting keys. Responsive controls accept suffixed keys (name_tablet, name_mobile). Value shapes follow the control type: dimensions {unit, top, right, bottom, left, isLinked}, sliders {unit, size}, colors as hex strings, selects as one of the option keys.

Inputs: widgetType (required).

inspect_elementInspect elementA
Read-only

Inspect one element by ID (from get_canvas_skeleton or a create result): non-default settings, controlsHint (the most relevant setting keys for this widget type), rendered bounding box in the preview iframe, an excerpt of computed styles (display, padding, margins, gap, font size, line height, overflow), visible text, whether it overflows horizontally, its parent chain and direct children. Use it before update_settings to see the current values and their exact shapes; call get_widget_controls when you need a key that is not in controlsHint. Fails with element_not_found for unknown IDs.

Inputs: id (required).

list_widgetsList widgetsA
Read-only

List the widget types registered in this Elementor installation as [{type, title, categories, atomic}]. Use filter (case-insensitive substring of type or title) to search, e.g. "button" or "image". Use the type as widgetType in a create op with elType "widget". Prefer classic widgets (heading, text-editor, button, image, icon-list …) inside "container" elements; atomic types (prefixed "e-") are Elementor V4 elements and may reject classic settings.

Inputs: filter.

mutate_canvasMutate canvasA
Destructive

Apply a batch of operations to the open document through Elementor's own command API, in the user's editor tab, as ONE undo step titled after label (the user can press Ctrl+Z). Ops: create {parentId?, at?, elType, widgetType?, settings?} (omit parentId to append at document root; elType "widget" needs widgetType), update_settings {id, settings} (merges keys), move {id, parentId, at?}, delete {id}, duplicate {id}, set_custom_css {id, css} (Elementor Pro custom_css; "selector" refers to the element). IDs must come from get_canvas_skeleton or from earlier create/duplicate results (returned per op as id) — never invent them. Within one batch, "$N" (e.g. "$0") as id/parentId refers to the element created by op N of the same batch, so a container and its children can be built in one undo step. Prefer elType "container" with flex settings (flex_direction, flex_gap, flex_justify_content, flex_align_items) over legacy section/column. Check keys with get_widget_controls first. Atomic V4 elements (elType/widgetType "e-*") can be created, but may reject classic settings (code unsupported_atomic). A failing op does not stop the others; inspect results[] and verify visually with capture_vision.

Inputs: ops (required), label.

open_editorOpen Elementor editorA
Idempotent

Navigate the dedicated Chrome tab to the Elementor editor of a page and wait until the editor ($e) is ready. Pass either postId (requires ELIVE_WP_URL; opens /wp-admin/post.php?post=&action=elementor) or a full editor url. Returns the same status object as canvas_status. If WordPress shows a login page, ask the user to log in once in that Chrome window and call open_editor again. After opening, call get_canvas_skeleton to learn element IDs.

Inputs: url, postId.

save_documentSave documentA
Idempotent

Save the open document via Elementor's save commands. mode "draft" saves a draft/revision without publishing, "update" saves an already published page, "publish" publishes it. Only save when the user asked for it or confirmed the result; mutations are visible in the editor before saving.

Inputs: mode (required).

set_hudUpdate HUDA
Idempotent

Update the small heads-up badge in the editor top bar: score (e.g. a 0–100 design quality estimate), delta (change since the last update, shown as +/-) and a short note. Purely informational for the user; it does not change the document.

Inputs: note, delta, score.

wait_for_user_promptWait for user promptA
Read-only

Block until the user presses Alt+Space in the Elementor editor and submits a prompt, then return {text, selection, seq} — selection holds the element IDs the user had selected, which is what "this" refers to in the prompt. Returns {timeout: true} after timeoutSec (default 120, max 600). Use it for tag-team work: finish a task, call agent_say to report, then wait for the next prompt in a loop. If your MCP client enforces a short request timeout, pass a smaller timeoutSec and call again.

Inputs: timeoutSec.

Tool Schema Changelog

Recent tool additions, removals, and schema changes observed during successful MCP inspections.

Loading tool schema changelog…

    TDQS

    A4.2/5.0

    Scored across 14 tools

    Disambiguation4/5

    Most tools target clearly distinct resources or actions (skeleton vs. single-element inspect, mutate vs. save, widgets vs. controls). There is mild overlap in the user-event area: get_canvas_skeleton also returns recent events, get_user_activity is a dedicated event feed, and wait_for_user_prompt surfaces Alt+Space prompts, so an agent could pick the wrong one. canvas_status and open_editor also both return the same status object, though the descriptions sequence them clearly.

    Naming Consistency4/5

    The dominant pattern is snake_case verb_noun (get_canvas_skeleton, list_widgets, inspect_element, save_document, capture_vision, open_editor, mutate_canvas). A few deviate to noun_verb or noun_state forms (canvas_status, design_lint, agent_say, set_hud), but names remain readable and largely predictable. Minor inconsistency, not confusing.

    Tool Count5/5

    14 tools is well within the healthy range and each has a distinct role: session setup, discovery, mutation, verification, and user interaction. Nothing feels padded; even the UX tools (set_hud, agent_say) serve the live-editor collaboration model.

    Completeness4/5

    The surface covers the full edit lifecycle: connect, discover widgets/controls, read tree and element detail, batch create/update/move/delete/duplicate/custom CSS, save, screenshot-verify, and lint. Gaps exist: no media library/upload tool (images must already exist as attachment IDs), no document/page-level settings or global styles access, no history/undo tool (relies on the user pressing Ctrl+Z), and no template save/insert.

    Maintenance

    ActivityMaintained
    ResponsivenessNo recent community issues

    Related MCP Connectors

    Related MCP Servers

    • A
      license
      Not graded
      quality
      B
      maintenance
      Enables coding agents to capture pixel-accurate screenshots and DOM state from localhost apps, receive user-drawn instructions and reference images, and manage implementation review cycles.
      21 npm
      MIT
    • A
      license
      Not graded
      quality
      B
      maintenance
      Enables coding agents to control the user's real Chrome or Firefox tabs, with their existing logins, to read pages, click, type, fill forms, take screenshots, inspect network traffic and run JavaScript. It works through a paired browser extension over a local WebSocket connection, so automation happens in the browser the user is actually looking at, including shadow DOM, cross-origin iframes, dialogs and single-page apps.
      MIT