@yashwant.dharmdas/elementor-mcp 3.8.0 → 3.10.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.
- package/dist/index.js +93 -3
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -182,13 +182,20 @@ function createMcpServer(sites) {
|
|
|
182
182
|
return { content: [{ type: "text", text: `Error: ${error.message}` }], isError: true };
|
|
183
183
|
}
|
|
184
184
|
});
|
|
185
|
-
server.tool("get-data", "Get the
|
|
185
|
+
server.tool("get-data", "Get the Elementor JSON for a page. **ALWAYS pass compact=true and strip_responsive=true for any page that isn't tiny** — this strips style/typography/spacing/responsive bulk and keeps only structure + content (60-80% smaller). Without compact, large pages will exceed Claude's tool-response size limit and get auto-saved to a file (forcing slow Python parsing). For surgical lookups (find one widget by type or condition) use find-elements instead — it's even smaller and runs server-side.", {
|
|
186
186
|
page_id: z.string().describe("WordPress Page ID"),
|
|
187
|
+
compact: z.boolean().optional().describe("Strip style/typography/spacing/empty fields. Use true unless you specifically need style data."),
|
|
188
|
+
strip_responsive: z.boolean().optional().describe("Also drop _tablet, _mobile, _laptop, _widescreen variants. Use true with compact=true."),
|
|
187
189
|
site: siteParam,
|
|
188
|
-
}, async ({ page_id, site }) => {
|
|
190
|
+
}, async ({ page_id, compact, strip_responsive, site }) => {
|
|
189
191
|
try {
|
|
190
192
|
const { wpUrl, authHeader } = resolveSite(sites, site);
|
|
191
|
-
const
|
|
193
|
+
const params = {};
|
|
194
|
+
if (compact !== undefined)
|
|
195
|
+
params.compact = compact ? "1" : "0";
|
|
196
|
+
if (strip_responsive !== undefined)
|
|
197
|
+
params.strip_responsive = strip_responsive ? "1" : "0";
|
|
198
|
+
const r = await axios.get(`${wpUrl}/wp-json/erc/v1/pages/${page_id}/data`, { headers: { Authorization: authHeader }, params });
|
|
192
199
|
return { content: [{ type: "text", text: JSON.stringify(r.data, null, 2) }] };
|
|
193
200
|
}
|
|
194
201
|
catch (error) {
|
|
@@ -263,6 +270,89 @@ function createMcpServer(sites) {
|
|
|
263
270
|
return { content: [{ type: "text", text: `Error setting element link: ${error.response?.data?.message || error.message}` }], isError: true };
|
|
264
271
|
}
|
|
265
272
|
});
|
|
273
|
+
server.tool("clone-element", "Clone an existing Elementor element (button, heading, container, etc.) with optional setting overrides and insert it at a position. **PERFECT for 'add another button like this one' tasks** — preserves all styling, classes, sizing, and structure from the source widget; you just override the content (text, link, image, etc.). 100 % server-side: no full-page download or upload — uses ~95 % fewer tokens than get-data + update-data. ALWAYS prefer this over add-widget-to-page or update-data when adding similar elements.", {
|
|
274
|
+
page_id: z.string().describe("WordPress Page ID"),
|
|
275
|
+
element_id: z.string().describe("Element ID to clone (the source — its styling and structure are copied; new IDs are auto-generated for the clone)"),
|
|
276
|
+
position: z.enum(["before", "after", "first_child", "last_child"]).optional().describe("Where to place the clone relative to target_id. Default: 'after'"),
|
|
277
|
+
target_id: z.string().optional().describe("Place clone near this element instead of the source. Default: same as element_id (places clone next to source)"),
|
|
278
|
+
overrides: z.string().optional().describe("JSON object of settings to override on the clone. Example: '{\"text\":\"Go to Growth99\",\"link\":{\"url\":\"https://growth99.com\",\"is_external\":\"on\"}}'. Top-level keys deep-merge for objects, replace for scalars."),
|
|
279
|
+
site: siteParam,
|
|
280
|
+
}, async ({ page_id, element_id, position, target_id, overrides, site }) => {
|
|
281
|
+
try {
|
|
282
|
+
let parsedOverrides = {};
|
|
283
|
+
if (overrides) {
|
|
284
|
+
try {
|
|
285
|
+
parsedOverrides = JSON.parse(overrides);
|
|
286
|
+
}
|
|
287
|
+
catch {
|
|
288
|
+
return { content: [{ type: "text", text: "Invalid JSON in overrides parameter." }], isError: true };
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
const { wpUrl, authHeader } = resolveSite(sites, site);
|
|
292
|
+
const body = {};
|
|
293
|
+
if (position)
|
|
294
|
+
body.position = position;
|
|
295
|
+
if (target_id)
|
|
296
|
+
body.target_id = target_id;
|
|
297
|
+
if (overrides)
|
|
298
|
+
body.overrides = parsedOverrides;
|
|
299
|
+
const r = await axios.post(`${wpUrl}/wp-json/erc/v1/pages/${page_id}/elements/${element_id}/clone`, body, { headers: { Authorization: authHeader, "Content-Type": "application/json" } });
|
|
300
|
+
return { content: [{ type: "text", text: JSON.stringify(r.data, null, 2) }] };
|
|
301
|
+
}
|
|
302
|
+
catch (error) {
|
|
303
|
+
return { content: [{ type: "text", text: `Error cloning element: ${error.response?.data?.message || error.message}` }], isError: true };
|
|
304
|
+
}
|
|
305
|
+
});
|
|
306
|
+
server.tool("insert-element", "Insert a brand-new Elementor element (custom JSON) at a specific position. Server-side — never downloads or uploads the full page JSON. Use this when there's no similar element to clone. For 'add another button like this one' use clone-element instead — it preserves styling automatically.", {
|
|
307
|
+
page_id: z.string().describe("WordPress Page ID"),
|
|
308
|
+
element: z.string().describe("JSON string of the element to insert. Schema: {elType, widgetType, settings, elements?}. ID auto-generated."),
|
|
309
|
+
target_id: z.string().optional().describe("Element ID to insert relative to. Omit to insert at page top/bottom."),
|
|
310
|
+
position: z.string().optional().describe("With target_id: 'before', 'after', 'first_child', 'last_child' (default 'after'). Without target_id: 'top' or 'bottom' (default 'bottom')."),
|
|
311
|
+
site: siteParam,
|
|
312
|
+
}, async ({ page_id, element, target_id, position, site }) => {
|
|
313
|
+
try {
|
|
314
|
+
let parsedElement;
|
|
315
|
+
try {
|
|
316
|
+
parsedElement = JSON.parse(element);
|
|
317
|
+
}
|
|
318
|
+
catch {
|
|
319
|
+
return { content: [{ type: "text", text: "Invalid JSON in element parameter." }], isError: true };
|
|
320
|
+
}
|
|
321
|
+
if (!parsedElement || typeof parsedElement !== "object") {
|
|
322
|
+
return { content: [{ type: "text", text: "element must be a JSON object." }], isError: true };
|
|
323
|
+
}
|
|
324
|
+
const { wpUrl, authHeader } = resolveSite(sites, site);
|
|
325
|
+
const body = { element: parsedElement };
|
|
326
|
+
if (target_id)
|
|
327
|
+
body.target_id = target_id;
|
|
328
|
+
if (position)
|
|
329
|
+
body.position = position;
|
|
330
|
+
const r = await axios.post(`${wpUrl}/wp-json/erc/v1/pages/${page_id}/insert-element`, body, { headers: { Authorization: authHeader, "Content-Type": "application/json" } });
|
|
331
|
+
return { content: [{ type: "text", text: JSON.stringify(r.data, null, 2) }] };
|
|
332
|
+
}
|
|
333
|
+
catch (error) {
|
|
334
|
+
return { content: [{ type: "text", text: `Error inserting element: ${error.response?.data?.message || error.message}` }], isError: true };
|
|
335
|
+
}
|
|
336
|
+
});
|
|
337
|
+
server.tool("move-element", "Move an existing Elementor element to a new position in the tree. Server-side — no full-page round-trip.", {
|
|
338
|
+
page_id: z.string().describe("WordPress Page ID"),
|
|
339
|
+
element_id: z.string().describe("Element ID to move"),
|
|
340
|
+
target_id: z.string().describe("Element ID to move next to"),
|
|
341
|
+
position: z.enum(["before", "after", "first_child", "last_child"]).optional().describe("Position relative to target_id. Default: 'after'"),
|
|
342
|
+
site: siteParam,
|
|
343
|
+
}, async ({ page_id, element_id, target_id, position, site }) => {
|
|
344
|
+
try {
|
|
345
|
+
const { wpUrl, authHeader } = resolveSite(sites, site);
|
|
346
|
+
const body = { target_id };
|
|
347
|
+
if (position)
|
|
348
|
+
body.position = position;
|
|
349
|
+
const r = await axios.post(`${wpUrl}/wp-json/erc/v1/pages/${page_id}/elements/${element_id}/move`, body, { headers: { Authorization: authHeader, "Content-Type": "application/json" } });
|
|
350
|
+
return { content: [{ type: "text", text: JSON.stringify(r.data, null, 2) }] };
|
|
351
|
+
}
|
|
352
|
+
catch (error) {
|
|
353
|
+
return { content: [{ type: "text", text: `Error moving element: ${error.response?.data?.message || error.message}` }], isError: true };
|
|
354
|
+
}
|
|
355
|
+
});
|
|
266
356
|
server.tool("merge-element-settings", "Deep-merge settings into a specific Elementor element without replacing the full element. Use for small setting adjustments.", {
|
|
267
357
|
page_id: z.string().describe("WordPress Page ID"),
|
|
268
358
|
element_id: z.string().describe("Elementor Element ID"),
|
package/package.json
CHANGED