elementor-mcp-agent 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/server.ts","../src/config.ts","../src/tools/sites.ts","../src/types/tool.ts","../src/throttle/token-bucket.ts","../src/api/errors.ts","../src/utils/logger.ts","../src/api/wp-rest.ts","../src/tools/pages.ts","../src/elementor/data-parser.ts","../src/elementor/safety.ts","../src/utils/confirmation.ts","../src/tools/templates.ts","../src/tools/updates.ts","../src/tools/index.ts","../src/resources/index.ts"],"sourcesContent":["/**\n * MCP server entry point — stdio transport.\n *\n * Run with: `npm run dev` (watch) or `npm start` (compiled).\n * Test interactively: `npm run inspector`.\n */\nimport { readFileSync } from \"node:fs\";\nimport { dirname, resolve } from \"node:path\";\nimport { fileURLToPath } from \"node:url\";\n\nimport { Server } from \"@modelcontextprotocol/sdk/server/index.js\";\nimport { StdioServerTransport } from \"@modelcontextprotocol/sdk/server/stdio.js\";\nimport {\n CallToolRequestSchema,\n ListToolsRequestSchema,\n ListResourcesRequestSchema,\n ReadResourceRequestSchema,\n} from \"@modelcontextprotocol/sdk/types.js\";\nimport type { z } from \"zod\";\nimport { zodToJsonSchema } from \"zod-to-json-schema\";\n\nimport { loadConfig } from \"./config.js\";\nimport { tools } from \"./tools/index.js\";\nimport { logger } from \"./utils/logger.js\";\nimport { listResources, readResource } from \"./resources/index.js\";\n\nconst __dirname = dirname(fileURLToPath(import.meta.url));\nconst pkg = JSON.parse(readFileSync(resolve(__dirname, \"../package.json\"), \"utf8\")) as {\n name: string;\n version: string;\n};\n\nfunction toMcpInputSchema(zod: z.ZodType<unknown>): Record<string, unknown> {\n // Draft 7 — accepted by both Anthropic API and the broader MCP ecosystem.\n return zodToJsonSchema(zod, { target: \"jsonSchema7\", $refStrategy: \"none\" }) as Record<string, unknown>;\n}\n\nasync function main() {\n // Validate config early — fail fast with a clear message rather than letting tools blow up\n try {\n loadConfig();\n } catch (e) {\n logger.error((e as Error).message);\n process.stderr.write(\"\\n\" + (e as Error).message + \"\\n\");\n process.exit(1);\n }\n\n const server = new Server(\n { name: pkg.name, version: pkg.version },\n { capabilities: { tools: {}, resources: {} } },\n );\n\n server.setRequestHandler(ListToolsRequestSchema, async () => ({\n tools: tools.map((t) => ({\n name: t.name,\n description: t.description,\n inputSchema: toMcpInputSchema(t.inputSchema),\n outputSchema: t.outputSchema ? toMcpInputSchema(t.outputSchema) : undefined,\n annotations: t.annotations,\n })),\n }));\n\n server.setRequestHandler(CallToolRequestSchema, async (req) => {\n const tool = tools.find((t) => t.name === req.params.name);\n if (!tool) throw new Error(`Unknown tool: ${req.params.name}`);\n const args = (req.params.arguments ?? {}) as unknown;\n const parsed = tool.inputSchema.safeParse(args);\n if (!parsed.success) {\n const msg = parsed.error.issues.map((i) => ` ${i.path.join(\".\")}: ${i.message}`).join(\"\\n\");\n return {\n content: [{ type: \"text\" as const, text: `Invalid arguments for ${tool.name}:\\n${msg}` }],\n isError: true,\n };\n }\n try {\n const result = await tool.handler(parsed.data);\n return {\n content: [{ type: \"text\" as const, text: JSON.stringify(result, null, 2) }],\n };\n } catch (e) {\n logger.error({ tool: tool.name, err: (e as Error).message }, \"tool error\");\n return {\n content: [{ type: \"text\" as const, text: `${tool.name} failed: ${(e as Error).message}` }],\n isError: true,\n };\n }\n });\n\n // Resources — Elementor docs corpus\n server.setRequestHandler(ListResourcesRequestSchema, async () => ({\n resources: await listResources(),\n }));\n server.setRequestHandler(ReadResourceRequestSchema, async (req) => readResource(req.params.uri));\n\n const transport = new StdioServerTransport();\n await server.connect(transport);\n logger.info({ tools: tools.length }, `${pkg.name} v${pkg.version} ready`);\n}\n\nmain().catch((e) => {\n logger.error((e as Error).message);\n process.stderr.write(\"\\n\" + (e as Error).message + \"\\n\");\n process.exit(1);\n});\n","import { readFileSync } from \"node:fs\";\nimport { z } from \"zod\";\n\nconst SiteSchema = z.object({\n id: z.string().min(1, \"site id required\"),\n url: z.string().url(\"invalid site url\"),\n username: z.string().min(1),\n application_password: z.string().min(20, \"WP application password should be ~24 chars\"),\n ssh: z\n .object({\n host: z.string(),\n user: z.string(),\n port: z.coerce.number().int().min(1).max(65535).default(22),\n path: z.string().describe(\"WP root path on remote, e.g. ~/sites/example.com\"),\n key_path: z.string().optional().describe(\"absolute path to private key\"),\n })\n .optional(),\n});\n\nconst ConfigSchema = z.object({\n sites: z.array(SiteSchema).min(1, \"at least one site is required\"),\n default_site_id: z.string().optional(),\n rate_limit_per_minute: z.coerce.number().int().min(1).max(600).default(60),\n confirmation_ttl_seconds: z.coerce.number().int().min(10).max(600).default(60),\n log_level: z.enum([\"trace\", \"debug\", \"info\", \"warn\", \"error\", \"fatal\", \"silent\"]).default(\"info\"),\n});\n\nexport type Site = z.infer<typeof SiteSchema>;\nexport type Config = z.infer<typeof ConfigSchema>;\n\nlet cached: Config | null = null;\n\n/**\n * Loads configuration from environment.\n *\n * Two supported formats:\n * 1) ELEMENTOR_MCP_SITES — JSON array of Site objects (recommended)\n * 2) ELEMENTOR_MCP_CONFIG_PATH — path to a JSON file containing {sites: [...], ...}\n *\n * Plus optional env vars: ELEMENTOR_MCP_DEFAULT_SITE_ID, ELEMENTOR_MCP_RATE_LIMIT,\n * ELEMENTOR_MCP_CONFIRMATION_TTL, LOG_LEVEL.\n */\nexport function loadConfig(): Config {\n if (cached) return cached;\n\n let raw: unknown;\n if (process.env.ELEMENTOR_MCP_SITES) {\n try {\n raw = { sites: JSON.parse(process.env.ELEMENTOR_MCP_SITES) };\n } catch (e) {\n throw new Error(\"ELEMENTOR_MCP_SITES must be valid JSON: \" + (e as Error).message);\n }\n } else if (process.env.ELEMENTOR_MCP_CONFIG_PATH) {\n try {\n raw = JSON.parse(readFileSync(process.env.ELEMENTOR_MCP_CONFIG_PATH, \"utf8\"));\n } catch (e) {\n throw new Error(\"Cannot read ELEMENTOR_MCP_CONFIG_PATH: \" + (e as Error).message);\n }\n } else {\n throw new Error(\n [\n \"No site configuration provided.\",\n \"\",\n \"How to set it up:\",\n \" • Easiest: set ELEMENTOR_MCP_SITES to a JSON array of sites. Example:\",\n ' ELEMENTOR_MCP_SITES=\\'[{\"id\":\"my-site\",\"url\":\"https://example.com\",',\n ' \"username\":\"admin\",\"application_password\":\"xxxx xxxx xxxx xxxx xxxx xxxx\"}]\\'',\n \"\",\n \" • Or: set ELEMENTOR_MCP_CONFIG_PATH to a JSON file with the same structure.\",\n \"\",\n \"Get a WordPress Application Password at:\",\n \" https://{your-site}/wp-admin/profile.php#application-passwords-section\",\n \"\",\n \"Full docs: https://github.com/Mogacode-ma/elementor-mcp-agent#configure\",\n ].join(\"\\n\"),\n );\n }\n\n const r = raw as Record<string, unknown>;\n const merged = {\n sites: (r.sites as unknown) ?? [],\n default_site_id: r.default_site_id ?? process.env.ELEMENTOR_MCP_DEFAULT_SITE_ID,\n rate_limit_per_minute:\n r.rate_limit_per_minute ?? process.env.ELEMENTOR_MCP_RATE_LIMIT ?? undefined,\n confirmation_ttl_seconds:\n r.confirmation_ttl_seconds ?? process.env.ELEMENTOR_MCP_CONFIRMATION_TTL ?? undefined,\n log_level: r.log_level ?? process.env.LOG_LEVEL ?? undefined,\n };\n\n const parsed = ConfigSchema.safeParse(merged);\n if (!parsed.success) {\n const issues = parsed.error.issues\n .map((i) => ` - ${i.path.join(\".\")}: ${i.message}`)\n .join(\"\\n\");\n throw new Error(\n [\n \"Invalid configuration — required values missing or malformed:\",\n issues,\n \"\",\n \"See https://github.com/Mogacode-ma/elementor-mcp-agent#configure\",\n ].join(\"\\n\"),\n );\n }\n cached = parsed.data;\n return cached;\n}\n\nexport function _resetConfigCache(): void {\n cached = null;\n}\n\nexport function getSite(siteId?: string): Site {\n const cfg = loadConfig();\n if (!siteId) {\n const def = cfg.default_site_id ?? cfg.sites[0]?.id;\n if (!def) throw new Error(\"No site configured\");\n const s = cfg.sites.find((x) => x.id === def);\n if (!s) throw new Error(`Default site '${def}' not found in sites list`);\n return s;\n }\n const s = cfg.sites.find((x) => x.id === siteId);\n if (!s) {\n const available = cfg.sites.map((x) => x.id).join(\", \");\n throw new Error(`Site '${siteId}' not found. Available: ${available}`);\n }\n return s;\n}\n","import { z } from \"zod\";\nimport { defineTool } from \"../types/tool.js\";\nimport { loadConfig, getSite } from \"../config.js\";\nimport { wpRequest } from \"../api/wp-rest.js\";\n\nexport const listSitesTool = defineTool({\n name: \"list_sites\",\n description: \"List every WordPress site configured in this MCP server's pool. Best called first in a session to discover available sites.\",\n inputSchema: z.object({}),\n outputSchema: z.object({\n total: z.number(),\n default_site_id: z.string().optional(),\n sites: z.array(\n z.object({\n id: z.string(),\n url: z.string(),\n username: z.string(),\n has_ssh: z.boolean(),\n }),\n ),\n }),\n annotations: { readOnlyHint: true, idempotentHint: true, openWorldHint: false },\n async handler() {\n const cfg = loadConfig();\n return {\n total: cfg.sites.length,\n default_site_id: cfg.default_site_id ?? cfg.sites[0]?.id,\n sites: cfg.sites.map((s) => ({\n id: s.id,\n url: s.url,\n username: s.username,\n has_ssh: !!s.ssh,\n })),\n };\n },\n});\n\nexport const pingSiteTool = defineTool({\n name: \"ping_site\",\n description: \"Verify connectivity + authentication to a WordPress site. Calls /wp-json/wp/v2/users/me to validate credentials and returns the WP version + Elementor version if detected.\",\n inputSchema: z.object({\n site_id: z.string().optional().describe(\"Site id from list_sites. Defaults to the default site.\"),\n }),\n outputSchema: z.object({\n ok: z.boolean(),\n site_id: z.string(),\n url: z.string(),\n wp_version: z.string().optional(),\n elementor_version: z.string().optional(),\n elementor_pro_version: z.string().optional(),\n user: z.object({ id: z.number(), name: z.string(), roles: z.array(z.string()).optional() }).optional(),\n error: z.string().optional(),\n }),\n annotations: { readOnlyHint: true, idempotentHint: true, openWorldHint: true },\n async handler(input) {\n const site = getSite(input.site_id);\n try {\n const me = await wpRequest<{ id: number; name: string; roles?: string[] }>(\n \"/wp/v2/users/me?context=edit\",\n { siteId: site.id },\n );\n // Site health (WP version) — best effort\n let wp_version: string | undefined;\n try {\n const health = await wpRequest<{ wordpress: { version: string } }>(\n \"/wp-site-health/v1/info\",\n { siteId: site.id },\n );\n wp_version = health.wordpress?.version;\n } catch {\n /* admin-restricted on some installs */\n }\n // Elementor / Pro versions via plugins endpoint\n let elementor_version: string | undefined;\n let elementor_pro_version: string | undefined;\n try {\n const plugins = await wpRequest<Array<{ plugin: string; version: string; status: string }>>(\n \"/wp/v2/plugins\",\n { siteId: site.id },\n );\n for (const p of plugins) {\n if (p.plugin.startsWith(\"elementor/\") && p.plugin.endsWith(\"/elementor.php\"))\n elementor_version = p.version;\n if (p.plugin.startsWith(\"elementor-pro/\")) elementor_pro_version = p.version;\n }\n } catch {\n /* may require admin */\n }\n return {\n ok: true,\n site_id: site.id,\n url: site.url,\n wp_version,\n elementor_version,\n elementor_pro_version,\n user: { id: me.id, name: me.name, roles: me.roles },\n };\n } catch (e) {\n return {\n ok: false,\n site_id: site.id,\n url: site.url,\n error: (e as Error).message,\n };\n }\n },\n});\n","import type { z } from \"zod\";\n\nexport interface ToolDefinition<TInput = unknown, TOutput = unknown> {\n name: string;\n description: string;\n inputSchema: z.ZodType<TInput>;\n outputSchema?: z.ZodType<TOutput>;\n annotations?: {\n readOnlyHint?: boolean;\n idempotentHint?: boolean;\n destructiveHint?: boolean;\n openWorldHint?: boolean;\n title?: string;\n };\n handler: (input: TInput) => Promise<TOutput>;\n}\n\n/**\n * Helper that lets each tool infer its input/output types from the Zod schemas\n * without needing explicit generic parameters.\n */\nexport function defineTool<S extends z.ZodType, O extends z.ZodType>(\n def: Omit<ToolDefinition<z.infer<S>, z.infer<O>>, \"inputSchema\" | \"outputSchema\"> & {\n inputSchema: S;\n outputSchema?: O;\n },\n): ToolDefinition<z.infer<S>, z.infer<O>> {\n return def as ToolDefinition<z.infer<S>, z.infer<O>>;\n}\n","/**\n * Per-site token bucket so we don't hammer a WordPress install.\n * Default 60 req/min — most managed hosts (incl. Infomaniak) start throttling\n * around that rate on the wp-json endpoint.\n */\nexport class TokenBucket {\n private tokens: number;\n private lastRefill = Date.now();\n private readonly refillPerMs: number;\n\n constructor(private readonly capacityPerMinute: number) {\n this.tokens = capacityPerMinute;\n this.refillPerMs = capacityPerMinute / 60_000;\n }\n\n async acquire(): Promise<void> {\n while (this.tokens < 1) {\n this.refill();\n if (this.tokens < 1) await new Promise((r) => setTimeout(r, 80));\n }\n this.tokens -= 1;\n }\n\n private refill(): void {\n const now = Date.now();\n this.tokens = Math.min(\n this.capacityPerMinute,\n this.tokens + (now - this.lastRefill) * this.refillPerMs,\n );\n this.lastRefill = now;\n }\n\n get available(): number {\n this.refill();\n return Math.floor(this.tokens);\n }\n}\n\nconst buckets = new Map<string, TokenBucket>();\nexport function bucketFor(siteId: string, perMinute: number): TokenBucket {\n let b = buckets.get(siteId);\n if (!b) {\n b = new TokenBucket(perMinute);\n buckets.set(siteId, b);\n }\n return b;\n}\n","export type WPErrorKind = \"auth\" | \"not_found\" | \"validation\" | \"rate_limited\" | \"server\" | \"network\" | \"unknown\";\n\nexport class WPError extends Error {\n constructor(\n public readonly kind: WPErrorKind,\n public readonly code: string,\n message: string,\n public readonly raw?: unknown,\n ) {\n super(message);\n this.name = \"WPError\";\n }\n}\n\nexport function fromHttp(status: number, body: unknown, fallback = \"WordPress API error\"): WPError {\n const raw = body as Record<string, unknown> | undefined;\n const code = (raw?.code as string) ?? `http_${status}`;\n const msg = (raw?.message as string) ?? fallback;\n let kind: WPErrorKind = \"unknown\";\n if (status === 401 || status === 403) kind = \"auth\";\n else if (status === 404) kind = \"not_found\";\n else if (status === 422 || status === 400) kind = \"validation\";\n else if (status === 429) kind = \"rate_limited\";\n else if (status >= 500) kind = \"server\";\n return new WPError(kind, code, msg, body);\n}\n","import pino from \"pino\";\n\nexport const logger = pino(\n {\n level: process.env.LOG_LEVEL ?? \"info\",\n base: { name: \"elementor-mcp-agent\" },\n },\n // Critical: stdout is reserved for MCP JSON-RPC. All logs MUST go to stderr.\n pino.destination(2),\n);\n","import { loadConfig, getSite, Site } from \"../config.js\";\nimport { bucketFor } from \"../throttle/token-bucket.js\";\nimport { fromHttp, WPError } from \"./errors.js\";\nimport { logger } from \"../utils/logger.js\";\n\nfunction authHeader(site: Site): string {\n const encoded = Buffer.from(`${site.username}:${site.application_password}`).toString(\"base64\");\n return `Basic ${encoded}`;\n}\n\nexport interface RequestOptions {\n siteId?: string;\n query?: Record<string, string | number | boolean | undefined>;\n body?: unknown;\n headers?: Record<string, string>;\n method?: \"GET\" | \"POST\" | \"PUT\" | \"PATCH\" | \"DELETE\";\n}\n\nexport async function wpRequest<T = unknown>(\n path: string,\n opts: RequestOptions = {},\n): Promise<T> {\n const cfg = loadConfig();\n const site = getSite(opts.siteId);\n await bucketFor(site.id, cfg.rate_limit_per_minute).acquire();\n\n const url = new URL(path.startsWith(\"http\") ? path : `${site.url.replace(/\\/$/, \"\")}/wp-json${path.startsWith(\"/\") ? \"\" : \"/\"}${path}`);\n if (opts.query) {\n for (const [k, v] of Object.entries(opts.query)) {\n if (v !== undefined) url.searchParams.set(k, String(v));\n }\n }\n\n const method = opts.method ?? (opts.body ? \"POST\" : \"GET\");\n const headers: Record<string, string> = {\n Authorization: authHeader(site),\n Accept: \"application/json\",\n \"User-Agent\": \"elementor-mcp-agent\",\n ...(opts.headers ?? {}),\n };\n if (opts.body && !headers[\"Content-Type\"]) headers[\"Content-Type\"] = \"application/json\";\n\n logger.debug({ method, url: url.toString(), site_id: site.id }, \"wp request\");\n\n let res: Response;\n try {\n res = await fetch(url.toString(), {\n method,\n headers,\n body: opts.body ? JSON.stringify(opts.body) : undefined,\n });\n } catch (e) {\n throw new WPError(\"network\", \"fetch_failed\", `Network error: ${(e as Error).message}`, e);\n }\n\n const text = await res.text();\n let parsed: unknown = text;\n if (text && (res.headers.get(\"content-type\") ?? \"\").includes(\"application/json\")) {\n try {\n parsed = JSON.parse(text);\n } catch {\n // fall through with raw text\n }\n }\n\n if (!res.ok) {\n throw fromHttp(res.status, parsed);\n }\n return parsed as T;\n}\n","import { z } from \"zod\";\nimport { defineTool } from \"../types/tool.js\";\nimport { wpRequest } from \"../api/wp-rest.js\";\nimport { parseElementorData, summarize, serializeElementorData, findReplaceInWidgets } from \"../elementor/data-parser.js\";\nimport { backupElementorData, flushElementorCSS } from \"../elementor/safety.js\";\nimport { issueConfirmation, consumeConfirmation } from \"../utils/confirmation.js\";\nimport { loadConfig } from \"../config.js\";\n\nexport const listElementorPagesTool = defineTool({\n name: \"list_elementor_pages\",\n description: \"List pages on a site that are built with Elementor (i.e. have _elementor_edit_mode = 'builder'). Returns id, title, slug, status, modified date.\",\n inputSchema: z.object({\n site_id: z.string().optional(),\n per_page: z.number().int().min(1).max(100).default(25),\n search: z.string().optional(),\n }),\n outputSchema: z.object({\n total: z.number(),\n pages: z.array(\n z.object({\n id: z.number(),\n title: z.string(),\n slug: z.string(),\n status: z.string(),\n link: z.string(),\n modified: z.string(),\n }),\n ),\n }),\n annotations: { readOnlyHint: true, idempotentHint: true, openWorldHint: true },\n async handler(input) {\n interface RawPage {\n id: number;\n title: { rendered: string };\n slug: string;\n status: string;\n link: string;\n modified: string;\n meta?: Record<string, unknown>;\n }\n const pages = await wpRequest<RawPage[]>(\"/wp/v2/pages\", {\n siteId: input.site_id,\n query: {\n per_page: input.per_page,\n search: input.search,\n meta_key: \"_elementor_edit_mode\",\n meta_value: \"builder\",\n context: \"edit\",\n _fields: \"id,title,slug,status,link,modified\",\n },\n });\n return {\n total: pages.length,\n pages: pages.map((p) => ({\n id: p.id,\n title: p.title.rendered,\n slug: p.slug,\n status: p.status,\n link: p.link,\n modified: p.modified,\n })),\n };\n },\n});\n\nexport const readPageElementorTool = defineTool({\n name: \"read_page_elementor\",\n description: \"Fetch the raw _elementor_data of a page and return a structured summary (counts by widget type, depth, total elements). Optionally returns the full parsed tree (verbose=true) which may be very large.\",\n inputSchema: z.object({\n site_id: z.string().optional(),\n page_id: z.number().int().positive(),\n verbose: z.boolean().default(false).describe(\"If true, return the entire parsed Elementor data tree (can be MBs).\"),\n }),\n outputSchema: z.object({\n page_id: z.number(),\n title: z.string(),\n summary: z.object({\n totalElements: z.number(),\n sections: z.number(),\n containers: z.number(),\n columns: z.number(),\n widgets: z.number(),\n maxDepth: z.number(),\n byWidgetType: z.record(z.number()),\n }),\n data: z.array(z.any()).optional(),\n }),\n annotations: { readOnlyHint: true, idempotentHint: true, openWorldHint: true },\n async handler(input) {\n const page = await wpRequest<{ id: number; title: { rendered: string }; meta?: Record<string, unknown> }>(\n `/wp/v2/pages/${input.page_id}?context=edit`,\n { siteId: input.site_id },\n );\n const raw = (page.meta?._elementor_data as string) ?? \"[]\";\n const data = parseElementorData(raw);\n const summary = summarize(data);\n return {\n page_id: page.id,\n title: page.title.rendered,\n summary,\n data: input.verbose ? data : undefined,\n };\n },\n});\n\nexport const findReplaceTool = defineTool({\n name: \"elementor_find_replace\",\n description: \"Find/replace plain text in every widget's settings on a single page. TWO-CALL DESTRUCTIVE FLOW: first call without `confirmation` performs a dry-run and returns a confirmation token + match count. Second call with the token actually applies the change after backing up the page's elementor data.\",\n inputSchema: z.object({\n site_id: z.string().optional(),\n page_id: z.number().int().positive(),\n find: z.string().min(1),\n replace: z.string(),\n widget_type: z.string().optional().describe(\"Restrict to one widget type, e.g. 'heading'.\"),\n case_sensitive: z.boolean().default(false),\n confirmation: z.string().optional().describe(\"Token returned from the dry-run call.\"),\n }),\n outputSchema: z.object({\n mode: z.enum([\"dry_run\", \"applied\"]),\n page_id: z.number(),\n match_count: z.number(),\n confirmation_token: z.string().optional(),\n expires_in_seconds: z.number().optional(),\n backup_meta_key: z.string().optional(),\n css_flush: z.string().optional(),\n }),\n annotations: { destructiveHint: true, idempotentHint: false, openWorldHint: true },\n async handler(input) {\n const cfg = loadConfig();\n // Always do the dry-run first to get the count\n const page = await wpRequest<{ id: number; title: { rendered: string }; meta?: Record<string, unknown> }>(\n `/wp/v2/pages/${input.page_id}?context=edit`,\n { siteId: input.site_id },\n );\n const raw = (page.meta?._elementor_data as string) ?? \"[]\";\n const data = parseElementorData(raw);\n const dry = findReplaceInWidgets(JSON.parse(JSON.stringify(data)), input.find, input.replace, {\n widgetType: input.widget_type,\n caseSensitive: input.case_sensitive,\n });\n\n if (!input.confirmation) {\n if (dry.replacementCount === 0) {\n return { mode: \"dry_run\" as const, page_id: input.page_id, match_count: 0 };\n }\n const token = issueConfirmation(\n \"elementor_find_replace\",\n { page_id: input.page_id, find: input.find, replace: input.replace },\n cfg.confirmation_ttl_seconds,\n );\n return {\n mode: \"dry_run\" as const,\n page_id: input.page_id,\n match_count: dry.replacementCount,\n confirmation_token: token,\n expires_in_seconds: cfg.confirmation_ttl_seconds,\n };\n }\n\n // Actually apply\n const conf = consumeConfirmation(input.confirmation, \"elementor_find_replace\");\n if (!conf) throw new Error(\"Invalid or expired confirmation token\");\n const original = conf.payload as { page_id: number; find: string; replace: string };\n if (original.page_id !== input.page_id || original.find !== input.find || original.replace !== input.replace) {\n throw new Error(\"Confirmation parameters don't match the original dry-run\");\n }\n\n // Backup first\n const backup = await backupElementorData(input.site_id, input.page_id);\n // Apply on a fresh copy\n const applied = findReplaceInWidgets(parseElementorData(raw), input.find, input.replace, {\n widgetType: input.widget_type,\n caseSensitive: input.case_sensitive,\n });\n await wpRequest(`/wp/v2/pages/${input.page_id}`, {\n siteId: input.site_id,\n method: \"PUT\",\n body: { meta: { _elementor_data: serializeElementorData(applied.data) } },\n });\n const flush = await flushElementorCSS(input.site_id, input.page_id);\n return {\n mode: \"applied\" as const,\n page_id: input.page_id,\n match_count: applied.replacementCount,\n backup_meta_key: backup.meta_key,\n css_flush: flush.method,\n };\n },\n});\n","/**\n * Elementor stores its page builder data as a JSON string in postmeta\n * (`_elementor_data`). Each top-level entry is a \"section\". Sections contain\n * columns, which contain widgets. Widgets have a `widgetType` (\"heading\",\n * \"text-editor\", \"image\", etc.) and a `settings` object.\n *\n * This module provides a typed wrapper + safe deep traversal helpers.\n */\n\nexport interface ElementorElement {\n id: string;\n elType: \"section\" | \"column\" | \"widget\" | \"container\";\n widgetType?: string;\n settings: Record<string, unknown>;\n elements?: ElementorElement[];\n isInner?: boolean;\n}\n\nexport type ElementorData = ElementorElement[];\n\nexport function parseElementorData(raw: string | ElementorData): ElementorData {\n if (Array.isArray(raw)) return raw;\n if (!raw || raw === \"[]\") return [];\n try {\n const decoded = JSON.parse(raw);\n if (!Array.isArray(decoded)) throw new Error(\"not an array\");\n return decoded as ElementorData;\n } catch (e) {\n throw new Error(`Failed to parse _elementor_data JSON: ${(e as Error).message}`);\n }\n}\n\nexport function serializeElementorData(data: ElementorData): string {\n return JSON.stringify(data);\n}\n\n/**\n * Walks every element top-down, yielding {element, path, depth}.\n * Path is the array of ids from root to current element.\n */\nexport function* walkElements(\n data: ElementorData,\n path: string[] = [],\n depth = 0,\n): Generator<{ element: ElementorElement; path: string[]; depth: number }> {\n for (const el of data) {\n const here = [...path, el.id];\n yield { element: el, path: here, depth };\n if (el.elements && el.elements.length > 0) {\n yield* walkElements(el.elements, here, depth + 1);\n }\n }\n}\n\nexport function findElementById(data: ElementorData, id: string): ElementorElement | null {\n for (const { element } of walkElements(data)) {\n if (element.id === id) return element;\n }\n return null;\n}\n\nexport function findWidgets(\n data: ElementorData,\n widgetType?: string,\n): { widget: ElementorElement; path: string[] }[] {\n const out: { widget: ElementorElement; path: string[] }[] = [];\n for (const { element, path } of walkElements(data)) {\n if (element.elType === \"widget\") {\n if (!widgetType || element.widgetType === widgetType) {\n out.push({ widget: element, path });\n }\n }\n }\n return out;\n}\n\n/**\n * Find/replace plain-text occurrences in every widget's settings.\n * Returns the modified data + how many replacements happened.\n *\n * Replacement is limited to string fields. Nested objects are recursed into.\n */\nexport function findReplaceInWidgets(\n data: ElementorData,\n find: string,\n replace: string,\n options: { widgetType?: string; caseSensitive?: boolean } = {},\n): { data: ElementorData; replacementCount: number } {\n let count = 0;\n const flags = options.caseSensitive ? \"g\" : \"gi\";\n const pattern = new RegExp(find.replace(/[.*+?^${}()|[\\]\\\\]/g, \"\\\\$&\"), flags);\n\n function replaceInValue(value: unknown): unknown {\n if (typeof value === \"string\") {\n const next = value.replace(pattern, () => {\n count++;\n return replace;\n });\n return next;\n }\n if (Array.isArray(value)) return value.map(replaceInValue);\n if (value && typeof value === \"object\") {\n const out: Record<string, unknown> = {};\n for (const [k, v] of Object.entries(value)) out[k] = replaceInValue(v);\n return out;\n }\n return value;\n }\n\n for (const { element } of walkElements(data)) {\n if (element.elType !== \"widget\") continue;\n if (options.widgetType && element.widgetType !== options.widgetType) continue;\n element.settings = replaceInValue(element.settings) as Record<string, unknown>;\n }\n\n return { data, replacementCount: count };\n}\n\n/**\n * Summary of an Elementor page — counts by widgetType and section depth.\n */\nexport function summarize(data: ElementorData): {\n totalElements: number;\n sections: number;\n containers: number;\n columns: number;\n widgets: number;\n byWidgetType: Record<string, number>;\n maxDepth: number;\n} {\n let totalElements = 0;\n let sections = 0;\n let containers = 0;\n let columns = 0;\n let widgets = 0;\n let maxDepth = 0;\n const byWidgetType: Record<string, number> = {};\n for (const { element, depth } of walkElements(data)) {\n totalElements++;\n maxDepth = Math.max(maxDepth, depth);\n if (element.elType === \"section\") sections++;\n else if (element.elType === \"container\") containers++;\n else if (element.elType === \"column\") columns++;\n else if (element.elType === \"widget\") {\n widgets++;\n const w = element.widgetType ?? \"unknown\";\n byWidgetType[w] = (byWidgetType[w] ?? 0) + 1;\n }\n }\n return { totalElements, sections, containers, columns, widgets, byWidgetType, maxDepth };\n}\n","import { wpRequest } from \"../api/wp-rest.js\";\n\n/**\n * Backup a page's _elementor_data to a custom postmeta key with a timestamp.\n * Returns the meta_key created.\n */\nexport async function backupElementorData(siteId: string | undefined, postId: number): Promise<{ meta_key: string; size_bytes: number }> {\n const current = await wpRequest<{ meta: Record<string, unknown> }>(`/wp/v2/pages/${postId}?context=edit&_fields=meta`, { siteId });\n const raw = (current.meta?._elementor_data as string) ?? \"[]\";\n const ts = new Date().toISOString().replace(/[:.]/g, \"-\");\n const meta_key = `_elementor_data_backup_${ts}`;\n await wpRequest(`/wp/v2/pages/${postId}`, {\n siteId,\n method: \"PUT\",\n body: { meta: { [meta_key]: raw } },\n });\n return { meta_key, size_bytes: raw.length };\n}\n\n/**\n * Trigger Elementor's CSS regeneration. Two strategies:\n * 1) Hit the elementor REST endpoint /elementor/v1/css if available (newer)\n * 2) Fallback: re-save the page (which causes Elementor to regen on next view)\n */\nexport async function flushElementorCSS(\n siteId: string | undefined,\n postId: number,\n): Promise<{ method: \"rest\" | \"resave\" }> {\n try {\n await wpRequest(`/elementor/v1/css?id=${postId}&action=regenerate`, { siteId, method: \"POST\" });\n return { method: \"rest\" };\n } catch {\n await wpRequest(`/wp/v2/pages/${postId}`, {\n siteId,\n method: \"PUT\",\n body: { date: new Date().toISOString() },\n });\n return { method: \"resave\" };\n }\n}\n","import { randomBytes } from \"node:crypto\";\n\ninterface PendingConfirmation {\n token: string;\n intent: string;\n payload: unknown;\n expiresAt: number;\n}\n\nconst pending = new Map<string, PendingConfirmation>();\n\nexport function issueConfirmation(intent: string, payload: unknown, ttlSeconds: number): string {\n const token = randomBytes(8).toString(\"hex\");\n pending.set(token, {\n token,\n intent,\n payload,\n expiresAt: Date.now() + ttlSeconds * 1000,\n });\n return token;\n}\n\nexport function consumeConfirmation(\n token: string,\n expectedIntent: string,\n): PendingConfirmation | null {\n const c = pending.get(token);\n if (!c) return null;\n pending.delete(token);\n if (c.expiresAt < Date.now()) return null;\n if (c.intent !== expectedIntent) return null;\n return c;\n}\n\nexport function _clearAllConfirmations(): void {\n pending.clear();\n}\n","import { z } from \"zod\";\nimport { defineTool } from \"../types/tool.js\";\nimport { wpRequest } from \"../api/wp-rest.js\";\nimport { parseElementorData, summarize, serializeElementorData } from \"../elementor/data-parser.js\";\n\nexport const listTemplatesTool = defineTool({\n name: \"list_elementor_templates\",\n description: \"List Elementor library templates on a site (saved sections, pages, popups). Type can be filtered: 'section', 'page', 'popup', 'header', 'footer'.\",\n inputSchema: z.object({\n site_id: z.string().optional(),\n type: z.enum([\"section\", \"page\", \"popup\", \"header\", \"footer\", \"any\"]).default(\"any\"),\n per_page: z.number().int().min(1).max(100).default(50),\n }),\n outputSchema: z.object({\n total: z.number(),\n templates: z.array(\n z.object({\n id: z.number(),\n title: z.string(),\n type: z.string(),\n modified: z.string(),\n }),\n ),\n }),\n annotations: { readOnlyHint: true, idempotentHint: true, openWorldHint: true },\n async handler(input) {\n const query: Record<string, string | number> = {\n per_page: input.per_page,\n context: \"edit\",\n _fields: \"id,title,modified,meta\",\n };\n if (input.type !== \"any\") {\n query.meta_key = \"_elementor_template_type\";\n query.meta_value = input.type;\n }\n interface RawTpl {\n id: number;\n title: { rendered: string };\n modified: string;\n meta?: { _elementor_template_type?: string };\n }\n const items = await wpRequest<RawTpl[]>(\"/wp/v2/elementor_library\", {\n siteId: input.site_id,\n query,\n });\n return {\n total: items.length,\n templates: items.map((t) => ({\n id: t.id,\n title: t.title.rendered,\n type: t.meta?._elementor_template_type ?? \"unknown\",\n modified: t.modified,\n })),\n };\n },\n});\n\nexport const exportTemplateTool = defineTool({\n name: \"export_elementor_template\",\n description: \"Export an Elementor template as a portable JSON object. Output is the same structure Elementor expects on import. Use it to copy sections between sites.\",\n inputSchema: z.object({\n site_id: z.string().optional(),\n template_id: z.number().int().positive(),\n }),\n outputSchema: z.object({\n template_id: z.number(),\n title: z.string(),\n type: z.string(),\n summary: z.object({\n totalElements: z.number(),\n widgets: z.number(),\n sections: z.number(),\n }),\n portable_json: z.string().describe(\"JSON-stringified payload ready to import via import_elementor_template.\"),\n }),\n annotations: { readOnlyHint: true, idempotentHint: true, openWorldHint: true },\n async handler(input) {\n const tpl = await wpRequest<{\n id: number;\n title: { rendered: string };\n meta?: { _elementor_template_type?: string; _elementor_data?: string };\n }>(`/wp/v2/elementor_library/${input.template_id}?context=edit`, {\n siteId: input.site_id,\n });\n const data = parseElementorData(tpl.meta?._elementor_data ?? \"[]\");\n const sum = summarize(data);\n const portable = {\n version: \"0.4\",\n title: tpl.title.rendered,\n type: tpl.meta?._elementor_template_type ?? \"page\",\n content: data,\n };\n return {\n template_id: tpl.id,\n title: tpl.title.rendered,\n type: tpl.meta?._elementor_template_type ?? \"unknown\",\n summary: { totalElements: sum.totalElements, widgets: sum.widgets, sections: sum.sections },\n portable_json: JSON.stringify(portable),\n };\n },\n});\n\nexport const importTemplateTool = defineTool({\n name: \"import_elementor_template\",\n description: \"Import an Elementor template (output of export_elementor_template) into a target site as a new template entry. Useful for syncing reusable sections across an agency's site fleet.\",\n inputSchema: z.object({\n site_id: z.string().optional().describe(\"Target site id.\"),\n portable_json: z.string().describe(\"JSON-stringified payload from export_elementor_template.\"),\n override_title: z.string().optional(),\n }),\n outputSchema: z.object({\n new_template_id: z.number(),\n title: z.string(),\n type: z.string(),\n url: z.string(),\n }),\n annotations: { destructiveHint: false, idempotentHint: false, openWorldHint: true },\n async handler(input) {\n let payload: { title: string; type: string; content: unknown };\n try {\n payload = JSON.parse(input.portable_json);\n } catch (e) {\n throw new Error(\"portable_json is not valid JSON: \" + (e as Error).message);\n }\n const title = input.override_title ?? payload.title;\n const data = Array.isArray(payload.content) ? payload.content : parseElementorData(payload.content as string);\n const res = await wpRequest<{ id: number; link: string }>(`/wp/v2/elementor_library`, {\n siteId: input.site_id,\n method: \"POST\",\n body: {\n title,\n status: \"publish\",\n meta: {\n _elementor_template_type: payload.type,\n _elementor_data: serializeElementorData(data),\n _elementor_edit_mode: \"builder\",\n },\n },\n });\n return {\n new_template_id: res.id,\n title,\n type: payload.type,\n url: res.link,\n };\n },\n});\n","import { z } from \"zod\";\nimport { defineTool } from \"../types/tool.js\";\nimport { wpRequest } from \"../api/wp-rest.js\";\nimport { loadConfig } from \"../config.js\";\n\ninterface PluginInfo {\n plugin: string;\n version: string;\n status: string;\n name?: string;\n plugin_uri?: string;\n}\n\nasync function fetchLatestElementor(): Promise<{ free: string; pro?: string }> {\n // Elementor (free) is on the WordPress.org plugin API\n const free = await fetch(\"https://api.wordpress.org/plugins/info/1.0/elementor.json\").then((r) => r.json()).catch(() => null);\n // Elementor Pro is commercial — we can scrape the changelog but it requires HTML parsing.\n // For now we return only the free version (the most common upgrade signal).\n return { free: (free?.version as string) ?? \"unknown\" };\n}\n\nexport const checkElementorVersionsTool = defineTool({\n name: \"check_elementor_versions\",\n description: \"For every configured site, fetch the installed Elementor / Elementor Pro version and compare against the latest available on wordpress.org. Returns a per-site row with 'outdated' flag.\",\n inputSchema: z.object({\n site_ids: z.array(z.string()).optional().describe(\"Subset of sites to check. Defaults to all.\"),\n }),\n outputSchema: z.object({\n checked: z.number(),\n latest_elementor_free: z.string(),\n sites: z.array(\n z.object({\n site_id: z.string(),\n url: z.string(),\n elementor_version: z.string().optional(),\n elementor_pro_version: z.string().optional(),\n outdated_free: z.boolean(),\n error: z.string().optional(),\n }),\n ),\n }),\n annotations: { readOnlyHint: true, idempotentHint: true, openWorldHint: true },\n async handler(input) {\n const cfg = loadConfig();\n const latest = await fetchLatestElementor();\n const targets = input.site_ids\n ? cfg.sites.filter((s) => input.site_ids?.includes(s.id))\n : cfg.sites;\n const rows = [];\n for (const site of targets) {\n try {\n const plugins = await wpRequest<PluginInfo[]>(\"/wp/v2/plugins\", { siteId: site.id });\n let elementor_version: string | undefined;\n let elementor_pro_version: string | undefined;\n for (const p of plugins) {\n if (p.plugin.startsWith(\"elementor/\") && p.plugin.endsWith(\"/elementor.php\"))\n elementor_version = p.version;\n if (p.plugin.startsWith(\"elementor-pro/\")) elementor_pro_version = p.version;\n }\n rows.push({\n site_id: site.id,\n url: site.url,\n elementor_version,\n elementor_pro_version,\n outdated_free: !!elementor_version && elementor_version !== latest.free && latest.free !== \"unknown\",\n });\n } catch (e) {\n rows.push({\n site_id: site.id,\n url: site.url,\n outdated_free: false,\n error: (e as Error).message,\n });\n }\n }\n return {\n checked: rows.length,\n latest_elementor_free: latest.free,\n sites: rows,\n };\n },\n});\n","import type { ToolDefinition } from \"../types/tool.js\";\nimport { listSitesTool, pingSiteTool } from \"./sites.js\";\nimport { listElementorPagesTool, readPageElementorTool, findReplaceTool } from \"./pages.js\";\nimport { listTemplatesTool, exportTemplateTool, importTemplateTool } from \"./templates.js\";\nimport { checkElementorVersionsTool } from \"./updates.js\";\n\n// We collect concrete typed tools into a heterogeneous array.\n// Variance widens the input/output to unknown at the array level — that's fine\n// because each tool re-validates its input via Zod at call time.\nexport const tools: ToolDefinition[] = [\n listSitesTool,\n pingSiteTool,\n listElementorPagesTool,\n readPageElementorTool,\n findReplaceTool,\n listTemplatesTool,\n exportTemplateTool,\n importTemplateTool,\n checkElementorVersionsTool,\n] as unknown as ToolDefinition[];\n","import { readFileSync, existsSync, readdirSync } from \"node:fs\";\nimport { dirname, resolve, join, basename } from \"node:path\";\nimport { fileURLToPath } from \"node:url\";\n\nconst __dirname = dirname(fileURLToPath(import.meta.url));\nconst DOCS_DIR = resolve(__dirname, \"../resources/elementor-docs\");\n\nexport interface Resource {\n uri: string;\n name: string;\n description?: string;\n mimeType: string;\n}\n\nexport async function listResources(): Promise<Resource[]> {\n if (!existsSync(DOCS_DIR)) return [];\n const files = readdirSync(DOCS_DIR).filter((f) => f.endsWith(\".md\"));\n return files.map((f) => ({\n uri: `elementor-docs://${f}`,\n name: basename(f, \".md\"),\n description: `Elementor documentation snippet (scraped from developer.elementor.com)`,\n mimeType: \"text/markdown\",\n }));\n}\n\nexport async function readResource(uri: string): Promise<{ contents: { uri: string; mimeType: string; text: string }[] }> {\n if (!uri.startsWith(\"elementor-docs://\")) {\n throw new Error(`Unknown resource URI: ${uri}`);\n }\n const filename = uri.replace(\"elementor-docs://\", \"\");\n const path = join(DOCS_DIR, filename);\n if (!existsSync(path)) throw new Error(`Resource not found: ${filename}`);\n return {\n contents: [\n {\n uri,\n mimeType: \"text/markdown\",\n text: readFileSync(path, \"utf8\"),\n },\n ],\n };\n}\n"],"mappings":"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","names":["readFileSync","dirname","resolve","fileURLToPath","s","z","z","z","z","z","z","z","z","readFileSync","__dirname","dirname","fileURLToPath","readFileSync","resolve"]}
package/package.json ADDED
@@ -0,0 +1,62 @@
1
+ {
2
+ "name": "elementor-mcp-agent",
3
+ "version": "0.1.0",
4
+ "mcpName": "io.github.Mogacode-ma/elementor-mcp-agent",
5
+ "description": "Agentic MCP server for WordPress Elementor — multi-site management, safe Elementor data editing, template import/export, CSS flush, version tracking. Built for agencies running many client sites.",
6
+ "type": "module",
7
+ "main": "./dist/server.js",
8
+ "bin": {
9
+ "elementor-mcp-agent": "dist/server.js"
10
+ },
11
+ "files": [
12
+ "dist",
13
+ "README.md",
14
+ "LICENSE",
15
+ "ARCHITECTURE.md",
16
+ "resources"
17
+ ],
18
+ "scripts": {
19
+ "build": "tsup",
20
+ "dev": "tsx watch src/server.ts",
21
+ "start": "node dist/server.js",
22
+ "lint": "eslint . --max-warnings 0",
23
+ "lint:fix": "eslint . --fix",
24
+ "format": "prettier --write .",
25
+ "format:check": "prettier --check .",
26
+ "typecheck": "tsc --noEmit",
27
+ "test": "vitest run",
28
+ "test:watch": "vitest",
29
+ "test:coverage": "vitest run --coverage",
30
+ "inspector": "npx @modelcontextprotocol/inspector node dist/server.js",
31
+ "docs:fetch": "tsx scripts/fetch-elementor-docs.ts",
32
+ "prepublishOnly": "npm run lint && npm run typecheck && npm run test && npm run build"
33
+ },
34
+ "keywords": ["mcp", "model-context-protocol", "elementor", "wordpress", "claude", "anthropic", "agency", "multi-site"],
35
+ "author": "MogaCode <hello@mogacode.ma>",
36
+ "license": "MIT",
37
+ "repository": {
38
+ "type": "git",
39
+ "url": "https://github.com/Mogacode-ma/elementor-mcp-agent.git"
40
+ },
41
+ "homepage": "https://github.com/Mogacode-ma/elementor-mcp-agent",
42
+ "engines": { "node": ">=18.0.0" },
43
+ "dependencies": {
44
+ "@modelcontextprotocol/sdk": "^1.0.0",
45
+ "pino": "^9.0.0",
46
+ "zod": "^3.23.0",
47
+ "zod-to-json-schema": "^3.23.0"
48
+ },
49
+ "devDependencies": {
50
+ "@types/node": "^20.11.0",
51
+ "@typescript-eslint/eslint-plugin": "^7.0.0",
52
+ "@typescript-eslint/parser": "^7.0.0",
53
+ "@vitest/coverage-v8": "^2.0.0",
54
+ "eslint": "^8.57.0",
55
+ "eslint-config-prettier": "^9.1.0",
56
+ "prettier": "^3.2.0",
57
+ "tsup": "^8.0.0",
58
+ "tsx": "^4.7.0",
59
+ "typescript": "^5.4.0",
60
+ "vitest": "^2.0.0"
61
+ }
62
+ }
@@ -0,0 +1,13 @@
1
+ # Elementor docs corpus (MCP Resources)
2
+
3
+ This folder hosts Markdown extracts of the public Elementor developer documentation
4
+ (<https://developer.elementor.com/docs/>). Each file is exposed via the MCP server
5
+ as a `resources/list` + `resources/read` entry, so an LLM client can look up
6
+ Elementor hooks, filters, widget structure, and editor patterns without leaving
7
+ its context.
8
+
9
+ **Refresh**: `npm run docs:fetch` triggers `scripts/fetch-elementor-docs.ts` which
10
+ scrapes a curated set of pages, converts them to Markdown, and writes them here.
11
+
12
+ We intentionally ship a small curated set rather than the full docs — quality over
13
+ quantity for token efficiency.
@@ -0,0 +1,31 @@
1
+ # Elementor — Common action hooks
2
+
3
+ This is a hand-curated reference of the most common action hooks exposed by
4
+ Elementor and Elementor Pro. Use these when extending Elementor from a custom
5
+ plugin or theme.
6
+
7
+ ## Editor & frontend lifecycle
8
+
9
+ - `elementor/init` — fires once Elementor's main object is instantiated. Use it to register custom widgets, controls, or skins.
10
+ - `elementor/elements/categories_registered` — register a custom widget category before widgets register themselves.
11
+ - `elementor/widgets/widgets_registered` — register custom widgets.
12
+ - `elementor/editor/before_enqueue_scripts` — enqueue assets that should only run in the editor.
13
+ - `elementor/preview/enqueue_styles` — enqueue styles that should only run inside the editor preview iframe.
14
+ - `elementor/frontend/before_enqueue_scripts` — enqueue scripts on the live frontend (not the editor).
15
+
16
+ ## Rendering
17
+
18
+ - `elementor/frontend/widget/before_render` / `after_render` — wrap any widget's output.
19
+ - `elementor/frontend/section/before_render` / `after_render` — same for sections.
20
+ - `elementor/frontend/the_content` — alters the HTML output of Elementor pages.
21
+
22
+ ## CSS regeneration
23
+
24
+ - `elementor/core/files/clear_cache` — clear the Elementor CSS cache from PHP.
25
+ - `elementor_pro/core/files/clear_cache` — same for Pro assets.
26
+
27
+ ## Common filters
28
+
29
+ - `elementor/widget/print_template` — modify the JS template a widget uses in the editor.
30
+ - `elementor/element/get_default_args` — change default control values for any widget.
31
+ - `elementor_pro/forms/render/item` — alter form fields before render.
@@ -0,0 +1,36 @@
1
+ # Safe editing patterns for Elementor
2
+
3
+ Elementor's data model is fragile. A malformed JSON in `_elementor_data` makes the
4
+ page unrenderable in the editor and often on the frontend too. This document
5
+ captures the patterns this MCP enforces.
6
+
7
+ ## The 3-step safe edit
8
+
9
+ 1. **Read** the page via the WP REST API with `?context=edit` (required to get postmeta).
10
+ 2. **Backup** the current `_elementor_data` into a timestamped meta key (`_elementor_data_backup_2026-05-21T17-00-00`). The MCP keeps backups indefinitely — purge them yourself when no longer needed.
11
+ 3. **Edit a deep clone** of the parsed data — never mutate the original.
12
+ 4. **Validate** the result by re-serializing and re-parsing (catches accidental cycles / undefined values).
13
+ 5. **PUT** the new JSON back via REST.
14
+ 6. **Flush CSS** via `/elementor/v1/css?id={id}&action=regenerate` or by re-saving.
15
+
16
+ ## Confirmation tokens for destructive ops
17
+
18
+ Operations that modify content (find/replace, widget swap, mass-delete) use a
19
+ two-call confirmation:
20
+
21
+ 1. First call → dry-run, returns `{match_count, confirmation_token, expires_in_seconds: 60}`.
22
+ 2. Second call with the same parameters + the `confirmation` token → actually applies.
23
+
24
+ The token is single-use and expires in 60s by default (configurable via
25
+ `ELEMENTOR_MCP_CONFIRMATION_TTL`).
26
+
27
+ ## Restoring from a backup
28
+
29
+ To restore, you need to set the current `_elementor_data` to the value of one of
30
+ the backup meta keys. The MCP does not yet expose a `restore_elementor_backup`
31
+ tool — coming in v0.2. In the meantime, you can do it manually via WP-CLI:
32
+
33
+ ```bash
34
+ wp post meta update <post_id> _elementor_data "$(wp post meta get <post_id> _elementor_data_backup_2026-05-21T17-00-00)"
35
+ wp elementor flush-css
36
+ ```
@@ -0,0 +1,64 @@
1
+ # Elementor — Widget structure in `_elementor_data`
2
+
3
+ Every Elementor page stores its layout in the postmeta key `_elementor_data` as a
4
+ JSON-encoded array. Top-level entries are **sections** or **containers** (the new
5
+ flexbox-based container introduced in Elementor 3.6).
6
+
7
+ ## Element types
8
+
9
+ ```
10
+ {
11
+ "id": "abc12345", // 8-char hex id, unique within the page
12
+ "elType": "section" | "container" | "column" | "widget",
13
+ "settings": { ... }, // per-element controls (responsive variants suffixed _tablet / _mobile)
14
+ "elements": [ ... ], // nested elements (sections → columns → widgets)
15
+ "isInner": false, // true if nested section
16
+ "widgetType": "heading" // only present when elType === "widget"
17
+ }
18
+ ```
19
+
20
+ ## Common widget types
21
+
22
+ - `heading` — text heading
23
+ - `text-editor` — TinyMCE-style rich text
24
+ - `image` — image with caption + lightbox
25
+ - `button` — single CTA button
26
+ - `icon` — single icon
27
+ - `icon-box` — icon + heading + description
28
+ - `icon-list` — list with icons
29
+ - `divider`
30
+ - `spacer`
31
+ - `tabs` / `accordion` / `toggle`
32
+ - `image-gallery` / `image-carousel`
33
+ - `template` — embedded saved template
34
+ - `theme-post-title` / `theme-post-content` — theme-builder dynamic widgets
35
+
36
+ ## Pro widgets
37
+
38
+ - `form` — Elementor Pro forms
39
+ - `posts` — dynamic posts grid
40
+ - `portfolio`
41
+ - `slides`
42
+ - `popup` (in popup templates)
43
+ - `nav-menu`
44
+ - `flip-box`
45
+ - `price-list` / `price-table`
46
+
47
+ ## Settings — key fields
48
+
49
+ Common to most widgets:
50
+
51
+ - `_animation` — entrance animation
52
+ - `_css_classes` — extra CSS classes
53
+ - `_padding` / `_margin` — spacing (object: `top, right, bottom, left, unit, isLinked`)
54
+ - `_background_background` — "classic", "gradient", "video"
55
+ - `_background_color` / `_background_image`
56
+
57
+ Responsive variants are suffixed: `_padding_tablet`, `_padding_mobile`.
58
+
59
+ ## Editing rules of thumb
60
+
61
+ 1. **Always preserve `id`** — Elementor uses ids for CSS scoping. Changing them invalidates the generated CSS.
62
+ 2. **Never edit `_elementor_data` without backing it up first** — corrupted JSON breaks the page.
63
+ 3. **Flush CSS after edit** — Elementor caches per-page CSS at `wp-content/uploads/elementor/css/`. Regenerate via REST or by re-saving the page.
64
+ 4. **Use `elementor_safe_edit` from this MCP** — it does all three (backup → edit → flush) atomically.