{"id":"91da27ff-e26f-4a63-82dc-70fdfe47adb4","entityType":"agent","slug":"clawhub-benkalsky-siteagent-elementor-studio","name":"SiteAgent Elementor Studio","canonicalUrl":"https://www.xpersona.co/agent/clawhub-benkalsky-siteagent-elementor-studio","canonicalPath":"/agent/clawhub-benkalsky-siteagent-elementor-studio","generatedAt":"2026-10-11T14:14:10.044Z","source":"CLAWHUB","claimStatus":"UNCLAIMED","verificationTier":"NONE","summary":{"evidence":{"source":"CLAWHUB","verified":false,"confidence":"medium","updatedAt":"2026-10-11T11:08:17.193Z","emptyReason":null},"description":"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 covers initial install of the MCP Adapter + elementor-mcp plugins, app-password auth wiring, schema-loading discipline, and the widget-vs-HTML decision tree. SKIP for Bricks, Divi, Beaver Builder, or non-Elementor WordPress builds.","descriptionLabel":"Source description","evidenceSummary":"Capability contract not published. No trust telemetry is available yet. 1.1K downloads reported by the source. Last updated 10/11/2026.","installCommand":"clawhub skill install s17b8qfjfq0g8kveh2g8v1179h83ejwb:siteagent-elementor-studio","sourceUrl":"https://clawhub.ai/benkalsky/siteagent-elementor-studio","homepage":"https://clawhub.ai/benkalsky/skills/siteagent-elementor-studio","primaryLinks":[{"label":"View on ClawHub","url":"https://clawhub.ai/benkalsky/siteagent-elementor-studio","kind":"source"},{"label":"Homepage","url":"https://clawhub.ai/benkalsky/skills/siteagent-elementor-studio","kind":"homepage"}],"safetyScore":84,"overallRank":62,"popularityScore":61,"trustScore":null,"claimedByName":null,"isOwner":false,"seoDescription":"SiteAgent Elementor Studio technical dossier on Xpersona with agent coverage, OPENCLEW support, and live trust metadata."},"coverage":{"evidence":{"source":"public-profile","verified":false,"confidence":"medium","updatedAt":"2026-10-11T11:08:17.193Z","emptyReason":null},"protocols":[{"protocol":"OPENCLEW","label":"OpenClaw","status":"self-declared","notes":"Declared in the public agent profile."}],"capabilities":[],"verifiedCount":0,"selfDeclaredCount":1,"capabilityMatrix":{"rows":[{"key":"OPENCLEW","type":"protocol","support":"unknown","confidenceSource":"profile","notes":"Listed on profile"}],"flattenedTokens":"protocol:OPENCLEW|unknown|profile"}},"adoption":{"evidence":{"source":"CLAWHUB","verified":false,"confidence":"medium","updatedAt":"2026-10-11T11:08:17.193Z","emptyReason":null},"stars":null,"forks":null,"downloads":1080,"packageName":null,"latestVersion":"1.7.1","tractionLabel":"1.1K downloads"},"release":{"evidence":{"source":"CLAWHUB","verified":false,"confidence":"medium","updatedAt":"2026-10-11T11:08:17.124Z","emptyReason":null},"lastUpdatedAt":"2026-10-11T11:08:17.193Z","lastCrawledAt":"2026-10-11T11:08:17.124Z","lastIndexedAt":null,"nextCrawlAt":"2026-10-12T11:08:17.124Z","lastVerifiedAt":null,"highlights":[{"version":"1.7.1","createdAt":"2026-09-14T23:05:39.989Z","changelog":"siteagent-elementor-studio 1.7.1 - Updated installer to pin and describe the new Digitizers/elementor-mcp fork v1.34.1 (was v1.24.0). - Noted in documentation that the fork now includes a built-in update checker (since v1.28.0), surfacing new GitHub releases in the WordPress Updates screen. - Clarified not to use the fork together with the Premium plugin due to naming conflicts. - Removed redundant documentation file (skill-card.md). - Minor documentation and setup instruction improvements.","fileCount":21,"zipByteSize":88046},{"version":"1.7.0","createdAt":"2026-09-14T22:07:02.039Z","changelog":"siteagent-elementor-studio v1.7.0 - Updated permissions and environment handling for plugin release pinning and integrity verification; now defaults to a kit-pinned plugin version and requires digests for all downloads. - Improved setup script and documentation to clarify local vs remote path handling and override behavior for EMCP_PIN_VERSION. - Removed the obsolete skill-card.md file for simplification. - Minor clarification and restructuring of descriptions around allowed shell/network/filesystem/env actions.","fileCount":21,"zipByteSize":86177},{"version":"1.6.1","createdAt":"2026-09-14T21:14:36.629Z","changelog":"siteagent-elementor-studio 1.6.1 - Minor documentation and skill metadata updates in SKILL.md. - Removed redundant or outdated file: skill-card.md. - No functional or user-facing changes in the code or setup scripts.","fileCount":21,"zipByteSize":84644},{"version":"1.6.0","createdAt":"2026-09-13T16:20:52.331Z","changelog":"siteagent-elementor-studio v1.6.0 - Updated documentation in SKILL.md to reflect recent clarifications and protocol improvements. - Removed obsolete skill-card.md file. - Minor script or setup updates in setup-elementor-mcp.sh. - No changes to user-facing features or core functionality.","fileCount":21,"zipByteSize":84668},{"version":"1.5.0","createdAt":"2026-09-13T02:26:59.985Z","changelog":"**Security and setup improvements for siteagent-elementor-studio 1.5.0** - Verifies the downloaded plugin zip against a sha256 digest (from the GitHub release API or supplied via EMCP_EXPECTED_SHA256 env) before unpacking or installing. - Refuses plaintext HTTP connections to non-local hosts unless explicitly allowed via the new WP_ALLOW_HTTP environment variable. - Sets .mcp.json file permissions to mode 600 before writing WordPress credentials. - Updated documentation to describe new security parameters and environment variable usage. - Removed the obsolete skill-card.md file.","fileCount":21,"zipByteSize":81267},{"version":"1.4.0","createdAt":"2026-09-12T22:48:15.665Z","changelog":"**Improved setup detection, configuration, and documentation flow.** - Enhanced first-session setup: now detects and handles placeholder `.mcp.json` configs, with clear instructions for both environment-variable-based setup and interactive script-based setup. - Updated SKILL.md to clarify how to locate and run the setup script, supporting installations from the skill directory when possible, with fallback guidance if missing. - Expanded setup guidance on running in directory contexts with tracked placeholder configs versus per-site config, helping users avoid credential leakage and misconfiguration. - Improved documentation for edge-case setup states and failure modes. - Removed obsolete or redundant documentation files, including `skill-card.md`.","fileCount":21,"zipByteSize":74352},{"version":"1.3.2","createdAt":"2026-07-08T16:07:56.204Z","changelog":"**siteagent-elementor-studio v1.3.2** - Updated documentation to clarify that the built-in MCP engine is now v1.24.0 (up to 118 tools, Elementor 4.x-correct). - Clarified Pro vs Free server detection: using the tool presence check is now emphasized as the definitive signal; notes added on the reliability of detect-elementor-version. - Removed references to a prior MCP server schema bug (now fixed) and improved instructions accordingly. - Minor clarifications and updates to setup instructions and conventions for first-time sessions.","fileCount":21,"zipByteSize":71798},{"version":"1.3.0","createdAt":"2026-07-08T10:48:20.912Z","changelog":"**Expanded Elementor 4 (atomic/V4) support and error recovery.** - Adds authoring capabilities for the Elementor 4 design system: Global Classes, Variables (design tokens), and Interactions on atomic sites. - Introduces error recovery instructions for fork-specific schema and governance issues on atomic/V4. - Adds references for CRUD in the atomic design system, error recovery patterns, and guidance for migrating from v3 to v4. - Documentation updates: describes expanded atomic engine capabilities and clarifies action flows on atomic sites.","fileCount":21,"zipByteSize":70854}]},"execution":{"evidence":{"source":"CLAWHUB","verified":false,"confidence":"low","updatedAt":null,"emptyReason":"No published capability contract is available yet."},"installCommand":"clawhub skill install s17b8qfjfq0g8kveh2g8v1179h83ejwb:siteagent-elementor-studio","setupComplexity":"low","setupSteps":["Install using `clawhub skill install s17b8qfjfq0g8kveh2g8v1179h83ejwb:siteagent-elementor-studio` in an isolated environment before connecting it to live workloads.","No published capability contract is available yet, so validate auth and request/response behavior manually.","Review the upstream CLAWHUB listing at https://clawhub.ai/benkalsky/siteagent-elementor-studio before using production credentials."],"contract":{"contractStatus":"missing","authModes":[],"requires":[],"forbidden":[],"supportsMcp":false,"supportsA2a":false,"supportsStreaming":false,"inputSchemaRef":null,"outputSchemaRef":null,"dataRegion":null,"contractUpdatedAt":null,"sourceUpdatedAt":null,"freshnessSeconds":null},"invocationGuide":{"preferredApi":{"snapshotUrl":"https://www.xpersona.co/api/v1/agents/clawhub-benkalsky-siteagent-elementor-studio/snapshot","contractUrl":"https://www.xpersona.co/api/v1/agents/clawhub-benkalsky-siteagent-elementor-studio/contract","trustUrl":"https://www.xpersona.co/api/v1/agents/clawhub-benkalsky-siteagent-elementor-studio/trust"},"curlExamples":["curl -s \"https://www.xpersona.co/api/v1/agents/clawhub-benkalsky-siteagent-elementor-studio/snapshot\"","curl -s \"https://www.xpersona.co/api/v1/agents/clawhub-benkalsky-siteagent-elementor-studio/contract\"","curl -s \"https://www.xpersona.co/api/v1/agents/clawhub-benkalsky-siteagent-elementor-studio/trust\""],"jsonRequestTemplate":{"query":"summarize this repo","constraints":{"maxLatencyMs":2000,"protocolPreference":["OPENCLEW"]}},"jsonResponseTemplate":{"ok":true,"result":{"summary":"...","confidence":0.9},"meta":{"source":"CLAWHUB","generatedAt":"2026-10-11T14:14:10.038Z"}},"retryPolicy":{"maxAttempts":3,"backoffMs":[500,1500,3500],"retryableConditions":["HTTP_429","HTTP_503","NETWORK_TIMEOUT"]}},"endpoints":{"dossierUrl":"https://www.xpersona.co/api/v1/agents/clawhub-benkalsky-siteagent-elementor-studio/dossier","snapshotUrl":"https://www.xpersona.co/api/v1/agents/clawhub-benkalsky-siteagent-elementor-studio/snapshot","contractUrl":"https://www.xpersona.co/api/v1/agents/clawhub-benkalsky-siteagent-elementor-studio/contract","trustUrl":"https://www.xpersona.co/api/v1/agents/clawhub-benkalsky-siteagent-elementor-studio/trust"}},"reliability":{"evidence":{"source":"runtime-metrics","verified":false,"confidence":"low","updatedAt":null,"emptyReason":"No trust, reliability, or runtime telemetry is available."},"trust":{"status":"unavailable","handshakeStatus":"UNKNOWN","verificationFreshnessHours":null,"reputationScore":null,"p95LatencyMs":null,"successRate30d":null,"fallbackRate":null,"attempts30d":null,"trustUpdatedAt":null,"trustConfidence":"unknown","sourceUpdatedAt":null,"freshnessSeconds":null},"decisionGuardrails":{"doNotUseIf":["Contract metadata is missing or unavailable for deterministic execution."],"safeUseWhen":[],"riskFlags":["missing_or_unavailable_contract","trust_data_unavailable","schema_references_missing"],"operationalConfidence":"low"},"executionMetrics":{"observedLatencyMsP50":null,"observedLatencyMsP95":null,"estimatedCostUsd":null,"uptime30d":null,"rateLimitRpm":null,"rateLimitBurst":null,"lastVerifiedAt":null,"verificationSource":null},"runtimeMetrics":{"successRate":null,"avgLatencyMs":null,"avgCostUsd":null,"hallucinationRate":null,"retryRate":null,"disputeRate":null,"p50Latency":null,"p95Latency":null,"lastUpdated":null}},"benchmarks":{"evidence":{"source":"no-benchmark-data","verified":false,"confidence":"low","updatedAt":null,"emptyReason":"No benchmark suites or observed failure patterns are available."},"suites":[],"failurePatterns":[]},"artifacts":{"evidence":{"source":"CLAWHUB","verified":false,"confidence":"medium","updatedAt":"2026-10-11T11:08:17.193Z","emptyReason":null},"readme":"Skill: SiteAgent Elementor Studio\n\nOwner: benkalsky\n\nSummary: 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 covers initial install of the MCP Adapter + elementor-mcp plugins, app-password auth wiring, schema-loading discipline, and the widget-vs-HTML decision tree. SKIP for Bricks, Divi, Beaver Builder, or non-Elementor WordPress builds.\n\nTags: latest:1.7.1\n\nVersion history:\n\nv1.7.1 | 2026-09-14T23:05:39.989Z | auto\n\nsiteagent-elementor-studio 1.7.1\n\n- Updated installer to pin and describe the new Digitizers/elementor-mcp fork v1.34.1 (was v1.24.0).\n- Noted in documentation that the fork now includes a built-in update checker (since v1.28.0), surfacing new GitHub releases in the WordPress Updates screen.\n- Clarified not to use the fork together with the Premium plugin due to naming conflicts.\n- Removed redundant documentation file (skill-card.md).\n- Minor documentation and setup instruction improvements.\n\nv1.7.0 | 2026-09-14T22:07:02.039Z | auto\n\nsiteagent-elementor-studio v1.7.0\n\n- Updated permissions and environment handling for plugin release pinning and integrity verification; now defaults to a kit-pinned plugin version and requires digests for all downloads.\n- Improved setup script and documentation to clarify local vs remote path handling and override behavior for EMCP_PIN_VERSION.\n- Removed the obsolete skill-card.md file for simplification.\n- Minor clarification and restructuring of descriptions around allowed shell/network/filesystem/env actions.\n\nv1.6.1 | 2026-09-14T21:14:36.629Z | auto\n\nsiteagent-elementor-studio 1.6.1\n\n- Minor documentation and skill metadata updates in SKILL.md.\n- Removed redundant or outdated file: skill-card.md.\n- No functional or user-facing changes in the code or setup scripts.\n\nv1.6.0 | 2026-09-13T16:20:52.331Z | auto\n\nsiteagent-elementor-studio v1.6.0\n\n- Updated documentation in SKILL.md to reflect recent clarifications and protocol improvements.\n- Removed obsolete skill-card.md file.\n- Minor script or setup updates in setup-elementor-mcp.sh.\n- No changes to user-facing features or core functionality.\n\nv1.5.0 | 2026-09-13T02:26:59.985Z | auto\n\n**Security and setup improvements for siteagent-elementor-studio 1.5.0**\n\n- Verifies the downloaded plugin zip against a sha256 digest (from the GitHub release API or supplied via EMCP_EXPECTED_SHA256 env) before unpacking or installing.\n- Refuses plaintext HTTP connections to non-local hosts unless explicitly allowed via the new WP_ALLOW_HTTP environment variable.\n- Sets .mcp.json file permissions to mode 600 before writing WordPress credentials.\n- Updated documentation to describe new security parameters and environment variable usage.\n- Removed the obsolete skill-card.md file.\n\nv1.4.0 | 2026-09-12T22:48:15.665Z | auto\n\n**Improved setup detection, configuration, and documentation flow.**\n\n- Enhanced first-session setup: now detects and handles placeholder `.mcp.json` configs, with clear instructions for both environment-variable-based setup and interactive script-based setup.\n- Updated SKILL.md to clarify how to locate and run the setup script, supporting installations from the skill directory when possible, with fallback guidance if missing.\n- Expanded setup guidance on running in directory contexts with tracked placeholder configs versus per-site config, helping users avoid credential leakage and misconfiguration.\n- Improved documentation for edge-case setup states and failure modes.\n- Removed obsolete or redundant documentation files, including `skill-card.md`.\n\nv1.3.2 | 2026-07-08T16:07:56.204Z | auto\n\n**siteagent-elementor-studio v1.3.2**\n\n- Updated documentation to clarify that the built-in MCP engine is now v1.24.0 (up to 118 tools, Elementor 4.x-correct).\n- Clarified Pro vs Free server detection: using the tool presence check is now emphasized as the definitive signal; notes added on the reliability of detect-elementor-version.\n- Removed references to a prior MCP server schema bug (now fixed) and improved instructions accordingly.\n- Minor clarifications and updates to setup instructions and conventions for first-time sessions.\n\nv1.3.0 | 2026-07-08T10:48:20.912Z | auto\n\n**Expanded Elementor 4 (atomic/V4) support and error recovery.**\n\n- Adds authoring capabilities for the Elementor 4 design system: Global Classes, Variables (design tokens), and Interactions on atomic sites.\n- Introduces error recovery instructions for fork-specific schema and governance issues on atomic/V4.\n- Adds references for CRUD in the atomic design system, error recovery patterns, and guidance for migrating from v3 to v4.\n- Documentation updates: describes expanded atomic engine capabilities and clarifies action flows on atomic sites.\n\nv1.2.1 | 2026-07-07T22:40:48.509Z | auto\n\n**Minor update with documentation and naming improvements.**\n\n- Updated documentation in SKILL.md for clarity and detail.\n- Clarified the initial working protocol and user prompt behavior.\n- Improved references to skill naming to consistently use \"SiteAgent Elementor Studio.\"\n- No functional or code changes, just documentation and descriptive updates.\n\nv1.2.0 | 2026-07-07T21:44:52.356Z | auto\n\nsiteagent-elementor-studio v1.2.0\n\n- Renamed skill and all mentions from \"elementor-pro-studio\" to \"siteagent-elementor-studio\"\n- Updated SKILL.md and invocation instructions to use the new skill name (including CLI and menu prompts)\n- Removed outdated skill-card.md\n- No changes to core protocols, permissions, or setup flow\n\nv1.1.2 | 2026-07-03T23:11:05.776Z | auto\n\n**elementor-pro-studio 1.1.2**\n\n- Added explicit skill permissions to SKILL.md, clarifying shell, network, filesystem, and environment variable usage.\n- Updated setup instructions: the setup script (`setup-elementor-mcp.sh`) is now offered only after explicit user confirmation and is never run automatically.\n- Removed the redundant `skill-card.md` file.\n- Various minor documentation clarifications in SKILL.md regarding when and how script and shell actions are triggered.\n\nv1.1.1 | 2026-07-03T21:19:49.535Z | auto\n\nelementor-pro-studio v1.1.1\n\n- Documentation updated: project link for the `elementor-mcp` server now points to the `Digitizers/elementor-mcp` fork, reflecting correct upstream and version compatibility.\n- Minor clarifications in setup instructions and references for improved accuracy.\n\nv1.1.0 | 2026-07-03T20:38:29.564Z | auto\n\n**Major update with improved detection, user flow, and setup guidance**\n\n- Added auto-detection for Elementor Pro, engine type (classic vs atomic/V4), and third-party integrations (ACF, Crocoblock/JetEngine).\n- Introduced a strict \"ask before doing\" protocol; skill now presents a menu and waits for user input before any write or destructive action.\n- Outlined safe read-only actions (list pages, get global settings) permitted prior to user selection.\n- Documented first-session setup instructions, including plugin installation script, for both local and live WordPress hosts.\n- Included clear branching based on detected Pro/free and engine type; skill adapts responses and tool usage accordingly.\n- Expanded documentation to cover best practices, integration checks, and troubleshooting common setup issues.\n\nArchive index:\n\nArchive v1.7.1: 21 files, 88046 bytes\n\nFiles: references/atomic-v4.md (9593b), references/brand-kit.md (3625b), references/design-system-crud.md (9059b), references/dynamic-data.md (3406b), references/engine-and-premium.md (7497b), references/error-recovery.md (7340b), references/forms.md (8624b), references/header-footer.md (3947b), references/lifecycle.md (1805b), references/pro-widgets.md (3128b), references/recipes.md (11027b), references/v3-to-v4-conversion.md (7250b), references/verticals/car-wash.md (2617b), references/verticals/dental.md (2832b), references/verticals/local-business.md (2647b), references/verticals/portfolio.md (2701b), references/verticals/salon.md (2527b), setup-elementor-mcp.sh (72430b), skill-card.md (3082b), SKILL.md (41569b), _meta.json (145b)\n\nFile v1.7.1:SKILL.md\n\n---\nname: siteagent-elementor-studio\nversion: 1.7.1\nlicense: MIT\ndescription: 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 covers initial install of the MCP Adapter + elementor-mcp plugins, app-password auth wiring, schema-loading discipline, and the widget-vs-HTML decision tree. SKIP for Bricks, Divi, Beaver Builder, or non-Elementor WordPress builds.\npermissions:\n  shell: \"Runs the bundled setup script (files/setup-elementor-mcp.sh) — only on explicit user confirmation. It shells out to curl/unzip/zip/python3 and, for Local sites, drives Local by Flywheel's bundled WP-CLI (plugin install/activate) against the running site's PHP + MySQL socket.\"\n  network:\n    - \"GitHub release download over HTTPS from the trusted Digitizers/elementor-mcp repo (api.github.com + release asset host) — the elementor-mcp plugin zip. By default the release this kit pins (EMCP_DEFAULT_VERSION in the setup script), checked before it is unpacked or installed against the sha256 recorded beside the pin — out of band from the download. EMCP_PIN_VERSION=<tag> or =latest selects another release, checked against the digest that release publishes (integrity, not provenance) or EMCP_EXPECTED_SHA256. Nothing is installed unverified\"\n    - \"The target WordPress site's REST API (/wp-json/ — auth check, plugin list/install, MCP route verification). Plaintext http:// is refused for a non-local host unless that exact host is named in WP_ALLOW_HTTP, because the run sends a reusable application password on every request\"\n  filesystem:\n    - \"Writes .mcp.json in the current working directory, created mode 600 before the credential is written (it embeds a reusable Basic-Auth WordPress credential), and appends .mcp.json to .gitignore there\"\n    - \"Reads Local by Flywheel site paths + bundled WP-CLI/PHP binaries; creates a temp working dir for the plugin zip\"\n  env:\n    - \"WP_URL / WP_USERNAME / WP_APP_PASSWORD (when used to supply the target site + Application Password auth)\"\n    - \"EMCP_PIN_VERSION (optional — a release tag, or latest, instead of the release this kit pins; the kit's pin never downgrades an installed newer plugin — the run stops and names this override)\"\n    - \"EMCP_EXPECTED_SHA256 (optional — verify the plugin zip against a digest obtained out of band; required for a release that publishes no digest)\"\n    - \"WP_ALLOW_HTTP (comma-separated host list — permits plaintext http for exactly those hosts; a blanket value is not a hostname and permits nothing)\"\n---\n\n# SiteAgent Elementor Studio Skill\n\nYou 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.\n\n## 🛑 First Action Protocol — ASK BEFORE DOING\n\n**When this skill is invoked, do not start running tools. Ask the user what they want first.**\n\n> **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.\n\nIf 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.\n\nOtherwise *(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:**\n\n```\nWhat would you like to do with your Elementor site?\n\n  1. Build       — create new pages or sections from a design\n  2. Edit        — change something on an existing page\n  3. Reference   — inspect current state (pages, colors, fonts, content)\n  4. Explore     — show me what's possible / what can the MCP do here\n```\n\nDo **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.\n\n### Read-only \"smoke test\" calls that are always safe to run\n\nWhen the user picks any option, you can run these **before** asking follow-up questions, since they help frame the next response:\n\n- `mcp__elementor__elementor-mcp-list-pages` — confirms auth + lists what's there\n- `mcp__elementor__elementor-mcp-get-global-settings` — current colors/fonts kit\n\nThat's it for unprompted tool calls. **Anything that creates, modifies, or deletes data requires the user to have explicitly asked for it.**\n\n## When this skill applies\n\n- The user mentions Elementor MCP, types `/siteagent-elementor-studio`, or says \"use the Elementor MCP\"\n- A `.mcp.json` in the project registers an MCP server pointing at `wp-json/mcp/elementor-mcp-server`\n- The user asks to build, edit, inspect, or troubleshoot an Elementor page\n- Tools beginning with `mcp__elementor__elementor-mcp-*` are available\n\n## First-session setup (when MCP not yet connected)\n\n> **Engine:** this skill drives our fork `Digitizers/elementor-mcp` (v1.34.1, the release this kit's installer pins; Elementor 4.x-correct), a single self-contained plugin — no Freemius, no phone-home. Since fork v1.28.0 it carries its own update checker, which **offers** later GitHub Releases on the site's normal Updates screens (release-only, a user agent naming only the plugin); installing one is WordPress's ordinary update flow, and the fork does not switch on WordPress's per-plugin auto-update. **Never run it alongside the paid \"MCP Tools for Elementor (Premium)\" — same class names → fatal.** Details + switch commands → `references/engine-and-premium.md`.\n\nIf the user has a WordPress site but no **working** `elementor` MCP connection — either no `.mcp.json` at all, or only this kit's committed placeholder config (values like `\"WP_URL\": \"${WP_URL:-}\"` with those env vars unset) and no `mcp__elementor__elementor-mcp-*` tools loaded:\n\n> In the placeholder case there are two fixes, not one: **export `WP_URL` / `WP_USERNAME` / `WP_APP_PASSWORD`** (the committed config reads them — fastest in this repo's checkout or a cloud session), or run the wizard below from a **separate per-site project directory** (it refuses to write credentials into the tracked placeholder file).\n\n1. **Check whether they're using Local-by-Flywheel or a live host.** Setup paths differ.\n2. **Run the bundled setup script** from the loaded skill's directory — it handles plugin install, auth wiring, and `.mcp.json` generation interactively for both flavors. The skill's base directory is announced when this skill loads (look for the filesystem path in the skill load message). Replace `<skill-dir>` with that announced path:\n   ```bash\n   bash \"<skill-dir>/setup-elementor-mcp.sh\"\n   ```\n   Keep the quotes — plugin-cache paths can contain spaces (e.g. a Windows\n   profile named `First Last`), and an unquoted substitution splits the path.\n   **If `<skill-dir>/setup-elementor-mcp.sh` is not found** (e.g., from a manual `INSTALL.sh` run instead of plugin-marketplace), use the fallback path for manual installations:\n   ```bash\n   bash ~/.claude/scripts/setup-elementor-mcp.sh\n   ```\n3. 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.\n4. On reopen, the deferred MCP tools will be exposed via ToolSearch — load the ones you need with `select:` queries.\n\nIf something fails, see \"Setup gotchas\" below.\n\n## Working session conventions\n\n### Always do this first\n\n```\nmcp__elementor__elementor-mcp-list-pages   # confirms auth + lists existing pages\nmcp__elementor__elementor-mcp-get-global-settings   # see existing colors/fonts kit\nmcp__elementor__elementor-mcp-get-container-schema  # ground truth on flex_* key names\n```\n\n### 🎯 Detect Pro vs Free FIRST — it changes which path you take\n\nBefore building anything, determine whether **Elementor Pro** is active. The\nwhole skill branches on this: with Pro you use native widgets (Form, Theme\nBuilder, Loop Grid, Popups, Dynamic Tags, Sticky/Motion); without it you use the\nfree-tier workarounds documented further down (Fluent Forms, UAE/HFE, HTML for\nmotion).\n\n**How to detect — by tool availability (the definitive Pro signal):**\n\nThe elementor-mcp server exposes Pro tools **conditionally**. When Pro is active\nthe tool list grows from ~74 to ~100+ tools and Pro-only tools appear. Check\nwhether these exist in your available `mcp__elementor__elementor-mcp-*` tools:\n\n- `add-form` — present ⇒ **Pro active**\n- `create-theme-template` — present ⇒ **Pro active**\n- `add-loop-grid` / `add-loop-carousel` — present ⇒ **Pro active**\n- `create-popup`, `set-dynamic-tag` — present ⇒ **Pro active**\n\nIf none of those Pro tools are exposed, treat the site as **Free** and use the\nworkarounds. The **tool-presence check above is the definitive Pro-vs-Free\nsignal** — prefer it. (`detect-elementor-version` is reliable on current builds —\nthe old v1.5.0 schema bug is fixed — but it reports **atomic/version** support,\nnot a Pro flag, so it doesn't settle Pro-vs-Free on its own.)\n\n> **Record the verdict once** (\"Pro detected\" / \"Free only\") and state it to the\n> user up front, then follow the matching branch in every section below. Each\n> \"Forms\", \"Header/Footer\", and motion section is written as **If Pro → … /\n> If Free → …**. Don't mix paths.\n\n### 🧬 Also detect the ENGINE — classic vs atomic (Elementor 4 / V4)\n\nThere's a **second** axis that changes everything: the page engine. Elementor 4\nintroduced the **atomic / V4** engine (`e-flexbox`, `e-div-block`, atomic\nwidgets, a typed-prop `$$type` data model). Classic widget writes **do not\npersist on an atomic page** — they silently appear to do nothing. So before\nbuilding, decide classic vs atomic.\n\n**How to detect — by atomic tool availability (preferred):**\n\nThe atomic tools register only when the site is on the atomic engine. Check your\navailable tools:\n\n- `add-flexbox`, `add-div-block`, `add-atomic-heading` / `add-atomic-button` / …,\n  `add-atomic-widget` / `update-atomic-widget` present ⇒ **atomic (V4) engine**\n- Only classic `add-container` / `add-heading` / … present ⇒ **classic engine**\n\nYou may also call `detect-elementor-version` (reliable on current releases — it\nreturns whether atomic is supported). The old v1.5.0 schema bug is fixed; still,\ntool-presence is the most direct signal.\n\n> **Which elementor-mcp PLUGIN version am I on?** Some behaviour below branches on\n> it, and `detect-elementor-version` answers about *Elementor*, not the plugin.\n> Call **`server-info`**: it returns `plugin_version` (plus registered-vs-exposed\n> tool counts and what is withholding the rest). It is always registered and\n> cannot be disabled, so **its absence is itself the answer** — no `server-info`\n> means the site is on **1.28.0 or older**. Use it for the one branch that needs it —\n> whether `update-atomic-widget` can restyle in place (1.28.1+). Nothing else should\n> depend on the version: for the universal atomic tools, pass raw `$$type` values, which\n> are correct on every build.\n\n> ⚠️ **Antigravity / tight tool caps.** Antigravity caps MCP tools at ~100. The\n> full Pro+atomic set is ~113, so atomic tools can get truncated and never reach\n> the client — making a V4 site look like \"writes don't persist\". Fix: enable the\n> MCP plugin's **Low-tools mode** (WP Admin → MCP Tools screen). Its curated\n> essentials set **includes the 5 atomic essentials** (`detect-elementor-version`,\n> `add-atomic-widget`, `update-atomic-widget`, `add-flexbox`, `add-div-block`) and\n> stays under the cap.\n\n> 🐞 **Known root cause (older MCP builds).** Elementor often runs atomic as an\n> opt-in *experiment* while `ELEMENTOR_VERSION` still reads `3.x`. MCP builds that\n> gate atomic on `version_compare(ELEMENTOR_VERSION,'4.0.0','>=')` therefore never\n> register the atomic tools on those sites. Fixed upstream by detecting via the\n> experiment/module. If atomic tools are missing on a clearly-V4 site, update the\n> elementor-mcp plugin (or confirm the V4 experiment is on under Elementor →\n> Settings → Features).\n\nRecord **both** axes: e.g. \"Pro + atomic\", \"Pro + classic\", \"Free + classic\".\nThe Pro/Free axis picks Form vs Fluent Forms etc.; the engine axis picks classic\nvs atomic widget tools (next section).\n\nThe 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:\n\n- `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)\n- `content_width: \"boxed\"|\"full\"` + `boxed_width: {unit, size, sizes}`\n- `min_height: {unit, size, sizes}` — use unit `vh` for full-screen heroes\n- `padding`/`margin: {unit, top, right, bottom, left, isLinked}` — `isLinked: false` when sides differ\n- `background_background: \"classic\"|\"gradient\"|\"video\"` — must be set first or other background_* keys are ignored\n- `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)\n\n### Widget call convention — flat params, NOT nested in `settings`\n\nThis bit me hard the first time. The `add-*` shortcut tools take their settings as **top-level parameters**, not inside a `settings: {}` object:\n\n```js\n// ✓ CORRECT\nmcp__elementor__elementor-mcp-add-heading({\n  post_id: 11,\n  parent_id: \"abc123\",\n  title: \"where estates <em>are entrusted</em>\",\n  header_size: \"h1\",\n  title_color: \"#FFFFFF\",\n  typography_typography: \"custom\",       // ← required to enable typography\n  typography_font_family: \"Cormorant Garamond\",\n  typography_font_size: {size: 110, unit: \"px\"},\n  typography_font_weight: \"300\",\n  typography_line_height: {size: 0.98, unit: \"em\"},\n})\n\n// ✗ WRONG — silently fails or returns \"title is required\"\nmcp__elementor__elementor-mcp-add-heading({\n  post_id: 11,\n  parent_id: \"abc123\",\n  settings: {title: \"...\", typography_font_family: \"...\"}\n})\n```\n\n`add-container` is the **exception** — it takes a `settings: {}` object. Don't generalize from one to the other.\n\n### Always set `typography_typography: \"custom\"`\n\nWithout 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.\n\n### Italic emphasis pattern\n\nDisplay headings often need a single italic-emphasized word. Don't use a separate widget — just inline `<em>` in the title:\n\n```js\ntitle: \"A <em>quiet</em> practice for an <em>uncommon</em> clientele.\"\n```\n\nCormorant Garamond and most luxury serifs have italic variants that auto-load when `<em>` appears. Confirm via the rendered page; if italics fail, the global typography needs the italic variant explicitly enabled.\n\n### Responsive values — suffix keys (classic) vs. variants (atomic)\n\nElementor stores a responsive control's per-breakpoint values under **suffixed keys**.\nThe base (desktop) value has **no suffix**; each breakpoint appends its own suffix to the\n**same base control key**, with the **same value shape** as desktop:\n\n```js\n// classic widget — tablet/mobile overrides of the same control:\nmcp__elementor__elementor-mcp-add-heading({\n  post_id, parent_id,\n  title: \"...\",\n  typography_font_size: {size: 110, unit: \"px\"},          // desktop (base, no suffix)\n  typography_font_size_tablet: {size: 72, unit: \"px\"},    // tablet\n  typography_font_size_mobile: {size: 44, unit: \"px\"},    // mobile\n  align: \"left\", align_tablet: \"center\",                  // alignment per breakpoint\n})\n```\n\n**The suffix set is breakpoint-dependent — do NOT hardcode an incomplete list.** It\nderives from Elementor's **active breakpoints** (`add_responsive_control()`), so beyond\n`_tablet` / `_mobile` a site may expose `_widescreen`, `_laptop`, `_tablet_extra`,\n`_mobile_extra`, or custom ones. Read the site's breakpoints rather than assuming; the\nfork passes any `<base>_<breakpoint>` key through as long as `<base>` is a real control.\n\n> **Atomic (V4) is different — no suffix keys.** On a V4 page responsive lives in a style\n> definition's **`variants` array**, keyed by a `breakpoint` meta (`desktop` = base, then\n> `tablet`/`mobile`/custom) — Global Classes take a `variants` param, local styles add\n> variant entries. Never put `_tablet`/`_mobile` suffix keys on atomic elements. See\n> `references/atomic-v4.md` and `references/design-system-crud.md`.\n\n## The widget-vs-HTML decision — DEFAULT TO NATIVE WIDGETS\n\n> 🚨 **CRITICAL ANTI-PATTERN — read this first.**\n>\n> **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.\n>\n> If you find yourself thinking *\"I'll just dump this section as HTML, it's faster,\"* **STOP.** Break it into native widgets.\n\n### Always default to native widgets\n\nFor every section the user wants, build it from native Elementor widgets:\n\n- **Headings** → `add-heading` widget *(supports inline `<em>` for italic emphasis)*\n- **Body copy** → `add-text-editor` widget\n- **Images** → `add-image` widget *(NOT an `<img>` tag inside an HTML widget)*\n- **Buttons / CTAs** → `add-button` widget *(NOT an `<a>` styled as a button)*\n- **Layout / spacing** → `add-container` with proper `flex_*` settings *(NOT `<div>`s with CSS flex)*\n- **Lists** → `add-icon-list` widget\n- **Tabs** → `add-tabs` widget\n- **Accordions / FAQs** → `add-accordion` widget\n- **Forms** → Fluent Forms shortcode via `add-shortcode` widget\n- **Nav menu in headers** → UAE Nav Menu widget *(`uael-nav-menu`)*\n\n### When HTML widget IS allowed *(narrow list — exceptions only)*\n\nOnly reach for an HTML widget in these specific cases. **Anything not on this list goes through native widgets.**\n\n1. **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.)*\n2. **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.**\n3. **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.\"\n4. **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.\n\n### What about card grids of 4+ items?\n\nEarlier 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.\n\n**The correct path for card grids:**\n\n- Build the first card with native widgets *(Container → Image → Heading → Text Editor → Button)*\n- Use `duplicate-element` to copy it 3+ more times\n- Use `update-element` to change the copy/image on each duplicate\n- Wrap them in a parent Container with `flex_direction: row` and `flex_wrap: wrap`\n\nThis 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.\n\n> **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.\n\n### Cross-widget styling — `<style>`-only HTML widgets\n\nWhen you need to style a native widget from outside (e.g., overriding the Tabs widget tab strip styles that the widget controls don't expose), use a **`<style>`-only HTML widget**: it contains ONLY a `<style>` block — no markup, no rendered content. Scope every selector to the parent Elementor element ID:\n\n```html\n<style>\n.elementor-element-f8d1545 .elementor-tab-title {\n  text-transform: uppercase !important;\n  letter-spacing: .26em !important;\n}\n.elementor-element-f8d1545 .elementor-tab-title.elementor-active {\n  border-bottom-color: #171615 !important;\n}\n</style>\n```\n\nThe `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.\n\n> ⚠️ **An HTML widget used for cross-widget styling MUST contain only `<style>`.** 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.\n\n## Building on Elementor 4 (atomic / V4)\n\nElementor 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`.**\n\n> **Atomic local styles wiring.** Atomic styling attaches through **two coupled pieces** —\n> `settings.classes` (a typed list of class ids the element wears) **and** a separate\n> top-level `styles` map holding each class's definition. Every id in `settings.classes`\n> must resolve to a local `styles` entry or a Global Class `g-` id, or it styles nothing.\n> The dedicated helpers build the local `styles` map for you **at creation**. To restyle an\n> element that already exists (check the plugin version with `server-info` — absent means\n> ≤1.28.0): on plugin **1.28.1+** pass flat style params to\n> `update-atomic-widget` and it merges them into the base variant; on **older builds** it\n> merged `settings` only and could not write the `styles` map, so restyle there by\n> (re)creating with a style-capable helper / universal `add-atomic-widget`, or point\n> `settings.classes` at a Global Class. Full pattern → `references/atomic-v4.md`.\n\n### Converting a classic (V3) design to atomic (V4)\n\nThere's no in-place migrator — you **rebuild** the design on a fresh V4 page with atomic\ntools (classic/atomic never mix). Tool map, `$$type` rules, styling parity, and a worked\nexample → **load `references/v3-to-v4-conversion.md`.**\n\n## Elementor 4 design system — Global Classes, Variables, Interactions (v1.14+)\n\nOn an **atomic (V4)** site the fork exposes CRUD for the shared design system, so you can\nauthor reusable styling instead of re-styling every element:\n\n- **Global Classes** (reusable style bundles): `create-global-class`, `update-global-class`,\n  `delete-global-class`, `apply-global-class` (+ read `list-global-classes`).\n- **Variables** (color/font/size design tokens): `list-variables`, `get-variable`,\n  `create-variable`, `edit-variable`, `delete-variable`, `restore-variable`.\n- **Interactions** (per-element scroll/hover/click animations): `list-interactions`,\n  `add-interaction`, `edit-interaction`, `delete-interaction`.\n\n`restore-variable` (undo a soft-deleted token) and `edit-interaction` (id-addressable\nin-place animation edit) are **fork-superset** capabilities the editor path doesn't offer.\nAll writes need `manage_options`; these tools register only when the atomic engine +\nmatching experiments are on. **Full tool shapes, params, Pro gating, caps, and when to use\neach → load `references/design-system-crud.md`.**\n\n## When a write fails — errors & recovery\n\nThe fork's errors are built for self-correction; read them, don't just relay them:\n\n- **Wrong widget name** → `invalid_widget_type` / `widget_not_found` carry `Did you mean:`\n  suggestions **inline in the message** — pick the nearest and retry (no second lookup).\n- **Bad atomic settings** → `save_rejected` embeds the atomic type's **prop schema** inline\n  — correct the settings and retry in one round trip.\n- **Numeric/slider values** → `get-widget-schema` now returns `minimum`/`maximum`/`multipleOf`\n  and slider `unit` enums — clamp to them before writing.\n- **Governance (opt-in, only with the SiteAgent worker):** `governance_grant_required` /\n  `governance_grant_invalid` mean the write needs a gateway-minted approval grant (you can't\n  self-fix — the user must approve); `governance_render_failed` means the write broke the page\n  and **was reverted** (don't blindly re-send — fix the cause); `governance_rollback_failed`\n  means the revert itself failed and the page may be **partially written** — **stop and\n  escalate** with the snapshot id.\n\n**Full recovery playbook (all error codes, retry semantics, range hints) → load\n`references/error-recovery.md`.**\n\n## Brand kit — intake & tokens\n\nTriggers when the user is setting up a new client / brand, or says \"set up the\nbrand\". Establishes the design tokens every later build references **by name, never\nraw hex/font**. Full schema + tool shapes: [`references/brand-kit.md`](references/brand-kit.md).\n\nThe 8 color tokens (`brand, accent, heading, text, bg, surface, muted, border`) and\n2 font tokens (`heading-font, body-font`) map to **named Elementor custom globals**.\n\nFlow:\n\n1. **Gather** the brand — 8 colors (hex), 2 font families, logo — from the user's\n   brief, a Figma file, or by asking. If fewer than 8 colors are given, derive the\n   rest (`surface` = tint of `bg`; `muted` = lower-contrast `text`; `border` = light\n   grey) and state the derivation.\n2. **Apply** — `update-global-colors` with the 8 `{_id, title, color}` entries, then\n   `update-global-typography` with the 2 font entries. (Exact payloads in the\n   reference.)\n3. **Record** the token→value map back to the user so recipes and later edits reuse it.\n4. **Verify** — `get-global-settings` shows the 8 colors + 2 fonts by name.\n\nAfter intake, **bind widget colors to these globals** (or use the recorded token\nvalue when setting directly). Introducing an ad-hoc hex/font mid-build breaks brand\nconsistency — don't.\n\n## Recipe library\n\nReusable, brand-token-driven build sequences for common sections. **Before building a\nsection, consult the matching recipe** and bind everything to the brand tokens (see\n\"Brand kit\" above). Classic-first; apply the recipe's Pro/V4 variant note when those\nengines are active. Full trees + token bindings: [`references/recipes.md`](references/recipes.md).\n\nAvailable recipes: **Hero**, **Services grid**, **Split (image + text)**, **Stats\nband**, **Testimonials**, **CTA band**, **Contact**, **FAQ**, **Pricing**, **Logos\nstrip**. (The library grows — add a recipe when a new section type recurs.)\n\nRecipes reuse the rest of this skill's rules (native widgets not HTML dumps,\n`duplicate-element`/Loop Grid for grids, flat-param convention) — they don't restate\nthem.\n\n## When the user asks to BUILD — building order\n\n**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/`).\n\n**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.\n\n> 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.\n\nFor a new page, build top-down section by section, in small commits, verifying after each:\n\n1. **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`.\n2. `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\n3. (Via WP-CLI) Set as static front page: `wp option update show_on_front page; wp option update page_on_front <id>`\n4. Build sections — **use the matching recipe from the Recipe library** (outer container → inner boxed container, max-width ~1360px → content), bound to brand tokens\n5. After each section: `get-page-structure(post_id)` to verify nesting, or just curl the front page\n6. **Pause for human review** before building header/footer (which use Header Footer Elementor templates, a different flow)\n\n## When the user asks to EDIT\n\nApproach existing pages surgically — don't rebuild what you don't have to:\n\n1. `list-pages` to find the page they're editing\n2. `get-page-structure(post_id)` to see the current widget tree and grab element IDs\n3. 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\n4. Verify the edit by re-reading `get-page-structure` or curling the rendered page\n5. **Never delete a section unless they explicitly ask** — even when restructuring. Use `move-element` or `update-element` first.\n\n## When the user asks to REFERENCE / INSPECT\n\nRead-only tools, no writes. Useful for \"show me\", \"tell me\", \"what's\", \"list\" requests:\n\n- `list-pages` — what pages exist\n- `get-global-settings` — colors, typography, layout settings\n- `get-page-structure(post_id)` — what's on a page\n- `get-element-settings(element_id)` — exact settings of one widget\n- `find-element(post_id, ...)` — locate a widget by content/type\n\nFormat the response as a clear summary, not a JSON dump. The user wants understanding, not raw data.\n\n## When the user asks to EXPLORE / \"what can you do?\"\n\nGive a short menu *(don't dump the whole tool list)*. Point them at the four modes from the First Action Protocol with concrete examples:\n\n- *\"Build a homepage from this HTML mockup\"* → mode 1\n- *\"Make the hero text 20% smaller\"* → mode 2\n- *\"Show me what colors are currently set globally\"* → mode 3\n- *\"What pages exist on the site?\"* → mode 3\n\nThen ask which mode they want.\n\n## Header/Footer notes\n\nTheme 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`.**\n## Pro-only widgets & features\n\nWhen 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`.**\n## Dynamic data stacks — ACF & Crocoblock/JetEngine (Tier-0)\n\nBind 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`.**\n## Forms\n\nIf 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`.**\n## Setup gotchas (what bit me last time)\n\n- **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.\n- **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`.\n- **Neither MCP plugin is on wordpress.org.** Cannot install via REST API by slug — must download zips from GitHub Releases.\n- **The elementor-mcp release zipball has an ugly auto-generated folder name** (`Digitizers-elementor-mcp-<sha>/`). WordPress uses the folder name as the plugin slug. Repack with a clean `elementor-mcp/` folder before installing.\n- **Claude Code only loads `.mcp.json` at startup** — after writing one, the user must quit and reopen.\n- **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.\n- **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.\n\n## Live-host vs Local differences\n\n**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.\n\n**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://<live-domain>/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`.\n\n## Tool-loading discipline\n\nThe MCP exposes ~75 deferred tools. Don't load them all at once — fetch schemas lazily as you build:\n\n- **First call:** `list-pages` (no schema needed — pre-loaded by ToolSearch when triggered)\n- **Before building containers:** load `get-container-schema`, `add-container`, `update-container`\n- **Before placing widgets:** load `add-heading`, `add-text-editor`, `add-button`, `add-image`, `add-html` in one batch\n- **Before specific widgets:** load `add-tabs`, `add-icon-list`, `add-divider`, `add-spacer` as needed\n\nUse `ToolSearch` query format `select:tool1,tool2,tool3` to load multiple in one call.\n\n## What the MCP **cannot** do (set expectations)\n\n- 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)\n- Set the static front page (use `wp option update`)\n- Build a custom header/footer on Elementor Free without the HFE plugin *(with Pro, use native Theme Builder via `create-theme-template`)*\n- Auto-translate arbitrary HTML/CSS into Elementor widgets — you read the source design and emit widget calls\n- Pixel-perfect parity with hand-coded HTML — Elementor's flexbox container model is the ceiling *(Pro adds CSS Grid containers, raising it)*\n\n**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.\n\n## Optional companion tooling — `wordpress-api-pro` (content/SEO/commerce ops)\n\n> **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.\n\nThe 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.\n\nThis skill is one stage of the studio toolbox (audit → build → content → host → ads). **Full handoffs + a \"where am I\" router → load `references/lifecycle.md`.**\n\n**When the user explicitly asks for one of these, `wordpress-api-pro` is the right tool instead of the MCP:**\n\n| Task | Script |\n|---|---|\n| Upload an actual image/file to the media library (then feed its URL/ID to an Elementor Image widget) | `upload_media.py` |\n| Read/write SEO meta (Rank Math / Yoast) | `seo_meta.py` |\n| Read/write ACF or JetEngine custom fields | `acf_fields.py` / `jetengine_fields.py` |\n| List/create/update WooCommerce products | `woo_products.py` |\n| Bulk content changes across many posts or **multiple sites** (dry-run first) | `batch_update.py`, `wp.sh` |\n| Plain post/page CRUD outside Elementor | `create_post.py` / `update_post.py` / `get_post.py` / `list_posts.py` |\n\n**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.\n\n> 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`.\n\n## Quick reference — the build flow that works *(mode 1 only)*\n\n> 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.\n\n```\n1. setup-elementor-mcp.sh          # one-time, ~3 minutes\n2. Quit + reopen Claude Code       # picks up .mcp.json\n3. list-pages                      # confirm auth\n4. get-global-settings             # see current kit\n5. update-global-colors + typography\n6. create-page (Elementor Canvas template)\n7. Set as front page via WP-CLI\n8. Build sections top-down, one at a time\n9. After each: get-page-structure or curl the front page\n10. Pause for human review before header/footer\n```\n\nWhen working from a designed HTML mockup, map the source design to Elementor like this:\n\n- **Brand colors** → `update-global-colors`\n- **Brand fonts** → `update-global-typography`\n- **Section copy** → `add-heading` + `add-text-editor` widgets\n- **Card grids (4+ identical items)** → build one card with native widgets, then `duplicate-element` and `update-element` per copy\n- **Tabs/accordions** → native `add-tabs`/`add-accordion` widgets *(HTML allowed inside `tab_content` strings only — see anti-pattern section)*\n- **Forms** → real Fluent Forms shortcode via `add-shortcode` widget *(see Fluent Forms section)*\n- **Headers/footers** → `elementor-hf` post type with UAE Nav Menu widget for nav\n\n> 🚨 **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.\n\nFile v1.7.1:_meta.json\n\n{\n  \"ownerId\": \"kn7afv05r120atbc75whrv1zkx825tt5\",\n  \"slug\": \"siteagent-elementor-studio\",\n  \"version\": \"1.7.1\",\n  \"publishedAt\": 1789427139989\n}\n\nFile v1.7.1:references/atomic-v4.md\n\n# Building on Elementor 4 (atomic / V4) — reference\n\n## Building on Elementor 4 (atomic / V4)\n\nApply this section **only when you detected the atomic engine** (atomic tools\npresent). On a classic-engine site, ignore it and use the classic widget tools\neverywhere. **Never mix:** classic `add-heading`/`add-container` writes do not\npersist on an atomic page, and atomic writes don't belong on a classic page.\n\n### Atomic tool family — use instead of the classic ones\n\n| Need | Classic (don't use on V4) | **Atomic (V4)** |\n|---|---|---|\n| Flex container | `add-container` | `add-flexbox` *(direction/justify/align/gap/wrap/padding/background_color)* |\n| Block container | `add-container` | `add-div-block` |\n| Heading | `add-heading` | `add-atomic-heading` |\n| Body text | `add-text-editor` | `add-atomic-paragraph` |\n| Button | `add-button` | `add-atomic-button` |\n| Image | `add-image` | `add-atomic-image` |\n| SVG / video / divider | `add-icon` / `add-html` | `add-atomic-svg` / `add-atomic-youtube` / `add-atomic-video` / `add-atomic-divider` |\n| Anything else | `add-widget` | `add-atomic-widget` *(any atomic type; pass raw `$$type` settings — correct on every version, see note)* / `update-atomic-widget` |\n\n### The atomic data model (what's different)\n\n- **Typed props (`$$type`).** Atomic settings are typed values, not flat strings.\n  For the **dedicated** helper tools (`add-atomic-heading`, `add-atomic-paragraph`,\n  `add-atomic-button`, `add-flexbox`, …) the MCP wraps them for you — **pass simple\n  flat values** (e.g. `title: \"Hello\"`, a hex `color`, a `{size,unit}` dimension) and\n  it stores them in the `$$type` format Elementor's atomic engine expects.\n  **For the universal `add-atomic-widget` / `update-atomic-widget`, always pass raw\n  `$$type` values** (fetch the shape with `get-widget-schema`). That is correct on\n  every plugin version: since 1.27.0 the save path coerces flat values through\n  `Atomic_Props::coerce_tree()`, and already-typed props pass through it untouched —\n  whereas on older builds those two tools wrote settings verbatim and a flat value\n  was silently saved as empty. Typed values work either way, and the version is not\n  always knowable mid-session, so don't make the write depend on it.\n\n  **A direct `_elementor_data` patch has no wrapper on any version.** It bypasses the\n  plugin entirely, so every prop must be raw `$$type` there too.\n- **Styles live in a separate `styles` map**, not inline on the element. Layout\n  props on `add-flexbox` (direction/justify/align/gap) are written as local styles\n  automatically — you don't hand-build the styles map.\n\n  **Since plugin 1.28.1, `update-atomic-widget` takes flat style params too**\n  (`padding`, `width`, `border_*`, `css_position`, `shadow_*`, typography, …) and\n  merges them into the element's base style variant, preserving props it wasn't\n  asked to change. Before that it wrote only `settings`, so a padding sent there\n  saved, reported success and rendered nothing — which is why older notes say\n  delete-and-recreate is the only way to restyle an atomic element. It isn't any\n  more. `settings` still means CONTENT.\n- **Confirm keys per widget** with `get-widget-schema` before building anything\n  non-trivial; the atomic prop names differ from the classic control names.\n\n### How local styles actually attach — `settings.classes` + the `styles` map\n\nThis is the wiring the creation helpers (`add-atomic-*` / `add-flexbox` / the universal\n`add-atomic-widget`) do for you — and the shape you hand-build only when patching\n`_elementor_data` directly. (On plugin **1.28.1+** `update-atomic-widget` also writes the\n`styles` map: pass flat style params and it merges them into the element's base variant.\nOn older builds it could only change which classes `settings.classes` referenced, which is\nwhy the notes below describe recreation or a Global Class as the way to restyle.) An\natomic element carries **two coupled pieces**:\n\n1. **`settings.classes`** — a typed prop listing the class IDs the element wears:\n   ```json\n   \"classes\": { \"$$type\": \"classes\", \"value\": [\"e-<elementId>-<hash>\", \"g-1a2b3c4\"] }\n   ```\n   It is a **reference list only** — an id here with no matching style definition renders\n   nothing.\n2. **`styles`** — a **top-level map on the element** (sibling to `settings`/`elements`),\n   keyed by the same class id, holding the actual style definition:\n   ```json\n   \"styles\": {\n     \"e-<elementId>-<hash>\": {\n       \"id\": \"e-<elementId>-<hash>\", \"label\": \"local\", \"type\": \"class\",\n       \"variants\": [ { \"meta\": {\"breakpoint\":\"desktop\",\"state\":null}, \"props\": { /* $$type props */ }, \"custom_css\": null } ]\n     }\n   }\n   ```\n\n**The rule:** every id in `settings.classes.value` must resolve — either to a **local**\nstyle def in this element's `styles` map, or to a **Global Class** `g-` id in the Class\nManager (`apply-global-class` / `create-global-class`). A local id present in `styles`\nbut missing from `settings.classes` won't apply; an id in `settings.classes` with no\n`styles` entry and no matching global class is a dangling reference that styles nothing.\nThe local `styles` map is **built at element-creation time** by the `add-atomic-*` /\n`add-flexbox` helpers (and the universal **`add-atomic-widget`** — *not* the classic\n`add-widget`, whose writes don't persist on a V4 page): they auto-compile a local class\nfrom the style props you pass (typography, color, background, …) into the element's\n`styles` map and wire its id into `settings.classes` for you. On plugin **1.28.1+** it can\nalso be written **after** creation, via `update-atomic-widget` — see the note below.\n\n> **Restyling an existing element depends on the plugin version** — read it from\n> `server-info` (`plugin_version`); if that tool isn't exposed at all, the site is on\n> 1.28.0 or older, since it cannot be disabled on builds that have it.\n>\n> On **1.28.1+**, `update-atomic-widget` takes flat style params (`padding`, `width`,\n> `border_*`, `css_position`, `shadow_*`, typography, …) and merges them into the element's\n> base style variant, preserving props it wasn't asked to change and wiring the class into\n> `settings.classes`. `settings` still means CONTENT.\n>\n> ⚠️ On **older builds** it wrote `settings` only and had no way to touch the `styles` map,\n> so a padding sent through `settings` saved, reported success and rendered nothing. There,\n> restyle by (a) setting the style at creation via the `add-atomic-*` helpers, or (b)\n> pointing `settings.classes` at an existing **Global Class** (`apply-global-class` /\n> `create-global-class`).\n>\n> Either way: writing a class id into `settings.classes` with **no** matching global class\n> and **no** matching local `styles` entry is a dangling reference that styles nothing.\n\n### Responsive on V4 — variants, not `_tablet`/`_mobile` suffixes\n\nClassic widgets take responsive values as **suffixed keys** (`align_tablet`,\n`columns_mobile` — see `../SKILL.md`). Atomic elements do **not**: each style def holds a\n**`variants` array**, and a variant's `meta.breakpoint` (`desktop` = base, then `tablet`,\n`mobile`, plus any active custom breakpoints) + `meta.state` (`null`/`hover`/`focus`/…)\nselect when its `props` apply. Author responsive/state styling by adding variants:\n\n- Via **Global Classes**: `create-global-class` / `update-global-class` take a `variants`\n  array of `{ breakpoint, state, styles }` — the base (desktop) is the plain `styles` map,\n  each extra variant a breakpoint/state override. See `design-system-crud.md`.\n- Via **inline local styles**: add another entry to the style def's `variants` array with\n  the target `meta.breakpoint`.\n\nThe breakpoint set is **not fixed** — it derives from Elementor's active breakpoints, so a\nsite with custom breakpoints exposes more than `tablet`/`mobile`. Don't hardcode a list;\nmirror the breakpoints the site actually defines.\n\n### Build order on V4\n\nSame top-down discipline as classic, with atomic tools:\n\n1. `update-global-colors` + `update-global-typography` (global kit still applies).\n2. `create-page` → build the outer layout with `add-flexbox` (section) → inner\n   `add-flexbox`/`add-div-block` (boxed content) → atomic widgets inside.\n3. Card grids: build one card from atomic widgets, then `duplicate-element` +\n   `update-atomic-widget` per copy (same pattern, atomic tools).\n4. Verify after each section with `get-page-structure` + curl the front page.\n\n### Pro widgets on V4 — the real limitation\n\nElementor has **not** shipped atomic equivalents for the Pro widgets yet (Form,\nLoop Grid, Nav Menu, Theme Builder parts). On an atomic page they're classic\nislands that **may not render**. So when the design needs them:\n\n- **Contact form:** prefer a **Fluent Forms shortcode** dropped via an atomic\n  widget (`add-atomic-widget` of a shortcode/HTML type), not the Pro Form widget.\n  Flag to the user that the native Pro Form isn't V4-ready.\n- **Dynamic listings:** if Loop Grid won't render, fall back to atomic cards built\n  from a query you fetch out-of-band (or `wordpress-api-pro`), or accept a classic\n  island only if it renders on this build.\n- **Header/footer:** Theme Builder still works at the template level; build the\n  template body with atomic tools where supported.\n\n> If the user needs heavy Pro-widget functionality **and** doesn't specifically\n> need V4, the lowest-friction path is a classic-engine site (turn off the V4\n> page experiment under Elementor → Settings → Features). Surface this tradeoff\n> rather than silently shipping a page where the form doesn't render.\n\nFile v1.7.1:references/brand-kit.md\n\n# Brand kit — token vocabulary & intake\n\nThe studio's brand tokens. Every client site sets these once; every build\nreferences them **by name, never raw hex/font**. Tokens map to **named Elementor\ncustom globals** (the `update-global-colors` / `update-global-typography` tools\nwrite `custom_colors` / `custom_typography`, merged by `_id`).\n\n## Color tokens (8 named custom globals)\n\n| token `_id` | title | role |\n|---|---|---|\n| `brand` | Brand | primary brand color — buttons, links, emphasis |\n| `accent` | Accent | secondary highlight |\n| `heading` | Heading | heading text color |\n| `text` | Text | body text color |\n| `bg` | Background | page background |\n| `surface` | Surface | card / section panel background |\n| `muted` | Muted | secondary / subtle text, captions |\n| `border` | Border | hairlines, dividers, card borders |\n\nIf a client supplies fewer than 8, derive and state it: `surface` = a light tint of\n`bg`; `muted` = `text` at ~60% contrast; `border` = `text` at ~12% / a light grey.\n\n## Typography tokens (2 named custom globals)\n\n| token `_id` | title | role |\n|---|---|---|\n| `heading-font` | Heading Font | headings |\n| `body-font` | Body Font | body / UI |\n\n## Type scale (applied per-widget by recipes — not a global object)\n\n| step | size (px, desktop) | typical use |\n|---|---|---|\n| h1 | 48 | hero title |\n| h2 | 36 | section title |\n| h3 | 28 | card title |\n| h4 | 22 | sub-heading |\n| body-lg | 18 | lead paragraph |\n| body | 16 | default text |\n| small | 14 | captions, labels |\n\nDefaults: heading weight 700, heading line-height 1.15; body weight 400, body\nline-height 1.6. Scale down ~15–20% on mobile.\n\n## Logo\n\nRecord the logo media id / URL in the intake record. Header recipes use the Site\nLogo widget (Pro/UAE) or a Heading fallback.\n\n## Intake template (fill one per client)\n\n```json\n{\n  \"client\": \"Acme\",\n  \"colors\": {\n    \"brand\": \"#1A56DB\",\n    \"accent\": \"#F59E0B\",\n    \"heading\": \"#0F172A\",\n    \"text\": \"#334155\",\n    \"bg\": \"#FFFFFF\",\n    \"surface\": \"#F8FAFC\",\n    \"muted\": \"#64748B\",\n    \"border\": \"#E2E8F0\"\n  },\n  \"fonts\": { \"heading-font\": \"Rubik\", \"body-font\": \"Inter\" },\n  \"logo\": \"https://acme.example/logo.svg\"\n}\n```\n\n## Applying it (MCP tool shapes)\n\n`update-global-colors` — one entry per color token:\n\n```json\n{ \"colors\": [\n  { \"_id\": \"brand\",   \"title\": \"Brand\",      \"color\": \"#1A56DB\" },\n  { \"_id\": \"accent\",  \"title\": \"Accent\",     \"color\": \"#F59E0B\" },\n  { \"_id\": \"heading\", \"title\": \"Heading\",    \"color\": \"#0F172A\" },\n  { \"_id\": \"text\",    \"title\": \"Text\",       \"color\": \"#334155\" },\n  { \"_id\": \"bg\",      \"title\": \"Background\",  \"color\": \"#FFFFFF\" },\n  { \"_id\": \"surface\", \"title\": \"Surface\",    \"color\": \"#F8FAFC\" },\n  { \"_id\": \"muted\",   \"title\": \"Muted\",      \"color\": \"#64748B\" },\n  { \"_id\": \"border\",  \"title\": \"Border\",     \"color\": \"#E2E8F0\" }\n] }\n```\n\n`update-global-typography` — one entry per font token:\n\n```json\n{ \"typography\": [\n  { \"_id\": \"heading-font\", \"title\": \"Heading Font\",\n    \"typography_font_family\": \"Rubik\",\n    \"typography_font_weight\": \"700\",\n    \"typography_line_height\": { \"size\": 1.15, \"unit\": \"em\" } },\n  { \"_id\": \"body-font\", \"title\": \"Body Font\",\n    \"typography_font_family\": \"Inter\",\n    \"typography_font_weight\": \"400\",\n    \"typography_line_height\": { \"size\": 1.6, \"unit\": \"em\" } }\n] }\n```\n\nThen `get-global-settings` to confirm the 8 colors + 2 fonts are present by name.\n\n## The discipline\n\nAfter intake, bind widget colors to these globals (or use the recorded token value\nwhen a recipe sets a value directly). Never introduce an ad-hoc hex/font mid-build —\nthat breaks brand consistency and the recipe library.\n\nFile v1.7.1:references/design-system-crud.md\n\n# Elementor 4 design system CRUD — Global Classes, Variables, Interactions\n\nApply this only on an **atomic (V4) site** (atomic tools present) with the matching\nElementor experiments on. These tools author the *shared design system* — the same\nClass Manager / Variables / Interactions an editor user would build by hand — so an\nagent can create reusable styling instead of re-styling every element inline.\n\nAll three families register **conditionally**. If the tools below aren't in your\n`mcp__elementor__elementor-mcp-*` list, the site's engine/experiments don't support\nthem — fall back to inline atomic local styles (see `atomic-v4.md`).\n\n> **Permissions.** Every *write* here needs `manage_options` (mutating the shared\n> design system / kit is site-wide, not per-post). Variable/interaction *reads*\n> (`list-variables`, `get-variable`, `list-interactions`) need `edit_posts`;\n> interaction tools additionally require `edit_post` on the target page. If a call\n> returns a `forbidden` error, the connected app-password user lacks the cap.\n\n---\n\n## 1. Global Classes (reusable style bundles) — Elementor 4 Class Manager\n\nA Global Class is a named, reusable set of styles (a `g-<7hex>` id) you author once and\napply to many atomic elements — the design-system equivalent of a CSS utility class.\nCompanion read tool: `list-global-classes` (resolves opaque `g-` ids → names + CSS).\n\n| Tool | Does | Key params |\n|---|---|---|\n| `create-global-class` | Author a new class | `label` (e.g. `\"card-base\"`), `styles` (CSS-prop→value map), optional `variants` |\n| `update-global-class` | Edit in place, **keeps the `g-` id** so bindings survive | `class_id`, any of `label` / `styles` / `variants` |\n| `delete-global-class` | Remove by id | `class_id` |\n| `apply-global-class` | Bind an existing class to one element | `class_id`, `post_id`, `element_id` |\n\n- **Ergonomic styles.** `styles` is a plain map like `{\"color\":\"#111\",\"padding\":24,\"font-size\":\"1.25rem\"}`.\n  The tool wraps values into Elementor's atomic `$$type` props automatically. Colors\n  (`color`, `border-color`, …), sizes (`padding`, `margin`, `width`, `font-size`, …),\n  and unitless numbers (`z-index`, `flex-grow`, …) are typed correctly; anything else\n  is stored as a string prop.\n- **`styles` replaces only the base/desktop variant** on `update-global-class` — other\n  variants are kept. `variants` replaces matching breakpoint/state variants (see\n  Responsive below). `label` renames without touching styles.\n- **`apply-global-class` is idempotent** — re-applying an already-present class is a\n  no-op. It appends the `g-` id to the element's `settings.classes`. A **non-atomic**\n  element (no `classes` control) is rejected with error code `not_atomic`, and the\n  error embeds the element's compact settings schema so you can see what it *is*.\n- **Delete does not cascade.** Elementor ignores dangling `g-` references left on\n  elements — those elements simply lose that styling, the page isn't rewritten.\n- **Cap: 100 classes.** `create-global-class` refuses past the limit\n  (`class_limit_reached`) — delete an unused class first.\n- **Invalid props are rejected up front.** When a prop name/type isn't valid for the\n  atomic style schema, the write returns `invalid_styles` with the rejected props +\n  the allowed schema inline — fix and retry in one round trip (see `error-recovery.md`).\n- **Not everything maps.** `background-color` is **not** a valid atomic key (atomic\n  uses the structured `background` prop) and flex `gap` is the structured\n  `layout-direction` prop, so those are deliberately excluded from the ergonomic map —\n  passing them yields an honest schema rejection rather than a silent drop.\n\n**When to use:** the design has a repeated visual treatment (cards, section padding,\nbutton variants). Author it once with `create-global-class`, then `apply-global-class`\nto each element — one later `update-global-class` restyles them all.\n\n---\n\n## 2. Variables (design tokens) — colors, fonts, sizes\n\nVariables are the atomic *tokens* (a color / font / size) that Global Classes and\natomic styles reference by id — Elementor 4's real design-token layer, stored on the\nactive kit. Ids look like `e-gv-<hash>`.\n\n| Tool | Does | Notes |\n|---|---|---|\n| `list-variables` | List active tokens `{id,type,label,value,order}` | excludes soft-deleted |\n| `get-variable` | One token by `variable_id` | `not_found` if absent/hidden |\n| `create-variable` | New token | `label`, `type` (`color`\\|`font`\\|`size`), `value` |\n| `edit-variable` | Change `label` and/or `value` in place, **keeps the id** | type is fixed |\n| `delete-variable` | **Soft-delete** (tombstone, not purged) | reversible |\n| `restore-variable` | Bring a soft-deleted token back | **fork-superset capability** |\n\n- **Value rules (validated).** `color` = strict hex (`#RGB` / `#RRGGBB` / `#RRGGBBAA`;\n  named colors rejected). `size` = `<number><unit>` (px/em/rem/%/vw/vh/vmin/vmax/ch/pt/pc/ex/fr)\n  **or** a CSS-function expression (`clamp()`/`calc()`/`min()`/`max()`/`var()`/`env()`).\n  `font` = a font-family name. Labels: no spaces, ≤50 chars, `[A-Za-z0-9_-]` only (the\n  label becomes a CSS custom-property name).\n- **Size variables are Pro-only.** On a Free site `create-variable type:size` returns\n  `requires_pro` (Elementor filters size tokens out on non-Pro — they'd save but never\n  render), and such tokens are hidden from list/get. Colors and fonts work on Free.\n- **`restore-variable` is a fork superset.** Delete is a reversible tombstone, and\n  `restore-variable` returns the token to the active set — the upstream/editor path\n  offers no such undo. Restoring an already-active token is a harmless no-op.\n- **Uniqueness + cap enforced.** Duplicate labels → `label_not_unique`; the token cap\n  → `limit_reached`. Both map to clear errors.\n\n**When to use:** establish brand tokens once (`create-variable` for each brand color /\nfont), then reference them from Global Classes and atomic styles. This is the V4-native\ncounterpart to the classic global-kit flow (`update-global-colors` /\n`update-global-typography`) — on a V4 site prefer Variables for anything atomic elements\nbind to.\n\n---\n\n## 3. Interactions (per-element animations) — scroll / hover / click\n\nAn Interaction attaches a scroll/hover/click animation to **one atomic element on one\npage** (a trigger + an animation preset). Stored on the element's top-level\n`interactions` field, not in `settings`.\n\n| Tool | Does | Notes |\n|---|---|---|\n| `list-interactions` | List an element's interactions (ergonomic shape) | `post_id`, `element_id` |\n| `add-interaction` | Add one animation | ergonomic fields below |\n| `edit-interaction` | **Id-addressable in-place edit** | **fork-superset capability** |\n| `delete-interaction` | Remove by `interaction_id` | `not_found` if absent |\n\nErgonomic fields (defaults shown):\n\n- `trigger` — **Free:** `load`, `scrollIn`. **Pro:** `scrollOut`, `hover`, `click`. Default `load`.\n- `effect` — `fade` \\| `slide` \\| `scale`. Default `fade`.\n- `type` — `in` \\| `out`. Default `in`.\n- `direction` — `''` \\| `left` \\| `right` \\| `top` \\| `bottom` \\| `top-left` \\| `top-right` \\| `bottom-left` \\| `bottom-right`. Default `''`.\n- `duration_ms` (default `600`), `delay_ms` (default `0`).\n- `easing` — **Free:** `easeIn`. **Pro:** `easeOut`, `easeInOut`, `backIn`, `backInOut`, `backOut`, `linear`. Default `easeIn`.\n\n- **Pro gating is enforced.** A Pro-only trigger/easing on a Free site returns\n  `requires_pro` — use a Free value or activate Pro. Unknown values return `invalid_*`.\n- **`edit-interaction` is the fork differentiator.** It finds the item by its\n  `interaction_id` and patches only the fields you pass, preserving the id and every\n  untouched field (including Pro-only nodes the ergonomic shape doesn't model). Use it\n  to tweak an existing animation instead of delete-then-add.\n- **Ids: temp → canonical.** `add-interaction` writes a `temp-<hex>` id, saves through\n  the document (which canonicalizes it to `{post_id}-{element_id}-{hash}`), then\n  re-reads to return the canonical id. Address later edits/deletes by that returned id.\n- **Cap: 5 per element** (`interaction_limit_reached`). Interactions attach only to\n  atomic elements — a non-atomic target returns `not_atomic` with its schema.\n\n**When to use:** the design calls for entrance/scroll motion on a specific element. On\nFree, `fade`/`slide`/`scale` `in`/`out` with `load`/`scrollIn` cover most reveals; reach\nfor Pro triggers/easing only when Pro is detected.\n\n---\n\n## Where this fits vs. the classic kit\n\n- **Classic (V3) site** → these tools aren't registered. Use the classic global-kit\n  flow (`update-global-colors` / `update-global-typography`) and inline widget styles.\n- **Atomic (V4) site** → Variables + Global Classes are the design-system layer;\n  Interactions add motion. Inline atomic local styles (the `styles` map + `settings.classes`,\n  see `atomic-v4.md`) are still fine for one-off styling — reach for Global Classes when\n  a treatment repeats.\n\nFile v1.7.1:references/dynamic-data.md\n\n# Dynamic data stacks (ACF & Crocoblock/JetEngine) — reference\n\n## Dynamic data stacks — ACF & Crocoblock/JetEngine (Tier-0)\n\n> ⚠️ **Names below are from public docs, UNVERIFIED on a live site.** Treat every\n> widget-type string / dynamic-tag id here as a *hint*, not gospel. Before relying\n> on one, **discover it at runtime** (list the available widget types / inspect an\n> existing element with `get-page-structure`) and **read back the first write** to\n> confirm it persisted. This is the same verify-or-bail discipline as the V4\n> section — these stacks were detected, not yet exercised end-to-end here.\n\nThe setup script / `new-client.sh` report **ACF** and **JetEngine** when active.\nBranch on those the way you branch on Pro.\n\n### ACF (Advanced Custom Fields)\n\nACF is a *data* layer, not widgets. You surface its fields through Elementor's\n**Dynamic Tags**, which means **Elementor Pro is required** (free Elementor has no\ndynamic tags). If ACF is active but Pro is not, say so and fall back to static\ncontent (or a free dynamic-tag plugin the user installs).\n\n- Bind an ACF field to a widget setting with `set-dynamic-tag` (same tool as the\n  Dynamic Tags section), pointing at the ACF source tag + the field name/key.\n- The field must be **exposed** — ACF field group saved, and for some flows\n  `show_in_rest` enabled — or the tag resolves empty. If a bind reads back empty,\n  check the field group before retrying.\n- Hint set (confirm at runtime): Elementor Pro registers per-type ACF tags such as\n  `acf-text`, `acf-url`, `acf-image`, `acf-number`, `acf-color`, `acf-file`,\n  `acf-gallery`, `acf-date-time`, `acf-post-object`. Repeater/flexible-content\n  fields are **not** bindable via dynamic tags — use a JetEngine repeater/listing\n  or HTML instead.\n\n### Crocoblock / JetEngine\n\nJetEngine registers its own Elementor widgets. There is **no dedicated MCP tool**\nfor them — place them with the **generic `add-widget`** using the Jet widget's\ntype string, then set its controls.\n\n- **Discover the exact `widgetType` first.** Either list the available widget types\n  the MCP/site exposes, or drop the widget once in the editor and read it back with\n  `get-page-structure`. Do **not** hardcode from the hint list below without this.\n- Hint set (confirm at runtime): `jet-listing-grid` (dynamic listings),\n  `jet-listing-dynamic-field`, `jet-listing-dynamic-image`, `jet-listing-dynamic-link`,\n  `jet-listing-dynamic-meta`, `jet-listing-dynamic-repeater`,\n  `jet-listing-dynamic-terms`.\n- A Listing Grid needs a **Listing Item template** to point at. Creating that\n  template, plus CPTs / meta boxes / the Query Builder, are **JetEngine admin-side**\n  operations that are **not** drivable through this MCP. If the user needs those\n  built, tell them to create the listing/CPT in JetEngine first, then you wire the\n  Listing Grid to it.\n- After the first `add-widget` of a Jet type, **read it back** — if it didn't\n  persist or renders empty, the type string or a required control is wrong; fix\n  before repeating.\n\n### What's in scope vs not (Tier-0)\n\n- ✅ In scope: detecting these stacks; binding ACF fields via Pro dynamic tags;\n  placing Jet widgets via `add-widget` with runtime-discovered types; read-back.\n- ❌ Not in scope: creating ACF field groups, JetEngine CPTs/meta boxes/listings,\n  or the Query Builder — those are admin-side. Hand them back to the user.\n\nFile v1.7.1:references/engine-and-premium.md\n\n# Engine & the Premium plugin — what runs the build\n\n**Engine = our fork `Digitizers/elementor-mcp` (v1.34.1, the release this kit's installer\npins)** — Elementor 4.x-correct. It bundles the WordPress MCP Adapter, so it installs as a\n**single plugin** — no separate adapter plugin needed.\n\nTool counts scale with the site. The figures here were measured on **v1.24.0 with every\napplicable tool enabled**: **61 / 100 / 105** on a classic (v3) install (free / Pro / Pro +\nWooCommerce), and **74 / 113 / 118** when the Elementor 4.0+ atomic engine is active (the\n+13 atomic tools). They are neither a count of v1.34.1 nor a minimum for it: releases since\nhave added tools, and a site can expose far fewer — the admin's per-tool toggles and\nLow-tools mode both trim the registered list. The live `tools/list` is the count; when it\nlooks short, check those two before concluding a tool does not exist:\n\n- **Per-tool toggles** live in the `elementor_mcp_disabled_tools` option, and nothing in\n  the MCP handshake, the tool list or the logs says that abilities were suppressed — a\n  fresh install has presented as **zero tools exposed** with 104 slugs in that list.\n  Diagnose: `wp option get elementor_mcp_disabled_tools --format=json`. **Clearing it alone\n  does not stick**: a seeder in the plugin's admin re-disables every Pro-badged tool\n  whenever `elementor_mcp_defaults_applied` is below its `DEFAULTS_VERSION` — deliberately,\n  so new Pro batches ship off by default — and after a plugin upgrade the counter is behind\n  again, so a list emptied while the seeder is armed is silently refilled on the next\n  wp-admin request. The order is: (1) load any wp-admin page once — that request runs the\n  seeder and bumps the counter; (2) confirm `wp option get elementor_mcp_defaults_applied`\n  now matches `DEFAULTS_VERSION`; (3) back the list up to the project, then\n  `wp option update elementor_mcp_disabled_tools '[]' --format=json` (or curate it — see the\n  next point); (4) restart Claude Code, since the tool list is read at startup.\n- **Low-tools mode** (EMCP Tools → Tools screen) filters the list down to a curated\n  ~50-slug essentials set for clients with a tool cap. On such a client do **not** clear\n  the whole disabled list: the full Pro + atomic set (~113 tools) overruns a ~100 cap, the\n  client silently truncates, and the atomic essentials can be what falls off — \"no tools\"\n  turns into the subtler \"writes don't persist\".\n\nThe v1.13–v1.34 fork work adds the design-system CRUD + governance surface on top (see\nbelow).\n\n## What the fork adds over the upstream base (the reason we run it)\n\nThe fork started from upstream's 1.x line and has diverged substantially:\n\n- **Elementor 4.x GA atomic correctness** — `is_v4()` schema gating, corrected `$$type` prop\n  shapes, style-controls compiled into local style classes, atomic detection by\n  element-type registration. Upstream's classic-only schema breaks on 4.1.x.\n- **GPL tool set enabled** (v1.13.0) — the brand-kit / SEO / a11y / Widget-Builder tools\n  register for everyone (no license gate).\n- **v4 design-system CRUD** (v1.14–v1.16) — Global Classes, Variables (with\n  `restore-variable`), Interactions (with `edit-interaction`).\n- **SiteAgent-governed writes** — page writes are snapshot-first + optional Ed25519\n  approval grants + optional post-write render-check auto-revert (v1.17–v1.19); **design-token\n  writes (system kit, global palette, Variables) are snapshot-governed too** (v1.24.0).\n- **Schema-in-error** (v1.20–v1.21) + **numeric range constraints** in `get-widget-schema`\n  (v1.23) — one-round-trip self-correction.\n\n## No Freemius / no phone-home\n\nThe vendored Freemius SDK and the upstream hosted \"Pro marketplace\" (Templates / Skills\nfetchers that pulled licensed content from `emcp.msrbuilds.com`) were **removed in v1.22.0**.\nThe fork has **no license gate and no phone-home**. It is distributed via GitHub\nreleases: this skill's installer installs the release it pins, and **since fork v1.28.0\nthe plugin carries its own update checker** (`includes/class-updater.php`, loaded from\nthe main plugin file), so later releases appear on the site's normal *Plugins* /\n*Dashboard → Updates* screens. Be precise about what that is and is not:\n\n- It **offers** updates; it does not install them. Installing one is WordPress's\n  ordinary plugin-update flow — an admin clicks *Update*, or has turned on WordPress's\n  per-plugin auto-update toggle for it. The fork does not turn that toggle on.\n- It offers a **published GitHub Release** only (release-only detection: a pushed tag\n  with no Release offers nothing), served from the Release's `elementor-mcp*.zip` asset.\n- The check contacts `api.github.com`, and the download `github.com`, with a user agent\n  that names only the plugin and its version — never the site URL WordPress's default\n  agent would send. What GitHub does receive is what any update check hands the host it\n  asks: the request's source IP, and here the plugin's name and version. What it does not\n  receive is the site URL, and there is no vendor endpoint and no telemetry of any kind —\n  that is the sense in which \"no phone-home\" holds.\n\nThose later updates run outside this kit's pin-and-digest check: the wizard verifies\nwhat it installs today, and WordPress's update flow governs what replaces it. An\noperator who wants every version reviewed leaves the auto-update toggle off (the\ndefault) and reviews the Release before clicking *Update*. The **free** bundled\nsample-prompts + brand-kit apply/backup/restore are retained.\n\n## Do NOT run the paid \"MCP Tools for Elementor (Premium)\" (`emcp-pro`) at the same time\n\nThe fork and upstream Premium share the same code lineage (same class names\n`Elementor_MCP_*`, same `ELEMENTOR_MCP_VERSION` constant, no PHP namespace). Activating both\n= `Cannot redeclare class` fatal. **Only one can be active.**\n\n| | Upstream Premium `emcp-pro` (3.0.0) | fork `elementor-mcp` (1.34.1) |\n|---|---|---|\n| Elementor 4.x GA atomic engine | ❌ classic-only schema (breaks on 4.1.x) | ✅ 4.x-correct |\n| v4 design-system CRUD (classes / variables / interactions) | ❌ | ✅ |\n| Governed writes (snapshot + grant + render-check) | ❌ | ✅ (page **and** design-token) |\n| Schema-in-error + numeric-range hints | ❌ | ✅ |\n| Freemius license / hosted marketplace / phone-home | ✅ | ❌ (removed v1.22) |\n| Direction | horizontal (WP content/plugin/theme CRUD, PHP-snippet authoring) | Elementor-4 depth + governance |\n\n**We run the fork.** It is the Elementor-4-correct, design-system-capable, governed engine\nthis skill is built around. There is no reason to switch to Premium for Elementor page\nbuilding; Premium went horizontal (site-wide CRUD) rather than deepening Elementor 4.\n\n## Switching (one active at a time)\n\n```bash\nwp plugin deactivate elementor-mcp && wp plugin activate emcp-pro    # → Premium\nwp plugin deactivate emcp-pro && wp plugin activate elementor-mcp    # → fork\n```\n\nBoth share the options `elementor_mcp_disabled_tools` and `elementor_mcp_low_tool_mode` —\na low-tools/disabled-tools state set under one carries to the other.\n\n## Production hygiene\n\nNeither plugin should stay active on a client's **production** server — both are build-time\nauthoring tools. Deactivate (or remove) at handoff. (The fork's governance — grants +\nrender-check — is opt-in and needs SiteAgent; it makes *authoring* writes reversible, not a\nreason to leave the tool live in production.)\n\nFile v1.7.1:references/error-recovery.md\n\n# When a write fails — errors, governance & self-correction\n\nThe fork surfaces structured errors designed for an agent to **recover from without\nguessing**. Read the error, don't just report it. This file covers three families:\nschema-in-error self-correction, governance (approval + rollback), and the numeric range\nhints that keep values valid in the first place.\n\n---\n\n## 1. Schema-in-error — correct and retry in ONE round trip\n\nThe fork embeds the fix inside the error message, so you rarely need a second discovery\ncall. The MCP adapter drops `WP_Error` *data*, so the fork puts the recoverable detail in\nthe **message** — read it there.\n\n### Wrong widget name → `invalid_widget_type` / `widget_not_found`\n\n`add-widget` with an unknown type, and `get-widget-schema` for an unknown type, return an\nerror whose message carries the **nearest valid widget names inline**:\n\n> `Widget type \"headng\" not found. Did you mean: heading, …?`\n\n**Recovery:** parse the `Did you mean:` list, pick the intended name, retry with it. Don't\nmake a separate `list-widgets` call — the suggestions are already there (ranked exact →\nsubstring → smallest edit distance). REST callers also get `data.suggestions` +\n`data.schema_hint`.\n\n### Bad atomic settings → `save_rejected`\n\nWhen an Elementor 4 **atomic** widget rejects settings (`add-atomic-widget`,\n`update-atomic-widget`, and the `add-atomic-*` helpers), the error carries the target\natomic type's **compact prop schema inline** — each prop as `{ type, enum? }` distilled\nfrom Elementor's own `get_props_schema()`. e.g. an `e-heading` rejection tells you\n`tag: {type:string, enum:[h1…h6]}`, `title: {type:html}`, `link: {type:link}`.\n\n**Recovery:** read the inline prop schema, correct the offending settings to the right\n`$$type`/enum, and re-send once. No `get-widget-schema` round trip needed.\n\n### Non-atomic target → `not_atomic`\n\n`apply-global-class`, `add-interaction`, and the other atomic-only writes reject a\nnon-atomic element with `not_atomic` and embed the element's **compact settings schema**\n(`type`, `setting_keys`, `has_classes`). That tells you the element isn't atomic, so a\nGlobal Class / Interaction can't bind to it — pick an atomic element instead, or convert\nthe section (see `v3-to-v4-conversion.md`).\n\n### Invalid Global Class props → `invalid_styles`\n\n`create-global-class` / `update-global-class` reject an unknown/mistyped style prop with\n`invalid_styles`, embedding `rejected_props`, `type_mismatches`, and `allowed_props`.\n\n**Recovery:** drop/rename the rejected props (or fix the type) and retry. Remember\n`background-color` and flex `gap` are intentionally *not* accepted here — use `background`\nand the structured layout, or set them via the dedicated atomic helpers.\n\n---\n\n## 2. Governance — approval grants & auto-rollback (opt-in)\n\n**Only present when the [SiteAgent worker](https://github.com/Digitizers/SiteAgent)\n(`digitizer-site-worker`) is installed** alongside the plugin. On a plain install none of\nthis fires and writes behave normally. All of it is **opt-in** — a bare SiteAgent install\ndoes not gate Elementor writes until an operator turns it on.\n\nGovernance wraps page-data writes with **capture-before-write** snapshots plus two\noptional gates. The error codes you may hit:\n\n| Error code | What happened | What to tell the user / do |\n|---|---|---|\n| `governance_grant_required` | Grant enforcement is on, but the write carried no `X-Aura-Approval-Grant`. **The tool never ran.** | The write needs approval. The **gateway must mint a grant** bound to this tool + params; you can't self-fix it. Ask the user to approve, then the request is retried *with* the grant. |\n| `governance_grant_invalid` | A grant was presented but rejected (bad signature, wrong tool/params/site binding, expired, or reused nonce). **The tool never ran.** | Same — a *fresh valid* grant is needed. Don't retry the same grant. |\n| `governance_render_failed` | The write succeeded at the data layer but the page came back **broken** (HTTP 5xx or white screen), so it was **reverted to the pre-write snapshot**. | The page is safe (unchanged). **Do not blindly re-send the identical write** — it broke the page. Investigate the settings that caused it (often a bad value), fix, then try again. |\n| `governance_rollback_failed` | A write failed (or render-reverted) **and the rollback itself failed** — the page may be **partially written**. | **Stop. Do not retry.** Surface this to the user with the snapshot id from the message; it must be restored manually. This is the one governance error you never auto-recover from. |\n| `governance_snapshot_failed` | Governance couldn't snapshot before writing, so it **refused the write** (fail-closed — no blind mutation without a rollback point). | The page is unchanged. Report the reason; the write can be retried once the snapshot path works. |\n\nKey facts that shape your response:\n\n- **Grants are opt-in and gateway-minted.** Enforcement is OFF by default even when a\n  SiteAgent gateway key exists. When on, a grant binds to the **exposed MCP tool name**\n  (`/` → `-`, e.g. `elementor-mcp-update-element`) + exact params. An agent cannot forge\n  one — approval is a human/gateway step.\n- **Dry-run previews are exempt.** A preview-capable tool (one whose schema has an `apply`\n  flag — the SEO/a11y generators) invoked with `apply` falsy writes nothing and needs no\n  grant. Reach for a preview first when you just want to *show* a proposed change.\n- **The render check is edits-only and fail-safe.** It reverts only when a\n  *confirmed-healthy* page turns broken after the write. Transient/inconclusive probes\n  never revert a good write, and create-style writes aren't render-checked.\n- **Retry semantics summary:** `grant_*` → needs approval, not a code fix; `render_failed`\n  → fix the content before retrying (the write was undone); `rollback_failed` → do not\n  retry, escalate to the user with the snapshot id.\n\n---\n\n## 3. Numeric range hints — send valid values the first time\n\n`get-widget-schema` now carries a control's own numeric bounds into the JSON Schema, so\nyou can pick a valid value without a second lookup:\n\n- **`number` controls** emit `minimum` / `maximum` / `multipleOf` (from the control's\n  `min` / `max` / `step` — unit-free, so unambiguous). A zero/omitted step is not emitted.\n- **`slider` controls** expose a `unit` **enum** (the units the control offers) and, when\n  the control offers exactly **one** unit, a `size` `minimum` / `maximum` from that unit's\n  range. A multi-unit slider (bounds differ per unit, e.g. `px` 0–1000 vs `%` 0–100)\n  leaves `size` unconstrained on purpose — don't assume a bound it didn't give you.\n\n**Use them:** before setting a numeric/slider control on anything non-trivial, read the\nschema and clamp your value to `[minimum, maximum]`, honor `multipleOf`, and pick a `unit`\nfrom the enum. This avoids the value being silently clamped/dropped by Elementor.\n\n> The fork's schema is also **richer than the bare `get_controls()` path** — it enables\n> style/group controls *outside the editor* (`Performance::set_use_style_controls`), so\n> typography/color/shadow controls appear in the schema even over the WP-CLI/stdio bridge.\n> Trust `get-widget-schema` as the ground truth for a widget's real controls.\n\nFile v1.7.1:references/forms.md\n\n# Forms — Elementor MCP (reference)\n\n## Forms\n\nBranch on the Pro detection from the top of this skill.\n\n### If Pro → native Form widget (preferred)\n\nWith Pro active the `add-form` MCP tool is exposed — build a real, submitting form\nas a native widget with no third-party plugin. The whole form (fields, labels,\nsubmit action, email notification) lives in Elementor and is editable in the\nvisual editor.\n\n**Build pattern** — `add-form` into the contact-section container, then define\nfields and the submit/email actions. Document and pass the form's settings the\nsame disciplined way the Fluent Forms class map below is documented:\n\n```js\n// Native Pro Form widget — placed in the contact section container\nmcp__elementor__elementor-mcp-add-form({\n  post_id: <page_id>,\n  parent_id: <contact_section_container_id>,\n  form_name: \"Contact\",\n  // fields as the form-widget schema defines them (id/type/label/required/width);\n  // load get-widget-schema for \"form\" first to confirm exact field-array keys\n  form_fields: [\n    { custom_id: \"name\",    field_type: \"text\",     field_label: \"Name\",    required: \"true\", width: \"50\" },\n    { custom_id: \"email\",   field_type: \"email\",    field_label: \"Email\",   required: \"true\", width: \"50\" },\n    { custom_id: \"message\", field_type: \"textarea\", field_label: \"Message\", required: \"true\", width: \"100\" }\n  ],\n  button_text: \"Send\",\n  // Submit actions: \"email\" is the default; set the To address in the email action group.\n  submit_actions: [\"email\"],\n  email_to: \"<site admin email>\"\n})\n```\n\n- **Confirm field/action key names against the live schema** before building:\n  `get-widget-schema({ widget_type: \"form\" })`. The form widget's field array and\n  action keys are the part most likely to drift between Pro versions — treat the\n  schema as ground truth, exactly as with the container schema.\n- **Styling** native form fields uses the widget's own style controls (typography,\n  spacing, borders, button) passed as flat params — no scoped CSS hack needed. Only\n  drop to a `<style>`-only HTML widget for things the controls don't expose, scoped\n  to the form's `element_id` (same rule as everywhere else).\n- This replaces the entire Fluent Forms split below — **don't** install Fluent\n  Forms when Pro is present.\n\n### If Free → Fluent Forms (fallback)\n\nElementor's native Form widget is Pro, and `add-form` is **not exposed** without it. The kit's wizard auto-installs **Fluent Forms** as the free workaround. The flow is split: the user builds the form, then Claude wires it into the page and styles it.\n\n#### The split — what Claude does vs. what the user does\n\n**The user does (manual, ~2-3 min in WP Admin):**\n\n1. **Fluent Forms → New Form** → pick the *Contact Form* template *(pre-built with Name / Email / Subject / Message)* OR start from blank\n2. *(optional)* Drag in extra fields — Phone, dropdown, etc.\n3. **Save Form** — note the form ID at the top of the page (usually `1` for the first form)\n4. **Settings → Email Notifications** → confirm the To address (default: `{admin_email}`)\n\n**Claude does:**\n\n1. Replace any placeholder form (HTML widget) in the contact section with `add-shortcode` widget containing `[fluentform id=\"<ID>\"]`\n2. Add a small `<style>` block (in an HTML widget alongside, NOT replacing the shortcode widget) that scopes Fluent Forms styling to match the site's design\n\n#### Wiring the form\n\n```js\n// Drop the shortcode widget where the form should appear\nmcp__elementor__elementor-mcp-add-shortcode({\n  post_id: <page_id>,\n  parent_id: <contact_section_container_id>,\n  shortcode: '[fluentform id=\"1\"]'\n})\n```\n\n#### Styling — verified Fluent Forms class structure (Fluent 6.x)\n\n```\n.fluentform                            ← outer wrapper\n.fluentform_wrapper_<formId>           ← per-form wrapper (e.g. .fluentform_wrapper_1)\n  .ff-default                          ← default skin marker\n    form.frm-fluent-form\n      .ff-el-group                     ← each field block\n        .ff-el-input--label            ← label\n          label                        ← actual <label> tag\n        .ff-el-input--content\n          input.ff-el-form-control     ← text inputs\n          textarea.ff-el-form-control  ← textareas\n      .ff-t-container                  ← two-column row (e.g. first/last name)\n        .ff-t-cell                       ← each cell\n      .ff_submit_btn_wrapper\n        button.ff-btn.ff-btn-submit    ← submit button\n      .ff-el-is-required               ← required field marker\n```\n\n#### CSS variables (the easiest override path)\n\nFluent Forms exposes these custom properties on `:root`. **Redefine them on the per-form wrapper to restyle the whole form without specificity battles:**\n\n```css\n.fluentform_wrapper_1 {\n  --fluentform-primary: #5C1A1B;          /* submit button bg + focus accent */\n  --fluentform-secondary: #171615;        /* body text in inputs */\n  --fluentform-border-color: #C9C2B3;     /* input borders */\n  --fluentform-border-radius: 0px;        /* hairline-square inputs */\n}\n```\n\nThat alone gets you ~80% of the way to a custom design.\n\n#### Full styling pattern (when CSS vars aren't enough)\n\nFor the remaining 20% (typography overrides, hairline-only borders, custom button feel), use scoped selectors with the per-form wrapper class. Specificity (0,2,0) matches Fluent's defaults; load order wins because your styles come after.\n\n```css\n/* Scope EVERYTHING to .fluentform_wrapper_<id> so you don't bleed into other pages. */\n\n.fluentform_wrapper_1 .ff-el-form-control {\n  font-family: 'Inter Tight', sans-serif;\n  font-size: 14px;\n  border: none;\n  border-bottom: 1px solid var(--fluentform-border-color);\n  border-radius: 0;\n  padding: 14px 0;\n  background: transparent;\n  color: #171615;\n}\n\n.fluentform_wrapper_1 .ff-el-form-control:focus {\n  border-bottom-color: #171615;\n  box-shadow: none;\n}\n\n.fluentform_wrapper_1 textarea.ff-el-form-control {\n  font-family: 'Cormorant Garamond', serif;\n  font-size: 17px;\n  min-height: 100px;\n}\n\n.fluentform_wrapper_1 .ff-el-input--label label {\n  font-family: 'Inter Tight', sans-serif;\n  font-size: 11px;\n  letter-spacing: 0.2em;\n  text-transform: uppercase;\n  color: #8A857E;\n}\n\n.fluentform_wrapper_1 .ff-btn-submit {\n  background: #5C1A1B;\n  color: #fff;\n  border: 1px solid #5C1A1B;\n  border-radius: 0;\n  padding: 16px 26px;\n  font-family: 'Inter Tight', sans-serif;\n  font-size: 11px;\n  font-weight: 500;\n  letter-spacing: 0.28em;\n  text-transform: uppercase;\n}\n\n.fluentform_wrapper_1 .ff-btn-submit:hover {\n  background: #3F1011;\n  border-color: #3F1011;\n}\n\n/* Two-column rows — turn into a CSS grid with consistent gap */\n.fluentform_wrapper_1 .ff-t-container {\n  display: grid;\n  grid-template-columns: 1fr 1fr;\n  gap: 22px;\n}\n@media (max-width: 640px) {\n  .fluentform_wrapper_1 .ff-t-container {\n    grid-template-columns: 1fr;\n  }\n}\n\n/* Asterisk marker on required fields */\n.fluentform_wrapper_1 .ff-el-is-required label::after {\n  color: #5C1A1B;\n}\n```\n\n#### Where to inject the styles\n\nTwo options:\n\n1. **Drop an HTML widget right above (or below) the Shortcode widget** with the `<style>` block inside. Wrap selectors in `.fluentform_wrapper_<id>` to keep them scoped. *(Recommended — keeps styles co-located with the form.)*\n2. **Add to Customizer → Additional CSS** *(Appearance → Customize)* — site-wide, persists across page rebuilds. *(Better for production sites where the form appears on multiple pages.)*\n\n#### Common gotchas\n\n- **Find the form ID by looking at the form's URL in WP Admin** — `/wp-admin/admin.php?page=fluent_forms&route=editor&form_id=1` → ID is `1`. Or query the DB: `SELECT id, title FROM wp_fluentform_forms`.\n- **Do NOT remove the `.ff-default` class** by overriding `class` attributes — Fluent's submit button styling cascades from it.\n- **Fluent's CSS loads after page render via `enqueue_scripts`.** If your overrides aren't applying, check that your `<style>` block lives in a widget that renders inside the page body (not the head).\n- **Asterisks for required fields** are pseudo-elements (`::after`) — color them via `.ff-el-is-required label::after { color: ... }`, not `color: ...` on the label itself.\n\n### Other form options (when Fluent Forms isn't available)\n\n1. **Contact Form 7** — same shortcode pattern: `[contact-form-7 id=\"...\"]`. Less polished default look, but free and works.\n2. **Styled HTML `<form>` with a JS-alert handler** — only as a flagged visual placeholder for early builds. **Tell the user explicitly: \"form is visual only — submissions don't go anywhere yet. Wire to Fluent Forms before going live.\"**\n\nFile v1.7.1:references/header-footer.md\n\n# Header / Footer — reference\n\n## Header/Footer notes\n\nBranch on the Pro detection from the top of this skill.\n\n### If Pro → native Theme Builder (preferred)\n\nWith Elementor Pro active, build headers/footers/single/archive templates with the\nnative **Theme Builder** via the `create-theme-template` MCP tool — no UAE/HFE\nplugin needed.\n\n1. **Create the template.** `create-theme-template` with the template type:\n   - `header`, `footer`, `single` (single post/page), `archive` (post listings).\n   The tool returns a `post_id` you build into like any page.\n2. **Build the layout** into that `post_id` with native widgets — a row Container\n   with logo (Site Logo widget) + native **Nav Menu** widget (`add-nav-menu`,\n   Pro) pointed at a WP menu by name + a Button CTA.\n3. **Set display conditions** so the template applies site-wide (or to a subset).\n   Theme Builder display conditions are Pro-native; configure \"Entire Site\" for a\n   global header/footer.\n4. **Verify** by curling the front page — the header/footer should render on every\n   matching page.\n\n> The WordPress **menu itself** still must exist first (WP Admin → Appearance →\n> Menus) — the MCP cannot create WP nav menus directly, on either tier. Point the\n> Nav Menu widget at it by name.\n\n### If Free → UAE / HFE workaround (fallback)\n\n`create-theme-template` is **not exposed** without Pro. With Elementor Free, headers and footers are built using **Ultimate Addons for Elementor (UAE)** by Brainstorm Force (the kit's setup wizard auto-installs this; alternatively the lighter **Header Footer Elementor (HFE)** plugin from the same company also works — both share the same `elementor-hf` post type).\n\n### Building a site-wide header\n\n1. **Create the WordPress menu first.** Tell the user to go to WP Admin → Appearance → Menus, name it (e.g. \"Main\"), add the pages they want, and save. The MCP cannot create WP nav menus directly — this step is a one-minute manual action.\n\n2. **Create the header template post.** Use `create-page` with `post_type: \"elementor-hf\"` and a title like \"Site Header\". Then set the following post meta via WP-CLI or the `update-element` flow:\n   - `ehf_template_type` = `\"type_header\"` (or `\"type_footer\"` for footers)\n   - `display-on-canvas` = `\"yes\"` (displays site-wide; alternative meta keys like `ehf_target_include_locations` may apply for narrower scopes)\n\n3. **Build the layout.** A row container with three children:\n   - **Left:** logo (Heading widget with brand name in display serif, OR `Site Logo` widget if UAE is installed)\n   - **Center:** **UAE Nav Menu widget** (`uael-nav-menu`) pointed at the WordPress menu by name. UAE's nav menu widget is **free** and handles mobile hamburger, dropdowns, hover states, active-page highlighting automatically — much cleaner than rendering nav as raw HTML.\n   - **Right:** Button widget with \"Contact\" or \"Get In Touch\" CTA\n\n4. **Verify display.** After building, instruct the user to check WP Admin → Appearance → Header Footer Builder → confirm the Display On rule is set to \"Entire Website.\"\n\n### When UAE Nav Menu isn't available\n\nIf only HFE (the lighter plugin) is installed without UAE: use the Shortcode widget calling `[wp_nav_menu menu=\"Main\" container=\"\"]` — WordPress's built-in shortcode renders the menu as a real `<ul>` with all the right classes for active-page highlighting and responsive styling.\n\n**Do not** fall back to manually listing the menu items inside an HTML widget — that hard-codes the navigation in two places (the WP menu AND the Elementor template) which means future menu edits won't reflect in the header. Always render the menu through `[wp_nav_menu]` or the UAE widget.\n\n### Footer pattern\n\nIdentical post type (`elementor-hf`) but `ehf_template_type = \"type_footer\"`. Layout is typically a 4-column container (brand block + 3 link columns) on a dark background, with a bottom row containing copyright + social icons.\n\nFile v1.7.1:references/lifecycle.md\n\n# Studio lifecycle — where the Elementor build fits\n\nThe Elementor build is one stage of the studio toolbox. Hand off cleanly to the neighbours.\n\n| Stage | Tool | Use it for |\n|---|---|---|\n| **Audit** (pre-sale / onboarding) | `wordpress-api-pro` → `site_audit.py` | No-auth Tier-1 scan of a prospect/client site (CMS, SEO, headers, SSL, PageSpeed) before proposing a build. |\n| **Content / commerce** | `wordpress-api-pro` (WP REST) | Seed posts/pages/CPTs, media, WooCommerce products, SEO meta, ACF/JetEngine fields — before or alongside the Elementor build. |\n| **Build** (this skill) | the fork `elementor-mcp` | Design + build pages/templates in Elementor. |\n| **Host** | `cloudways-mcp` / `hostinger-mcp` | Provision/monitor/maintain the server the site runs on; SSL/cache/backups (Cloudways UI/API for SSL — not an MCP tool). |\n| **Ads** | `meta-ads-mcp` | Launch/manage the campaign that drives traffic to the built site. |\n\n## Handoffs\n\n- **Audit → Build:** run `site_audit.py` first; its findings (CMS, theme, current builder, SEO gaps) scope the build brief and tell you whether Elementor/Pro is even present.\n- **Build → Content:** use `wordpress-api-pro` to populate real content into the structures you built (drafts-first, dry-run for bulk).\n- **Build → Host:** confirm the target server in `cloudways-mcp`/`hostinger-mcp`; clear cache after a deploy; never leave the MCP build plugins active on production.\n\n## \"Where am I?\" router\n\n- Prospect, no site access yet → **Audit** (`site_audit.py`).\n- Site access, needs pages → **Build** (here).\n- Pages built, needs real content/products → **Content** (`wordpress-api-pro`).\n- Site done, needs server ops/SSL/cache → **Host** (`cloudways-mcp`/`hostinger-mcp`).\n- Site live, needs traffic → **Ads** (`meta-ads-mcp`).\n\nFile v1.7.1:references/pro-widgets.md\n\n# Pro-only widgets & features — reference\n\n## Pro-only widgets & features\n\nThese sections apply **only when Pro was detected** at the top of the skill. If\nthe site is Free, these tools are not exposed — use the documented free-tier\npatterns instead (Loop Grid → duplicate-element grid; Popups → no equivalent;\nDynamic Tags → static content; Sticky/Motion → Customizer CSS).\n\n> The widget-vs-HTML anti-pattern still applies in full. Pro widgets give you\n> *more* native building blocks, which is **more** reason never to dump HTML.\n\n### Loop Grid / Loop Carousel — dynamic listings\n\n`add-loop-grid` (and `add-loop-carousel`) render a repeating template across a\nquery of posts/CPTs/products — the native, editable replacement for hand-built\ncard grids when the content is dynamic.\n\n1. **Build the loop item template** — a small Container with the card layout\n   (Image → Heading → Text → Button) wired to **Dynamic Tags** (post title,\n   featured image, excerpt, permalink) so every item pulls its own data.\n2. **Add the Loop Grid** with `add-loop-grid`, point it at that template, and set\n   the query (post type, count, order) + columns/gap via the widget's settings.\n3. Confirm exact setting keys with `get-widget-schema({ widget_type: \"loop-grid\" })`\n   before building.\n\nUse this for blog feeds, portfolios, team grids, product listings. For a fixed\nset of bespoke non-dynamic cards, the `duplicate-element` pattern is still correct.\n\n### Popups\n\n`create-popup` builds a popup template; then configure triggers / conditions /\ntiming (on load, on scroll %, exit intent, after delay; display conditions for\nwhich pages it shows on).\n\n1. `create-popup` → returns a `post_id`; build the popup content into it with\n   native widgets like any page.\n2. Set trigger + display rules via the popup's settings (load `get-widget-schema`\n   / the popup settings schema to confirm keys).\n3. Wire an open action where needed (e.g. a Button's link set to the popup), or\n   let the trigger fire it automatically.\n\n### Dynamic Tags\n\n`set-dynamic-tag` binds live data to a widget setting instead of a static value —\npost title/excerpt/featured image, author, site name/logo, ACF/custom fields,\narchive title, etc.\n\n- Use it to make Theme Builder templates (single/archive) and Loop Grid items\n  data-driven.\n- Bind on the specific setting (e.g. a Heading's `title`, an Image's `image`) via\n  `set-dynamic-tag` pointing at the source tag + its options.\n\n### Sticky header & Motion Effects\n\nThese are the Pro-native answer to the free-tier \"solid header / Customizer CSS\"\nnote.\n\n- **Transparent-on-top → solid-on-scroll header:** set the header Container's\n  **Sticky** to `Top` plus a sticky-state background, instead of hand-writing a\n  scroll listener. Configure via the container's sticky/effects settings.\n- **Motion Effects** (scrolling/mouse parallax, entrance animations, transforms)\n  are exposed as element settings — set them on the target element rather than\n  emitting custom JS/CSS.\n- Confirm the effects setting keys via `get-container-schema` / the element's\n  widget schema before writing them.\n\nArchive v1.7.0: 21 files, 86177 bytes\n\nFiles: references/atomic-v4.md (9593b), references/brand-kit.md (3625b), references/design-system-crud.md (9059b), references/dynamic-data.md (3406b), references/engine-and-premium.md (4042b), references/error-recovery.md (7340b), references/forms.md (8624b), references/header-footer.md (3947b), references/lifecycle.md (1805b), references/pro-widgets.md (3128b), references/recipes.md (11027b), references/v3-to-v4-conversion.md (7250b), references/verticals/car-wash.md (2617b), references/verticals/dental.md (2832b), references/verticals/local-business.md (2647b), references/verticals/portfolio.md (2701b), references/verticals/salon.md (2527b), setup-elementor-mcp.sh (71926b), skill-card.md (3330b), SKILL.md (41237b), _meta.json (145b)\n\nFile v1.7.0:SKILL.md\n\n---\nname: siteagent-elementor-studio\nversion: 1.7.0\nlicense: MIT\ndescription: 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 covers initial install of the MCP Adapter + elementor-mcp plugins, app-password auth wiring, schema-loading discipline, and the widget-vs-HTML decision tree. SKIP for Bricks, Divi, Beaver Builder, or non-Elementor WordPress builds.\npermissions:\n  shell: \"Runs the bundled setup script (files/setup-elementor-mcp.sh) — only on explicit user confirmation. It shells out to curl/unzip/zip/python3 and, for Local sites, drives Local by Flywheel's bundled WP-CLI (plugin install/activate) against the running site's PHP + MySQL socket.\"\n  network:\n    - \"GitHub release download over HTTPS from the trusted Digitizers/elementor-mcp repo (api.github.com + release asset host) — the elementor-mcp plugin zip. By default the release this kit pins (EMCP_DEFAULT_VERSION in the setup script), checked before it is unpacked or installed against the sha256 recorded beside the pin — out of band from the download. EMCP_PIN_VERSION=<tag> or =latest selects another release, checked against the digest that release publishes (integrity, not provenance) or EMCP_EXPECTED_SHA256. Nothing is installed unverified\"\n    - \"The target WordPress site's REST API (/wp-json/ — auth check, plugin list/install, MCP route verification). Plaintext http:// is refused for a non-local host unless that exact host is named in WP_ALLOW_HTTP, because the run sends a reusable application password on every request\"\n  filesystem:\n    - \"Writes .mcp.json in the current working directory, created mode 600 before the credential is written (it embeds a reusable Basic-Auth WordPress credential), and appends .mcp.json to .gitignore there\"\n    - \"Reads Local by Flywheel site paths + bundled WP-CLI/PHP binaries; creates a temp working dir for the plugin zip\"\n  env:\n    - \"WP_URL / WP_USERNAME / WP_APP_PASSWORD (when used to supply the target site + Application Password auth)\"\n    - \"EMCP_PIN_VERSION (optional — a release tag, or latest, instead of the release this kit pins; the kit's pin never downgrades an installed newer plugin — the run stops and names this override)\"\n    - \"EMCP_EXPECTED_SHA256 (optional — verify the plugin zip against a digest obtained out of band; required for a release that publishes no digest)\"\n    - \"WP_ALLOW_HTTP (comma-separated host list — permits plaintext http for exactly those hosts; a blanket value is not a hostname and permits nothing)\"\n---\n\n# SiteAgent Elementor Studio Skill\n\nYou 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.\n\n## 🛑 First Action Protocol — ASK BEFORE DOING\n\n**When this skill is invoked, do not start running tools. Ask the user what they want first.**\n\n> **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.\n\nIf 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.\n\nOtherwise *(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:**\n\n```\nWhat would you like to do with your Elementor site?\n\n  1. Build       — create new pages or sections from a design\n  2. Edit        — change something on an existing page\n  3. Reference   — inspect current state (pages, colors, fonts, content)\n  4. Explore     — show me what's possible / what can the MCP do here\n```\n\nDo **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.\n\n### Read-only \"smoke test\" calls that are always safe to run\n\nWhen the user picks any option, you can run these **before** asking follow-up questions, since they help frame the next response:\n\n- `mcp__elementor__elementor-mcp-list-pages` — confirms auth + lists what's there\n- `mcp__elementor__elementor-mcp-get-global-settings` — current colors/fonts kit\n\nThat's it for unprompted tool calls. **Anything that creates, modifies, or deletes data requires the user to have explicitly asked for it.**\n\n## When this skill applies\n\n- The user mentions Elementor MCP, types `/siteagent-elementor-studio`, or says \"use the Elementor MCP\"\n- A `.mcp.json` in the project registers an MCP server pointing at `wp-json/mcp/elementor-mcp-server`\n- The user asks to build, edit, inspect, or troubleshoot an Elementor page\n- Tools beginning with `mcp__elementor__elementor-mcp-*` are available\n\n## First-session setup (when MCP not yet connected)\n\n> **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`.\n\nIf the user has a WordPress site but no **working** `elementor` MCP connection — either no `.mcp.json` at all, or only this kit's committed placeholder config (values like `\"WP_URL\": \"${WP_URL:-}\"` with those env vars unset) and no `mcp__elementor__elementor-mcp-*` tools loaded:\n\n> In the placeholder case there are two fixes, not one: **export `WP_URL` / `WP_USERNAME` / `WP_APP_PASSWORD`** (the committed config reads them — fastest in this repo's checkout or a cloud session), or run the wizard below from a **separate per-site project directory** (it refuses to write credentials into the tracked placeholder file).\n\n1. **Check whether they're using Local-by-Flywheel or a live host.** Setup paths differ.\n2. **Run the bundled setup script** from the loaded skill's directory — it handles plugin install, auth wiring, and `.mcp.json` generation interactively for both flavors. The skill's base directory is announced when this skill loads (look for the filesystem path in the skill load message). Replace `<skill-dir>` with that announced path:\n   ```bash\n   bash \"<skill-dir>/setup-elementor-mcp.sh\"\n   ```\n   Keep the quotes — plugin-cache paths can contain spaces (e.g. a Windows\n   profile named `First Last`), and an unquoted substitution splits the path.\n   **If `<skill-dir>/setup-elementor-mcp.sh` is not found** (e.g., from a manual `INSTALL.sh` run instead of plugin-marketplace), use the fallback path for manual installations:\n   ```bash\n   bash ~/.claude/scripts/setup-elementor-mcp.sh\n   ```\n3. 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.\n4. On reopen, the deferred MCP tools will be exposed via ToolSearch — load the ones you need with `select:` queries.\n\nIf something fails, see \"Setup gotchas\" below.\n\n## Working session conventions\n\n### Always do this first\n\n```\nmcp__elementor__elementor-mcp-list-pages   # confirms auth + lists existing pages\nmcp__elementor__elementor-mcp-get-global-settings   # see existing colors/fonts kit\nmcp__elementor__elementor-mcp-get-container-schema  # ground truth on flex_* key names\n```\n\n### 🎯 Detect Pro vs Free FIRST — it changes which path you take\n\nBefore building anything, determine whether **Elementor Pro** is active. The\nwhole skill branches on this: with Pro you use native widgets (Form, Theme\nBuilder, Loop Grid, Popups, Dynamic Tags, Sticky/Motion); without it you use the\nfree-tier workarounds documented further down (Fluent Forms, UAE/HFE, HTML for\nmotion).\n\n**How to detect — by tool availability (the definitive Pro signal):**\n\nThe elementor-mcp server exposes Pro tools **conditionally**. When Pro is active\nthe tool list grows from ~74 to ~100+ tools and Pro-only tools appear. Check\nwhether these exist in your available `mcp__elementor__elementor-mcp-*` tools:\n\n- `add-form` — present ⇒ **Pro active**\n- `create-theme-template` — present ⇒ **Pro active**\n- `add-loop-grid` / `add-loop-carousel` — present ⇒ **Pro active**\n- `create-popup`, `set-dynamic-tag` — present ⇒ **Pro active**\n\nIf none of those Pro tools are exposed, treat the site as **Free** and use the\nworkarounds. The **tool-presence check above is the definitive Pro-vs-Free\nsignal** — prefer it. (`detect-elementor-version` is reliable on current builds —\nthe old v1.5.0 schema bug is fixed — but it reports **atomic/version** support,\nnot a Pro flag, so it doesn't settle Pro-vs-Free on its own.)\n\n> **Record the verdict once** (\"Pro detected\" / \"Free only\") and state it to the\n> user up front, then follow the matching branch in every section below. Each\n> \"Forms\", \"Header/Footer\", and motion section is written as **If Pro → … /\n> If Free → …**. Don't mix paths.\n\n### 🧬 Also detect the ENGINE — classic vs atomic (Elementor 4 / V4)\n\nThere's a **second** axis that changes everything: the page engine. Elementor 4\nintroduced the **atomic / V4** engine (`e-flexbox`, `e-div-block`, atomic\nwidgets, a typed-prop `$$type` data model). Classic widget writes **do not\npersist on an atomic page** — they silently appear to do nothing. So before\nbuilding, decide classic vs atomic.\n\n**How to detect — by atomic tool availability (preferred):**\n\nThe atomic tools register only when the site is on the atomic engine. Check your\navailable tools:\n\n- `add-flexbox`, `add-div-block`, `add-atomic-heading` / `add-atomic-button` / …,\n  `add-atomic-widget` / `update-atomic-widget` present ⇒ **atomic (V4) engine**\n- Only classic `add-container` / `add-heading` / … present ⇒ **classic engine**\n\nYou may also call `detect-elementor-version` (reliable on current releases — it\nreturns whether atomic is supported). The old v1.5.0 schema bug is fixed; still,\ntool-presence is the most direct signal.\n\n> **Which elementor-mcp PLUGIN version am I on?** Some behaviour below branches on\n> it, and `detect-elementor-version` answers about *Elementor*, not the plugin.\n> Call **`server-info`**: it returns `plugin_version` (plus registered-vs-exposed\n> tool counts and what is withholding the rest). It is always registered and\n> cannot be disabled, so **its absence is itself the answer** — no `server-info`\n> means the site is on **1.28.0 or older**. Use it for the one branch that needs it —\n> whether `update-atomic-widget` can restyle in place (1.28.1+). Nothing else should\n> depend on the version: for the universal atomic tools, pass raw `$$type` values, which\n> are correct on every build.\n\n> ⚠️ **Antigravity / tight tool caps.** Antigravity caps MCP tools at ~100. The\n> full Pro+atomic set is ~113, so atomic tools can get truncated and never reach\n> the client — making a V4 site look like \"writes don't persist\". Fix: enable the\n> MCP plugin's **Low-tools mode** (WP Admin → MCP Tools screen). Its curated\n> essentials set **includes the 5 atomic essentials** (`detect-elementor-version`,\n> `add-atomic-widget`, `update-atomic-widget`, `add-flexbox`, `add-div-block`) and\n> stays under the cap.\n\n> 🐞 **Known root cause (older MCP builds).** Elementor often runs atomic as an\n> opt-in *experiment* while `ELEMENTOR_VERSION` still reads `3.x`. MCP builds that\n> gate atomic on `version_compare(ELEMENTOR_VERSION,'4.0.0','>=')` therefore never\n> register the atomic tools on those sites. Fixed upstream by detecting via the\n> experiment/module. If atomic tools are missing on a clearly-V4 site, update the\n> elementor-mcp plugin (or confirm the V4 experiment is on under Elementor →\n> Settings → Features).\n\nRecord **both** axes: e.g. \"Pro + atomic\", \"Pro + classic\", \"Free + classic\".\nThe Pro/Free axis picks Form vs Fluent Forms etc.; the engine axis picks classic\nvs atomic widget tools (next section).\n\nThe 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:\n\n- `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)\n- `content_width: \"boxed\"|\"full\"` + `boxed_width: {unit, size, sizes}`\n- `min_height: {unit, size, sizes}` — use unit `vh` for full-screen heroes\n- `padding`/`margin: {unit, top, right, bottom, left, isLinked}` — `isLinked: false` when sides differ\n- `background_background: \"classic\"|\"gradient\"|\"video\"` — must be set first or other background_* keys are ignored\n- `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)\n\n### Widget call convention — flat params, NOT nested in `settings`\n\nThis bit me hard the first time. The `add-*` shortcut tools take their settings as **top-level parameters**, not inside a `settings: {}` object:\n\n```js\n// ✓ CORRECT\nmcp__elementor__elementor-mcp-add-heading({\n  post_id: 11,\n  parent_id: \"abc123\",\n  title: \"where estates <em>are entrusted</em>\",\n  header_size: \"h1\",\n  title_color: \"#FFFFFF\",\n  typography_typography: \"custom\",       // ← required to enable typography\n  typography_font_family: \"Cormorant Garamond\",\n  typography_font_size: {size: 110, unit: \"px\"},\n  typography_font_weight: \"300\",\n  typography_line_height: {size: 0.98, unit: \"em\"},\n})\n\n// ✗ WRONG — silently fails or returns \"title is required\"\nmcp__elementor__elementor-mcp-add-heading({\n  post_id: 11,\n  parent_id: \"abc123\",\n  settings: {title: \"...\", typography_font_family: \"...\"}\n})\n```\n\n`add-container` is the **exception** — it takes a `settings: {}` object. Don't generalize from one to the other.\n\n### Always set `typography_typography: \"custom\"`\n\nWithout 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.\n\n### Italic emphasis pattern\n\nDisplay headings often need a single italic-emphasized word. Don't use a separate widget — just inline `<em>` in the title:\n\n```js\ntitle: \"A <em>quiet</em> practice for an <em>uncommon</em> clientele.\"\n```\n\nCormorant Garamond and most luxury serifs have italic variants that auto-load when `<em>` appears. Confirm via the rendered page; if italics fail, the global typography needs the italic variant explicitly enabled.\n\n### Responsive values — suffix keys (classic) vs. variants (atomic)\n\nElementor stores a responsive control's per-breakpoint values under **suffixed keys**.\nThe base (desktop) value has **no suffix**; each breakpoint appends its own suffix to the\n**same base control key**, with the **same value shape** as desktop:\n\n```js\n// classic widget — tablet/mobile overrides of the same control:\nmcp__elementor__elementor-mcp-add-heading({\n  post_id, parent_id,\n  title: \"...\",\n  typography_font_size: {size: 110, unit: \"px\"},          // desktop (base, no suffix)\n  typography_font_size_tablet: {size: 72, unit: \"px\"},    // tablet\n  typography_font_size_mobile: {size: 44, unit: \"px\"},    // mobile\n  align: \"left\", align_tablet: \"center\",                  // alignment per breakpoint\n})\n```\n\n**The suffix set is breakpoint-dependent — do NOT hardcode an incomplete list.** It\nderives from Elementor's **active breakpoints** (`add_responsive_control()`), so beyond\n`_tablet` / `_mobile` a site may expose `_widescreen`, `_laptop`, `_tablet_extra`,\n`_mobile_extra`, or custom ones. Read the site's breakpoints rather than assuming; the\nfork passes any `<base>_<breakpoint>` key through as long as `<base>` is a real control.\n\n> **Atomic (V4) is different — no suffix keys.** On a V4 page responsive lives in a style\n> definition's **`variants` array**, keyed by a `breakpoint` meta (`desktop` = base, then\n> `tablet`/`mobile`/custom) — Global Classes take a `variants` param, local styles add\n> variant entries. Never put `_tablet`/`_mobile` suffix keys on atomic elements. See\n> `references/atomic-v4.md` and `references/design-system-crud.md`.\n\n## The widget-vs-HTML decision — DEFAULT TO NATIVE WIDGETS\n\n> 🚨 **CRITICAL ANTI-PATTERN — read this first.**\n>\n> **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.\n>\n> If you find yourself thinking *\"I'll just dump this section as HTML, it's faster,\"* **STOP.** Break it into native widgets.\n\n### Always default to native widgets\n\nFor every section the user wants, build it from native Elementor widgets:\n\n- **Headings** → `add-heading` widget *(supports inline `<em>` for italic emphasis)*\n- **Body copy** → `add-text-editor` widget\n- **Images** → `add-image` widget *(NOT an `<img>` tag inside an HTML widget)*\n- **Buttons / CTAs** → `add-button` widget *(NOT an `<a>` styled as a button)*\n- **Layout / spacing** → `add-container` with proper `flex_*` settings *(NOT `<div>`s with CSS flex)*\n- **Lists** → `add-icon-list` widget\n- **Tabs** → `add-tabs` widget\n- **Accordions / FAQs** → `add-accordion` widget\n- **Forms** → Fluent Forms shortcode via `add-shortcode` widget\n- **Nav menu in headers** → UAE Nav Menu widget *(`uael-nav-menu`)*\n\n### When HTML widget IS allowed *(narrow list — exceptions only)*\n\nOnly reach for an HTML widget in these specific cases. **Anything not on this list goes through native widgets.**\n\n1. **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.)*\n2. **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.**\n3. **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.\"\n4. **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.\n\n### What about card grids of 4+ items?\n\nEarlier 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.\n\n**The correct path for card grids:**\n\n- Build the first card with native widgets *(Container → Image → Heading → Text Editor → Button)*\n- Use `duplicate-element` to copy it 3+ more times\n- Use `update-element` to change the copy/image on each duplicate\n- Wrap them in a parent Container with `flex_direction: row` and `flex_wrap: wrap`\n\nThis 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.\n\n> **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.\n\n### Cross-widget styling — `<style>`-only HTML widgets\n\nWhen you need to style a native widget from outside (e.g., overriding the Tabs widget tab strip styles that the widget controls don't expose), use a **`<style>`-only HTML widget**: it contains ONLY a `<style>` block — no markup, no rendered content. Scope every selector to the parent Elementor element ID:\n\n```html\n<style>\n.elementor-element-f8d1545 .elementor-tab-title {\n  text-transform: uppercase !important;\n  letter-spacing: .26em !important;\n}\n.elementor-element-f8d1545 .elementor-tab-title.elementor-active {\n  border-bottom-color: #171615 !important;\n}\n</style>\n```\n\nThe `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.\n\n> ⚠️ **An HTML widget used for cross-widget styling MUST contain only `<style>`.** 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.\n\n## Building on Elementor 4 (atomic / V4)\n\nElementor 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`.**\n\n> **Atomic local styles wiring.** Atomic styling attaches through **two coupled pieces** —\n> `settings.classes` (a typed list of class ids the element wears) **and** a separate\n> top-level `styles` map holding each class's definition. Every id in `settings.classes`\n> must resolve to a local `styles` entry or a Global Class `g-` id, or it styles nothing.\n> The dedicated helpers build the local `styles` map for you **at creation**. To restyle an\n> element that already exists (check the plugin version with `server-info` — absent means\n> ≤1.28.0): on plugin **1.28.1+** pass flat style params to\n> `update-atomic-widget` and it merges them into the base variant; on **older builds** it\n> merged `settings` only and could not write the `styles` map, so restyle there by\n> (re)creating with a style-capable helper / universal `add-atomic-widget`, or point\n> `settings.classes` at a Global Class. Full pattern → `references/atomic-v4.md`.\n\n### Converting a classic (V3) design to atomic (V4)\n\nThere's no in-place migrator — you **rebuild** the design on a fresh V4 page with atomic\ntools (classic/atomic never mix). Tool map, `$$type` rules, styling parity, and a worked\nexample → **load `references/v3-to-v4-conversion.md`.**\n\n## Elementor 4 design system — Global Classes, Variables, Interactions (v1.14+)\n\nOn an **atomic (V4)** site the fork exposes CRUD for the shared design system, so you can\nauthor reusable styling instead of re-styling every element:\n\n- **Global Classes** (reusable style bundles): `create-global-class`, `update-global-class`,\n  `delete-global-class`, `apply-global-class` (+ read `list-global-classes`).\n- **Variables** (color/font/size design tokens): `list-variables`, `get-variable`,\n  `create-variable`, `edit-variable`, `delete-variable`, `restore-variable`.\n- **Interactions** (per-element scroll/hover/click animations): `list-interactions`,\n  `add-interaction`, `edit-interaction`, `delete-interaction`.\n\n`restore-variable` (undo a soft-deleted token) and `edit-interaction` (id-addressable\nin-place animation edit) are **fork-superset** capabilities the editor path doesn't offer.\nAll writes need `manage_options`; these tools register only when the atomic engine +\nmatching experiments are on. **Full tool shapes, params, Pro gating, caps, and when to use\neach → load `references/design-system-crud.md`.**\n\n## When a write fails — errors & recovery\n\nThe fork's errors are built for self-correction; read them, don't just relay them:\n\n- **Wrong widget name** → `invalid_widget_type` / `widget_not_found` carry `Did you mean:`\n  suggestions **inline in the message** — pick the nearest and retry (no second lookup).\n- **Bad atomic settings** → `save_rejected` embeds the atomic type's **prop schema** inline\n  — correct the settings and retry in one round trip.\n- **Numeric/slider values** → `get-widget-schema` now returns `minimum`/`maximum`/`multipleOf`\n  and slider `unit` enums — clamp to them before writing.\n- **Governance (opt-in, only with the SiteAgent worker):** `governance_grant_required` /\n  `governance_grant_invalid` mean the write needs a gateway-minted approval grant (you can't\n  self-fix — the user must approve); `governance_render_failed` means the write broke the page\n  and **was reverted** (don't blindly re-send — fix the cause); `governance_rollback_failed`\n  means the revert itself failed and the page may be **partially written** — **stop and\n  escalate** with the snapshot id.\n\n**Full recovery playbook (all error codes, retry semantics, range hints) → load\n`references/error-recovery.md`.**\n\n## Brand kit — intake & tokens\n\nTriggers when the user is setting up a new client / brand, or says \"set up the\nbrand\". Establishes the design tokens every later build references **by name, never\nraw hex/font**. Full schema + tool shapes: [`references/brand-kit.md`](references/brand-kit.md).\n\nThe 8 color tokens (`brand, accent, heading, text, bg, surface, muted, border`) and\n2 font tokens (`heading-font, body-font`) map to **named Elementor custom globals**.\n\nFlow:\n\n1. **Gather** the brand — 8 colors (hex), 2 font families, logo — from the user's\n   brief, a Figma file, or by asking. If fewer than 8 colors are given, derive the\n   rest (`surface` = tint of `bg`; `muted` = lower-contrast `text`; `border` = light\n   grey) and state the derivation.\n2. **Apply** — `update-global-colors` with the 8 `{_id, title, color}` entries, then\n   `update-global-typography` with the 2 font entries. (Exact payloads in the\n   reference.)\n3. **Record** the token→value map back to the user so recipes and later edits reuse it.\n4. **Verify** — `get-global-settings` shows the 8 colors + 2 fonts by name.\n\nAfter intake, **bind widget colors to these globals** (or use the recorded token\nvalue when setting directly). Introducing an ad-hoc hex/font mid-build breaks brand\nconsistency — don't.\n\n## Recipe library\n\nReusable, brand-token-driven build sequences for common sections. **Before building a\nsection, consult the matching recipe** and bind everything to the brand tokens (see\n\"Brand kit\" above). Classic-first; apply the recipe's Pro/V4 variant note when those\nengines are active. Full trees + token bindings: [`references/recipes.md`](references/recipes.md).\n\nAvailable recipes: **Hero**, **Services grid**, **Split (image + text)**, **Stats\nband**, **Testimonials**, **CTA band**, **Contact**, **FAQ**, **Pricing**, **Logos\nstrip**. (The library grows — add a recipe when a new section type recurs.)\n\nRecipes reuse the rest of this skill's rules (native widgets not HTML dumps,\n`duplicate-element`/Loop Grid for grids, flat-param convention) — they don't restate\nthem.\n\n## When the user asks to BUILD — building order\n\n**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/`).\n\n**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.\n\n> 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.\n\nFor a new page, build top-down section by section, in small commits, verifying after each:\n\n1. **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`.\n2. `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\n3. (Via WP-CLI) Set as static front page: `wp option update show_on_front page; wp option update page_on_front <id>`\n4. Build sections — **use the matching recipe from the Recipe library** (outer container → inner boxed container, max-width ~1360px → content), bound to brand tokens\n5. After each section: `get-page-structure(post_id)` to verify nesting, or just curl the front page\n6. **Pause for human review** before building header/footer (which use Header Footer Elementor templates, a different flow)\n\n## When the user asks to EDIT\n\nApproach existing pages surgically — don't rebuild what you don't have to:\n\n1. `list-pages` to find the page they're editing\n2. `get-page-structure(post_id)` to see the current widget tree and grab element IDs\n3. 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\n4. Verify the edit by re-reading `get-page-structure` or curling the rendered page\n5. **Never delete a section unless they explicitly ask** — even when restructuring. Use `move-element` or `update-element` first.\n\n## When the user asks to REFERENCE / INSPECT\n\nRead-only tools, no writes. Useful for \"show me\", \"tell me\", \"what's\", \"list\" requests:\n\n- `list-pages` — what pages exist\n- `get-global-settings` — colors, typography, layout settings\n- `get-page-structure(post_id)` — what's on a page\n- `get-element-settings(element_id)` — exact settings of one widget\n- `find-element(post_id, ...)` — locate a widget by content/type\n\nFormat the response as a clear summary, not a JSON dump. The user wants understanding, not raw data.\n\n## When the user asks to EXPLORE / \"what can you do?\"\n\nGive a short menu *(don't dump all 75 tools)*. Point them at the four modes from the First Action Protocol with concrete examples:\n\n- *\"Build a homepage from this HTML mockup\"* → mode 1\n- *\"Make the hero text 20% smaller\"* → mode 2\n- *\"Show me what colors are currently set globally\"* → mode 3\n- *\"What pages exist on the site?\"* → mode 3\n\nThen ask which mode they want.\n\n## Header/Footer notes\n\nTheme 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`.**\n## Pro-only widgets & features\n\nWhen 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`.**\n## Dynamic data stacks — ACF & Crocoblock/JetEngine (Tier-0)\n\nBind 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`.**\n## Forms\n\nIf 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`.**\n## Setup gotchas (what bit me last time)\n\n- **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.\n- **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`.\n- **Neither MCP plugin is on wordpress.org.** Cannot install via REST API by slug — must download zips from GitHub Releases.\n- **The elementor-mcp release zipball has an ugly auto-generated folder name** (`Digitizers-elementor-mcp-<sha>/`). WordPress uses the folder name as the plugin slug. Repack with a clean `elementor-mcp/` folder before installing.\n- **Claude Code only loads `.mcp.json` at startup** — after writing one, the user must quit and reopen.\n- **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.\n- **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.\n\n## Live-host vs Local differences\n\n**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.\n\n**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://<live-domain>/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`.\n\n## Tool-loading discipline\n\nThe MCP exposes ~75 deferred tools. Don't load them all at once — fetch schemas lazily as you build:\n\n- **First call:** `list-pages` (no schema needed — pre-loaded by ToolSearch when triggered)\n- **Before building containers:** load `get-container-schema`, `add-container`, `update-container`\n- **Before placing widgets:** load `add-heading`, `add-text-editor`, `add-button`, `add-image`, `add-html` in one batch\n- **Before specific widgets:** load `add-tabs`, `add-icon-list`, `add-divider`, `add-spacer` as needed\n\nUse `ToolSearch` query format `select:tool1,tool2,tool3` to load multiple in one call.\n\n## What the MCP **cannot** do (set expectations)\n\n- 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)\n- Set the static front page (use `wp option update`)\n- Build a custom header/footer on Elementor Free without the HFE plugin *(with Pro, use native Theme Builder via `create-theme-template`)*\n- Auto-translate arbitrary HTML/CSS into Elementor widgets — you read the source design and emit widget calls\n- Pixel-perfect parity with hand-coded HTML — Elementor's flexbox container model is the ceiling *(Pro adds CSS Grid containers, raising it)*\n\n**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.\n\n## Optional companion tooling — `wordpress-api-pro` (content/SEO/commerce ops)\n\n> **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.\n\nThe 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.\n\nThis skill is one stage of the studio toolbox (audit → build → content → host → ads). **Full handoffs + a \"where am I\" router → load `references/lifecycle.md`.**\n\n**When the user explicitly asks for one of these, `wordpress-api-pro` is the right tool instead of the MCP:**\n\n| Task | Script |\n|---|---|\n| Upload an actual image/file to the media library (then feed its URL/ID to an Elementor Image widget) | `upload_media.py` |\n| Read/write SEO meta (Rank Math / Yoast) | `seo_meta.py` |\n| Read/write ACF or JetEngine custom fields | `acf_fields.py` / `jetengine_fields.py` |\n| List/create/update WooCommerce products | `woo_products.py` |\n| Bulk content changes across many posts or **multiple sites** (dry-run first) | `batch_update.py`, `wp.sh` |\n| Plain post/page CRUD outside Elementor | `create_post.py` / `update_post.py` / `get_post.py` / `list_posts.py` |\n\n**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.\n\n> 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`.\n\n## Quick reference — the build flow that works *(mode 1 only)*\n\n> 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.\n\n```\n1. setup-elementor-mcp.sh          # one-time, ~3 minutes\n2. Quit + reopen Claude Code       # picks up .mcp.json\n3. list-pages                      # confirm auth\n4. get-global-settings             # see current kit\n5. update-global-colors + typography\n6. create-page (Elementor Canvas template)\n7. Set as front page via WP-CLI\n8. Build sections top-down, one at a time\n9. After each: get-page-structure or curl the front page\n10. Pause for human review before header/footer\n```\n\nWhen working from a designed HTML mockup, map the source design to Elementor like this:\n\n- **Brand colors** → `update-global-colors`\n- **Brand fonts** → `update-global-typography`\n- **Section copy** → `add-heading` + `add-text-editor` widgets\n- **Card grids (4+ identical items)** → build one card with native widgets, then `duplicate-element` and `update-element` per copy\n- **Tabs/accordions** → native `add-tabs`/`add-accordion` widgets *(HTML allowed inside `tab_content` strings only — see anti-pattern section)*\n- **Forms** → real Fluent Forms shortcode via `add-shortcode` widget *(see Fluent Forms section)*\n- **Headers/footers** → `elementor-hf` post type with UAE Nav Menu widget for nav\n\n> 🚨 **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.\n\nFile v1.7.0:_meta.json\n\n{\n  \"ownerId\": \"kn7afv05r120atbc75whrv1zkx825tt5\",\n  \"slug\": \"siteagent-elementor-studio\",\n  \"version\": \"1.7.0\",\n  \"publishedAt\": 1789423622039\n}\n\nFile v1.7.0:references/atomic-v4.md\n\n# Building on Elementor 4 (atomic / V4) — reference\n\n## Building on Elementor 4 (atomic / V4)\n\nApply this section **only when you detected the atomic engine** (atomic tools\npresent). On a classic-engine site, ignore it and use the classic wi\n\nArchive v1.6.1: 21 files, 84644 bytes\n\nFiles: references/atomic-v4.md (9593b), references/brand-kit.md (3625b), references/design-system-crud.md (9059b), references/dynamic-data.md (3406b), references/engine-and-premium.md (4042b), references/error-recovery.md (7340b), references/forms.md (8624b), references/header-footer.md (3947b), references/lifecycle.md (1805b), references/pro-widgets.md (3128b), references/recipes.md (11027b), references/v3-to-v4-conversion.md (7250b), references/verticals/car-wash.md (2617b), references/verticals/dental.md (2832b), references/verticals/local-business.md (2647b), references/verticals/portfolio.md (2701b), references/verticals/salon.md (2527b), setup-elementor-mcp.sh (68579b), skill-card.md (3232b), SKILL.md (40965b), _meta.json (145b)\n\nArchive v1.6.0: 21 files, 84668 bytes\n\nFiles: references/atomic-v4.md (9593b), references/brand-kit.md (3625b), references/design-system-crud.md (9059b), references/dynamic-data.md (3406b), references/engine-and-premium.md (4042b), references/error-recovery.md (7340b), references/forms.md (8624b), references/header-footer.md (3947b), references/lifecycle.md (1805b), references/pro-widgets.md (3128b), references/recipes.md (11027b), references/v3-to-v4-conversion.md (7250b), references/verticals/car-wash.md (2617b), references/verticals/dental.md (2832b), references/verticals/local-business.md (2647b), references/verticals/portfolio.md (2701b), references/verticals/salon.md (2527b), setup-elementor-mcp.sh (68381b), skill-card.md (3391b), SKILL.md (40965b), _meta.json (145b)\n\nArchive v1.5.0: 21 files, 81267 bytes\n\nFiles: references/atomic-v4.md (9593b), references/brand-kit.md (3625b), references/design-system-crud.md (9059b), references/dynamic-data.md (3406b), references/engine-and-premium.md (4042b), references/error-recovery.md (7340b), references/forms.md (8624b), references/header-footer.md (3947b), references/lifecycle.md (1805b), references/pro-widgets.md (3128b), references/recipes.md (11027b), references/v3-to-v4-conversion.md (7250b), references/verticals/car-wash.md (2617b), references/verticals/dental.md (2832b), references/verticals/local-business.md (2647b), references/verticals/portfolio.md (2701b), references/verticals/salon.md (2527b), setup-elementor-mcp.sh (59508b), skill-card.md (4031b), SKILL.md (40965b), _meta.json (145b)\n\nArchive v1.4.0: 21 files, 74352 bytes\n\nFiles: references/atomic-v4.md (9593b), references/brand-kit.md (3625b), references/design-system-crud.md (9059b), references/dynamic-data.md (3406b), references/engine-and-premium.md (4042b), references/error-recovery.md (7340b), references/forms.md (8624b), references/header-footer.md (3947b), references/lifecycle.md (1805b), references/pro-widgets.md (3128b), references/recipes.md (11027b), references/v3-to-v4-conversion.md (7250b), references/verticals/car-wash.md (2617b), references/verticals/dental.md (2832b), references/verticals/local-business.md (2647b), references/verticals/portfolio.md (2701b), references/verticals/salon.md (2527b), setup-elementor-mcp.sh (43066b), skill-card.md (3297b), SKILL.md (40310b), _meta.json (145b)\n\nArchive v1.3.2: 21 files, 71798 bytes\n\nFiles: references/atomic-v4.md (8107b), references/brand-kit.md (3625b), references/design-system-crud.md (9059b), references/dynamic-data.md (3406b), references/engine-and-premium.md (4042b), references/error-recovery.md (7340b), references/forms.md (8624b), references/header-footer.md (3947b), references/lifecycle.md (1805b), references/pro-widgets.md (3128b), references/recipes.md (11027b), references/v3-to-v4-conversion.md (6577b), references/verticals/car-wash.md (2617b), references/verticals/dental.md (2832b), references/verticals/local-business.md (2647b), references/verticals/portfolio.md (2701b), references/verticals/salon.md (2527b), setup-elementor-mcp.sh (41421b), skill-card.md (2709b), SKILL.md (38296b), _meta.json (145b)\n\nArchive v1.3.0: 21 files, 70854 bytes\n\nFiles: references/atomic-v4.md (8107b), references/brand-kit.md (3625b), references/design-system-crud.md (9059b), references/dynamic-data.md (3406b), references/engine-and-premium.md (1679b), references/error-recovery.md (7340b), references/forms.md (8624b), references/header-footer.md (3947b), references/lifecycle.md (1805b), references/pro-widgets.md (3128b), references/recipes.md (11027b), references/v3-to-v4-conversion.md (6577b), references/verticals/car-wash.md (2617b), references/verticals/dental.md (2832b), references/verticals/local-business.md (2647b), references/verticals/portfolio.md (2701b), references/verticals/salon.md (2527b), setup-elementor-mcp.sh (41421b), skill-card.md (3254b), SKILL.md (38085b), _meta.json (145b)\n\nArchive v1.2.1: 18 files, 56473 bytes\n\nFiles: references/atomic-v4.md (4096b), references/brand-kit.md (3625b), references/dynamic-data.md (3406b), references/engine-and-premium.md (1679b), references/forms.md (8624b), references/header-footer.md (3947b), references/lifecycle.md (1805b), references/pro-widgets.md (3128b), references/recipes.md (11027b), references/verticals/car-wash.md (2617b), references/verticals/dental.md (2832b), references/verticals/local-business.md (2647b), references/verticals/portfolio.md (2701b), references/verticals/salon.md (2527b), setup-elementor-mcp.sh (41421b), skill-card.md (3404b), SKILL.md (32849b), _meta.json (145b)\n\nArchive v1.2.0: 18 files, 56501 bytes\n\nFiles: references/atomic-v4.md (4096b), references/brand-kit.md (3625b), references/dynamic-data.md (3406b), references/engine-and-premium.md (1679b), references/forms.md (8624b), references/header-footer.md (3947b), references/lifecycle.md (1805b), references/pro-widgets.md (3128b), references/recipes.md (11027b), references/verticals/car-wash.md (2617b), references/verticals/dental.md (2832b), references/verticals/local-business.md (2647b), references/verticals/portfolio.md (2701b), references/verticals/salon.md (2527b), setup-elementor-mcp.sh (41421b), skill-card.md (3485b), SKILL.md (32843b), _meta.json (145b)","readmeExcerpt":"Skill: SiteAgent Elementor Studio Owner: benkalsky Summary: 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","codeSnippets":[],"executableExamples":[{"language":"text","snippet":"What would you like to do with your Elementor site?\n\n  1. Build       — create new pages or sections from a design\n  2. Edit        — change something on an existing page\n  3. Reference   — inspect current state (pages, colors, fonts, content)\n  4. Explore     — show me what's possible / what can the MCP do here"},{"language":"bash","snippet":"bash \"<skill-dir>/setup-elementor-mcp.sh\""},{"language":"bash","snippet":"bash ~/.claude/scripts/setup-elementor-mcp.sh"},{"language":"text","snippet":"mcp__elementor__elementor-mcp-list-pages   # confirms auth + lists existing pages\nmcp__elementor__elementor-mcp-get-global-settings   # see existing colors/fonts kit\nmcp__elementor__elementor-mcp-get-container-schema  # ground truth on flex_* key names"},{"language":"js","snippet":"// ✓ CORRECT\nmcp__elementor__elementor-mcp-add-heading({\n  post_id: 11,\n  parent_id: \"abc123\",\n  title: \"where estates <em>are entrusted</em>\",\n  header_size: \"h1\",\n  title_color: \"#FFFFFF\",\n  typography_typography: \"custom\",       // ← required to enable typography\n  typography_font_family: \"Cormorant Garamond\",\n  typography_font_size: {size: 110, unit: \"px\"},\n  typography_font_weight: \"300\",\n  typography_line_height: {size: 0.98, unit: \"em\"},\n})\n\n// ✗ WRONG — silently fails or returns \"title is required\"\nmcp__elementor__elementor-mcp-add-heading({\n  post_id: 11,\n  parent_id: \"abc123\",\n  settings: {title: \"...\", typography_font_family: \"...\"}\n})"},{"language":"js","snippet":"title: \"A <em>quiet</em> practice for an <em>uncommon</em> clientele.\""}],"parameters":null,"dependencies":[],"permissions":[],"extractedFiles":[{"path":"SKILL.md","content":"---\nname: siteagent-elementor-studio\nversion: 1.7.1\nlicense: MIT\ndescription: 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 covers initial install of the MCP Adapter + elementor-mcp plugins, app-password auth wiring, schema-loading discipline, and the widget-vs-HTML decision tree. SKIP for Bricks, Divi, Beaver Builder, or non-Elementor WordPress builds.\npermissions:\n  shell: \"Runs the bundled setup script (files/setup-elementor-mcp.sh) — only on explicit user confirmation. It shells out to curl/unzip/zip/python3 and, for Local sites, drives Local by Flywheel's bundled WP-CLI (plugin install/activate) against the running site's PHP + MySQL socket.\"\n  network:\n    - \"GitHub release download over HTTPS from the trusted Digitizers/elementor-mcp repo (api.github.com + release asset host) — the elementor-mcp plugin zip. By default the release this kit pins (EMCP_DEFAULT_VERSION in the setup script), checked before it is unpacked or installed against the sha256 recorded beside the pin — out of band from the download. EMCP_PIN_VERSION=<tag> or =latest selects another release, checked against the digest that release publishes (integrity, not provenance) or EMCP_EXPECTED_SHA256. Nothing is installed unverified\"\n    - \"The target WordPress site's REST API (/wp-json/ — auth check, plugin list/install, MCP route verification). Plaintext http:// is refused for a non-local host unless that exact host is named in WP_ALLOW_HTTP, because the run sends a reusable application password on every request\"\n  filesystem:\n    - \"Writes .mcp.json in the current working directory, created mode 600 before the credential is written (it embeds a reusable Basic-Auth WordPress credential), and appends .mcp.json to .gitignore there\"\n    - \"Reads Local by Flywheel site paths + bundled WP-CLI/PHP binaries; creates a temp working dir for the plugin zip\"\n  env:\n    - \"WP_URL / WP_USERNAME / WP_APP_PASSWORD (when used to supply the target site + Application Password auth)\"\n    - \"EMCP_PIN_VERSION (optional — a release tag, or latest, instead of the re"},{"path":"_meta.json","content":"{\n  \"ownerId\": \"kn7afv05r120atbc75whrv1zkx825tt5\",\n  \"slug\": \"siteagent-elementor-studio\",\n  \"version\": \"1.7.1\",\n  \"publishedAt\": 1789427139989\n}"},{"path":"references/atomic-v4.md","content":"# Building on Elementor 4 (atomic / V4) — reference\n\n## Building on Elementor 4 (atomic / V4)\n\nApply this section **only when you detected the atomic engine** (atomic tools\npresent). On a classic-engine site, ignore it and use the classic widget tools\neverywhere. **Never mix:** classic `add-heading`/`add-container` writes do not\npersist on an atomic page, and atomic writes don't belong on a classic page.\n\n### Atomic tool family — use instead of the classic ones\n\n| Need | Classic (don't use on V4) | **Atomic (V4)** |\n|---|---|---|\n| Flex container | `add-container` | `add-flexbox` *(direction/justify/align/gap/wrap/padding/background_color)* |\n| Block container | `add-container` | `add-div-block` |\n| Heading | `add-heading` | `add-atomic-heading` |\n| Body text | `add-text-editor` | `add-atomic-paragraph` |\n| Button | `add-button` | `add-atomic-button` |\n| Image | `add-image` | `add-atomic-image` |\n| SVG / video / divider | `add-icon` / `add-html` | `add-atomic-svg` / `add-atomic-youtube` / `add-atomic-video` / `add-atomic-divider` |\n| Anything else | `add-widget` | `add-atomic-widget` *(any atomic type; pass raw `$$type` settings — correct on every version, see note)* / `update-atomic-widget` |\n\n### The atomic data model (what's different)\n\n- **Typed props (`$$type`).** Atomic settings are typed values, not flat strings.\n  For the **dedicated** helper tools (`add-atomic-heading`, `add-atomic-paragraph`,\n  `add-atomic-button`, `add-flexbox`, …) the MCP wraps them for you — **pass simple\n  flat values** (e.g. `title: \"Hello\"`, a hex `color`, a `{size,unit}` dimension) and\n  it stores them in the `$$type` format Elementor's atomic engine expects.\n  **For the universal `add-atomic-widget` / `update-atomic-widget`, always pass raw\n  `$$type` values** (fetch the shape with `get-widget-schema`). That is correct on\n  every plugin version: since 1.27.0 the save path coerces flat values through\n  `Atomic_Props::coerce_tree()`, and already-typed props pass through it untouched —\n  whereas on older builds those two tools wrote settings verbatim and a flat value\n  was silently saved as empty. Typed values work either way, and the version is not\n  always knowable mid-session, so don't make the write depend on it.\n\n  **A direct `_elementor_data` patch has no wrapper on any version.** It bypasses the\n  plugin entirely, so every prop must be raw `$$type` there too.\n- **Styles live in a separate `styles` map**, not inline on the element. Layout\n  props on `add-flexbox` (direction/justify/align/gap) are written as local styles\n  automatically — you don't hand-build the styles map.\n\n  **Since plugin 1.28.1, `update-atomic-widget` takes flat style params too**\n  (`padding`, `width`, `border_*`, `css_position`, `shadow_*`, typography, …) and\n  merges them into the element's base style variant, preserving props it wasn't\n  asked to change. Before that it wrote only `settings`, so a padding sent there\n  saved, reported success and rendered nothing — which is why older no"},{"path":"references/brand-kit.md","content":"# Brand kit — token vocabulary & intake\n\nThe studio's brand tokens. Every client site sets these once; every build\nreferences them **by name, never raw hex/font**. Tokens map to **named Elementor\ncustom globals** (the `update-global-colors` / `update-global-typography` tools\nwrite `custom_colors` / `custom_typography`, merged by `_id`).\n\n## Color tokens (8 named custom globals)\n\n| token `_id` | title | role |\n|---|---|---|\n| `brand` | Brand | primary brand color — buttons, links, emphasis |\n| `accent` | Accent | secondary highlight |\n| `heading` | Heading | heading text color |\n| `text` | Text | body text color |\n| `bg` | Background | page background |\n| `surface` | Surface | card / section panel background |\n| `muted` | Muted | secondary / subtle text, captions |\n| `border` | Border | hairlines, dividers, card borders |\n\nIf a client supplies fewer than 8, derive and state it: `surface` = a light tint of\n`bg`; `muted` = `text` at ~60% contrast; `border` = `text` at ~12% / a light grey.\n\n## Typography tokens (2 named custom globals)\n\n| token `_id` | title | role |\n|---|---|---|\n| `heading-font` | Heading Font | headings |\n| `body-font` | Body Font | body / UI |\n\n## Type scale (applied per-widget by recipes — not a global object)\n\n| step | size (px, desktop) | typical use |\n|---|---|---|\n| h1 | 48 | hero title |\n| h2 | 36 | section title |\n| h3 | 28 | card title |\n| h4 | 22 | sub-heading |\n| body-lg | 18 | lead paragraph |\n| body | 16 | default text |\n| small | 14 | captions, labels |\n\nDefaults: heading weight 700, heading line-height 1.15; body weight 400, body\nline-height 1.6. Scale down ~15–20% on mobile.\n\n## Logo\n\nRecord the logo media id / URL in the intake record. Header recipes use the Site\nLogo widget (Pro/UAE) or a Heading fallback.\n\n## Intake template (fill one per client)\n\n```json\n{\n  \"client\": \"Acme\",\n  \"colors\": {\n    \"brand\": \"#1A56DB\",\n    \"accent\": \"#F59E0B\",\n    \"heading\": \"#0F172A\",\n    \"text\": \"#334155\",\n    \"bg\": \"#FFFFFF\",\n    \"surface\": \"#F8FAFC\",\n    \"muted\": \"#64748B\",\n    \"border\": \"#E2E8F0\"\n  },\n  \"fonts\": { \"heading-font\": \"Rubik\", \"body-font\": \"Inter\" },\n  \"logo\": \"https://acme.example/logo.svg\"\n}\n```\n\n## Applying it (MCP tool shapes)\n\n`update-global-colors` — one entry per color token:\n\n```json\n{ \"colors\": [\n  { \"_id\": \"brand\",   \"title\": \"Brand\",      \"color\": \"#1A56DB\" },\n  { \"_id\": \"accent\",  \"title\": \"Accent\",     \"color\": \"#F59E0B\" },\n  { \"_id\": \"heading\", \"title\": \"Heading\",    \"color\": \"#0F172A\" },\n  { \"_id\": \"text\",    \"title\": \"Text\",       \"color\": \"#334155\" },\n  { \"_id\": \"bg\",      \"title\": \"Background\",  \"color\": \"#FFFFFF\" },\n  { \"_id\": \"surface\", \"title\": \"Surface\",    \"color\": \"#F8FAFC\" },\n  { \"_id\": \"muted\",   \"title\": \"Muted\",      \"color\": \"#64748B\" },\n  { \"_id\": \"border\",  \"title\": \"Border\",     \"color\": \"#E2E8F0\" }\n] }\n```\n\n`update-global-typography` — one entry per font token:\n\n```json\n{ \"typography\": [\n  { \"_id\": \"heading-font\", \"title\": \"Heading Font\",\n    \"typography_font_family\": \"Rubik\",\n    \"ty"},{"path":"references/design-system-crud.md","content":"# Elementor 4 design system CRUD — Global Classes, Variables, Interactions\n\nApply this only on an **atomic (V4) site** (atomic tools present) with the matching\nElementor experiments on. These tools author the *shared design system* — the same\nClass Manager / Variables / Interactions an editor user would build by hand — so an\nagent can create reusable styling instead of re-styling every element inline.\n\nAll three families register **conditionally**. If the tools below aren't in your\n`mcp__elementor__elementor-mcp-*` list, the site's engine/experiments don't support\nthem — fall back to inline atomic local styles (see `atomic-v4.md`).\n\n> **Permissions.** Every *write* here needs `manage_options` (mutating the shared\n> design system / kit is site-wide, not per-post). Variable/interaction *reads*\n> (`list-variables`, `get-variable`, `list-interactions`) need `edit_posts`;\n> interaction tools additionally require `edit_post` on the target page. If a call\n> returns a `forbidden` error, the connected app-password user lacks the cap.\n\n---\n\n## 1. Global Classes (reusable style bundles) — Elementor 4 Class Manager\n\nA Global Class is a named, reusable set of styles (a `g-<7hex>` id) you author once and\napply to many atomic elements — the design-system equivalent of a CSS utility class.\nCompanion read tool: `list-global-classes` (resolves opaque `g-` ids → names + CSS).\n\n| Tool | Does | Key params |\n|---|---|---|\n| `create-global-class` | Author a new class | `label` (e.g. `\"card-base\"`), `styles` (CSS-prop→value map), optional `variants` |\n| `update-global-class` | Edit in place, **keeps the `g-` id** so bindings survive | `class_id`, any of `label` / `styles` / `variants` |\n| `delete-global-class` | Remove by id | `class_id` |\n| `apply-global-class` | Bind an existing class to one element | `class_id`, `post_id`, `element_id` |\n\n- **Ergonomic styles.** `styles` is a plain map like `{\"color\":\"#111\",\"padding\":24,\"font-size\":\"1.25rem\"}`.\n  The tool wraps values into Elementor's atomic `$$type` props automatically. Colors\n  (`color`, `border-color`, …), sizes (`padding`, `margin`, `width`, `font-size`, …),\n  and unitless numbers (`z-index`, `flex-grow`, …) are typed correctly; anything else\n  is stored as a string prop.\n- **`styles` replaces only the base/desktop variant** on `update-global-class` — other\n  variants are kept. `variants` replaces matching breakpoint/state variants (see\n  Responsive below). `label` renames without touching styles.\n- **`apply-global-class` is idempotent** — re-applying an already-present class is a\n  no-op. It appends the `g-` id to the element's `settings.classes`. A **non-atomic**\n  element (no `classes` control) is rejected with error code `not_atomic`, and the\n  error embeds the element's compact settings schema so you can see what it *is*.\n- **Delete does not cascade.** Elementor ignores dangling `g-` references left on\n  elements — those elements simply lose that styling, the page isn't rewritten.\n- **Cap: 100 classes.** `cr"}],"languages":[],"docsSourceLabel":"CLAWHUB","editorialOverview":null,"editorialQuality":{"score":100,"threshold":65,"status":"thin","wordCount":2687,"uniquenessScore":39,"reasons":["uniqueness-below-45"]}},"media":{"evidence":{"source":"no-media","verified":false,"confidence":"low","updatedAt":"2026-10-11T11:08:17.193Z","emptyReason":"No screenshots, media assets, or demo links are available."},"primaryImageUrl":null,"mediaAssetCount":0,"assets":[],"demoUrl":null},"ownerResources":{"evidence":{"source":"unclaimed","verified":false,"confidence":"low","updatedAt":"2026-10-11T11:08:17.193Z","emptyReason":"This page has not been claimed by the agent owner."},"hasCustomPage":false,"customPageUpdatedAt":null,"customLinks":[],"structuredLinks":{"docsUrl":null,"demoUrl":null,"supportUrl":null,"pricingUrl":null,"statusUrl":null},"customPage":null},"relatedAgents":{"evidence":{"source":"protocol-neighbors","verified":false,"confidence":"medium","updatedAt":"2026-10-11T14:14:10.044Z","emptyReason":null},"items":[{"id":"8ebccd8e-3863-4187-8355-c3f14e1f9edf","entityType":"agent","canonicalPath":"/agent/iofficeai-aionui","slug":"iofficeai-aionui","name":"AionUi","description":"Free, local, open-source 24/7 Cowork app and OpenClaw for Gemini CLI, Claude Code, Codex, OpenCode, Qwen Code, Goose CLI, Auggie, and more | 🌟 Star if you like it!","url":"https://github.com/iOfficeAI/AionUi","homepage":"https://www.aionui.com","source":"GITHUB_REPOS","protocols":["MCP","OPENCLAW"],"capabilities":[],"safetyScore":100,"overallRank":70,"updatedAt":"2026-10-09T19:11:12.944Z","createdAt":"2026-02-25T03:38:16.584Z","downloads":null},{"id":"b917f68a-ebff-438e-84f8-3f4b2494c0bc","entityType":"agent","canonicalPath":"/agent/activepieces-activepieces","slug":"activepieces-activepieces","name":"activepieces","description":"AI Agents & MCPs & AI Workflow Automation • (~400 MCP servers for AI agents) • AI Automation / AI Agent with MCPs • AI Workflows & AI Agents • MCPs for AI Agents","url":"https://github.com/activepieces/activepieces","homepage":"https://www.activepieces.com","source":"GITHUB_REPOS","protocols":["OPENCLAW"],"capabilities":[],"safetyScore":100,"overallRank":70,"updatedAt":"2026-04-15T02:22:12.426Z","createdAt":"2026-02-25T03:38:12.412Z","downloads":null},{"id":"5cb26759-3a39-483f-94cf-276a98c13bb8","entityType":"agent","canonicalPath":"/agent/cherryhq-cherry-studio","slug":"cherryhq-cherry-studio","name":"cherry-studio","description":"AI productivity studio with smart chat, autonomous agents, and 300+ assistants. Unified access to frontier LLMs","url":"https://github.com/CherryHQ/cherry-studio","homepage":"https://cherry-ai.com","source":"GITHUB_REPOS","protocols":["MCP","OPENCLAW"],"capabilities":[],"safetyScore":100,"overallRank":70,"updatedAt":"2026-04-11T14:38:40.986Z","createdAt":"2026-02-25T03:38:19.379Z","downloads":null},{"id":"6f6582d0-5d76-4f0f-b81d-86520247950b","entityType":"agent","canonicalPath":"/agent/copilotkit-copilotkit","slug":"copilotkit-copilotkit","name":"CopilotKit","description":"The Frontend for Agents & Generative UI. React + Angular","url":"https://github.com/CopilotKit/CopilotKit","homepage":"https://docs.copilotkit.ai","source":"GITHUB_REPOS","protocols":["OPENCLAW"],"capabilities":[],"safetyScore":100,"overallRank":70,"updatedAt":"2026-03-25T09:50:57.846Z","createdAt":"2026-02-25T03:39:14.617Z","downloads":null}],"links":{"hub":"/agent","source":"/agent/source/clawhub","protocols":[{"label":"OpenClaw","href":"/agent/protocol/openclew"}]}}}