@yashwant.dharmdas/elementor-mcp 3.6.0 → 3.7.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.
Files changed (2) hide show
  1. package/dist/index.js +41 -32
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -195,36 +195,33 @@ function createMcpServer(sites) {
195
195
  return { content: [{ type: "text", text: `Error fetching page data: ${error.response?.data?.message || error.message}` }], isError: true };
196
196
  }
197
197
  });
198
- server.tool("find-elements", "Search the Elementor element tree of a page and return matching elements. Filter by element type, widget type, or text content.", {
198
+ server.tool("find-elements", "Search the Elementor element tree of a page SERVER-SIDE and return ONLY matching elements — never downloads the full JSON. Use this instead of get-data whenever you need to locate specific widgets. Filters are combined with AND. Example uses: find all buttons (widget_type='button'), find buttons with no link (widget_type='button' + has_empty='link.url'), find headings containing a phrase (widget_type='heading' + contains='Welcome').", {
199
199
  page_id: z.string().describe("WordPress Page ID"),
200
- el_type: z.string().optional().describe("Filter by elType: 'container' or 'widget'"),
201
- widget_type: z.string().optional().describe("Filter by widgetType, e.g. 'heading', 'image', 'button'"),
202
- contains: z.string().optional().describe("Filter by text content found anywhere in the element's settings"),
203
- include_path: z.boolean().optional().describe("Include ancestor IDs in results"),
200
+ el_type: z.string().optional().describe("Filter by elType: 'widget' or 'container'"),
201
+ widget_type: z.string().optional().describe("Filter by widgetType e.g. 'button', 'heading', 'image', 'text-editor'"),
202
+ contains: z.string().optional().describe("Substring that must appear anywhere in the element's settings JSON"),
203
+ has_empty: z.string().optional().describe("Dot-notation setting path that must be empty/missing — e.g. 'link.url' finds buttons with no URL"),
204
+ return_keys: z.array(z.string()).optional().describe("Limit which settings keys are returned — e.g. ['text','link'] to keep response small"),
205
+ include_path: z.boolean().optional().describe("Include ancestor element IDs in each result"),
204
206
  site: siteParam,
205
- }, async ({ page_id, el_type, widget_type, contains, include_path, site }) => {
207
+ }, async ({ page_id, el_type, widget_type, contains, has_empty, return_keys, include_path, site }) => {
206
208
  try {
207
209
  const { wpUrl, authHeader } = resolveSite(sites, site);
208
- const r = await axios.get(`${wpUrl}/wp-json/erc/v1/pages/${page_id}/data`, { headers: { Authorization: authHeader } });
209
- const results = [];
210
- function traverse(els, pathArr) {
211
- for (const el of els) {
212
- const currentPath = [...pathArr, el.id];
213
- let match = true;
214
- if (el_type && el.elType !== el_type)
215
- match = false;
216
- if (widget_type && el.widgetType !== widget_type)
217
- match = false;
218
- if (contains && !JSON.stringify(el.settings || {}).includes(contains))
219
- match = false;
220
- if (match)
221
- results.push(include_path ? { ...el, _path: currentPath } : el);
222
- if (el.elements?.length)
223
- traverse(el.elements, currentPath);
224
- }
225
- }
226
- traverse(r.data, []);
227
- return { content: [{ type: "text", text: JSON.stringify(results, null, 2) }] };
210
+ const body = {};
211
+ if (el_type)
212
+ body.el_type = el_type;
213
+ if (widget_type)
214
+ body.widget_type = widget_type;
215
+ if (contains)
216
+ body.contains = contains;
217
+ if (has_empty)
218
+ body.has_empty = has_empty;
219
+ if (return_keys)
220
+ body.return_keys = return_keys;
221
+ if (include_path)
222
+ body.include_path = include_path;
223
+ const r = await axios.post(`${wpUrl}/wp-json/erc/v1/pages/${page_id}/find`, body, { headers: { Authorization: authHeader, "Content-Type": "application/json" } });
224
+ return { content: [{ type: "text", text: JSON.stringify(r.data, null, 2) }] };
228
225
  }
229
226
  catch (error) {
230
227
  return { content: [{ type: "text", text: `Error: ${error.response?.data?.message || error.message}` }], isError: true };
@@ -1935,15 +1932,19 @@ function createMcpServer(sites) {
1935
1932
  }
1936
1933
  });
1937
1934
  // ── Group 27: Content Audit (text extraction & bulk fix application) ────────
1938
- server.tool("extract-pages-text", "Extract clean text content from one or many Elementor pages — returns ONLY the words, not the JSON or HTML noise. Designed for grammar/spelling/sentence audits across many pages at once. Uses a key-name heuristic so it works for ANY widget (built-in, Elementor Pro, WooCommerce, third-party, custom) without widget-specific code. Each block includes element_id + field_path so fixes can be written back precisely with apply-text-fixes.", {
1939
- page_ids: z.array(z.number()).optional().describe("Specific page IDs to extract from"),
1940
- scope: z.enum(["all_pages"]).optional().describe("Use 'all_pages' to extract from every published Elementor page"),
1941
- post_type: z.string().optional().describe("Limit to a specific post type (e.g. 'page', 'post', 'astra-portfolio')"),
1935
+ server.tool("extract-pages-text", "Extract clean text content from one or many Elementor pages — returns ONLY the words, not the JSON or HTML noise. Designed for grammar/spelling/sentence audits. Uses a key-name heuristic so it works for ANY widget (built-in, Elementor Pro, WooCommerce, third-party, custom). Each block includes id + path so fixes can be written back with apply-text-fixes. ALWAYS use compact=true, min_words=3, max_chars=600 for grammar/spelling audits. ALWAYS use page_ids with 5 pages max per call — never scope='all_pages' (response too large).", {
1936
+ page_ids: z.array(z.number()).optional().describe("Specific page IDs to extract — max 5 per call for grammar audits"),
1937
+ scope: z.enum(["all_pages"]).optional().describe("Use 'all_pages' only for non-audit use cases returns too much data for grammar checks"),
1938
+ post_type: z.string().optional().describe("Limit to a specific post type (e.g. 'page', 'post')"),
1939
+ compact: z.boolean().optional().describe("true = return only id/path/text per block, omit widget type and is_html. Always true for grammar audits."),
1940
+ min_words: z.number().optional().describe("Skip text blocks shorter than N words. Default 1. Use 3 for grammar audits to skip buttons and labels."),
1941
+ max_chars: z.number().optional().describe("Truncate each text block at N characters. Default 0 (no limit). Use 600 for grammar audits."),
1942
+ max_pages: z.number().optional().describe("Hard cap on pages processed per call. Safety net — use page_ids batching instead."),
1942
1943
  site: siteParam,
1943
- }, async ({ page_ids, scope, post_type, site }) => {
1944
+ }, async ({ page_ids, scope, post_type, compact, min_words, max_chars, max_pages, site }) => {
1944
1945
  try {
1945
1946
  if (!page_ids && !scope && !post_type) {
1946
- return { content: [{ type: "text", text: "Provide page_ids, scope='all_pages', or post_type." }], isError: true };
1947
+ return { content: [{ type: "text", text: "Provide page_ids (max 5 per call), scope='all_pages', or post_type." }], isError: true };
1947
1948
  }
1948
1949
  const { wpUrl, authHeader } = resolveSite(sites, site);
1949
1950
  const body = {};
@@ -1953,6 +1954,14 @@ function createMcpServer(sites) {
1953
1954
  body.scope = scope;
1954
1955
  if (post_type)
1955
1956
  body.post_type = post_type;
1957
+ if (compact !== undefined)
1958
+ body.compact = compact;
1959
+ if (min_words !== undefined)
1960
+ body.min_words = min_words;
1961
+ if (max_chars !== undefined)
1962
+ body.max_chars = max_chars;
1963
+ if (max_pages !== undefined)
1964
+ body.max_pages = max_pages;
1956
1965
  const r = await axios.post(`${wpUrl}/wp-json/erc/v1/site/extract-text`, body, { headers: { Authorization: authHeader, "Content-Type": "application/json" } });
1957
1966
  return { content: [{ type: "text", text: JSON.stringify(r.data, null, 2) }] };
1958
1967
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@yashwant.dharmdas/elementor-mcp",
3
- "version": "3.6.0",
3
+ "version": "3.7.0",
4
4
  "description": "MCP server for controlling Elementor via Claude — supports multiple WordPress sites",
5
5
  "main": "dist/index.js",
6
6
  "type": "module",