@yashwant.dharmdas/elementor-mcp 3.4.0 → 3.6.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 +119 -17
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -849,42 +849,70 @@ function createMcpServer(sites) {
|
|
|
849
849
|
return { content: [{ type: "text", text: `Error: ${e.response?.data?.message || e.message}` }], isError: true };
|
|
850
850
|
}
|
|
851
851
|
});
|
|
852
|
-
server.tool("create-custom-code", "Create a new custom code snippet (requires Elementor Pro).", {
|
|
853
|
-
title: z.string(),
|
|
854
|
-
code: z.string(),
|
|
855
|
-
location: z.string().optional().describe("head
|
|
856
|
-
status: z.string().optional(),
|
|
852
|
+
server.tool("create-custom-code", "Create a new custom code snippet (requires Elementor Pro). Supports the full Publish Settings — set condition='entire_site' so the snippet actually applies sitewide (otherwise it has no effect even when published).", {
|
|
853
|
+
title: z.string().describe("Snippet title (e.g. 'Favicon Code')"),
|
|
854
|
+
code: z.string().describe("Raw code to inject (HTML, JS, or CSS)"),
|
|
855
|
+
location: z.string().optional().describe("Where to inject: 'head', 'body_start', 'body_end' (default: head)"),
|
|
856
|
+
status: z.string().optional().describe("Post status: 'publish' (default) or 'draft'. Use 'publish' to make the snippet active immediately."),
|
|
857
|
+
priority: z.number().optional().describe("Load priority — lower number = earlier. Default: 10"),
|
|
858
|
+
condition: z.enum(["entire_site", "specific_page", "post_type"]).optional().describe("Where the snippet applies. 'entire_site' = sitewide (most common, required for favicons/global tags). Default: entire_site"),
|
|
859
|
+
page_id: z.string().optional().describe("Required when condition='specific_page'. Apply only to this page."),
|
|
860
|
+
post_type: z.string().optional().describe("Required when condition='post_type'. WordPress post type slug (e.g. 'page', 'post', 'astra-portfolio')."),
|
|
857
861
|
site: siteParam,
|
|
858
|
-
}, async ({ title, code, location, status, site }) => {
|
|
862
|
+
}, async ({ title, code, location, status, priority, condition, page_id, post_type, site }) => {
|
|
859
863
|
try {
|
|
860
864
|
const { wpUrl, authHeader } = resolveSite(sites, site);
|
|
861
|
-
const
|
|
865
|
+
const body = {
|
|
866
|
+
title,
|
|
867
|
+
code,
|
|
868
|
+
location: location || "head",
|
|
869
|
+
status: status || "publish",
|
|
870
|
+
priority: priority ?? 10,
|
|
871
|
+
condition: condition || "entire_site",
|
|
872
|
+
};
|
|
873
|
+
if (condition === "specific_page" && page_id)
|
|
874
|
+
body.page_id = page_id;
|
|
875
|
+
if (condition === "post_type" && post_type)
|
|
876
|
+
body.post_type = post_type;
|
|
877
|
+
const r = await axios.post(`${wpUrl}/wp-json/erc/v1/site/custom-code`, body, { headers: { Authorization: authHeader, "Content-Type": "application/json" } });
|
|
862
878
|
return { content: [{ type: "text", text: JSON.stringify(r.data, null, 2) }] };
|
|
863
879
|
}
|
|
864
880
|
catch (e) {
|
|
865
881
|
return { content: [{ type: "text", text: `Error: ${e.response?.data?.message || e.message}` }], isError: true };
|
|
866
882
|
}
|
|
867
883
|
});
|
|
868
|
-
server.tool("update-custom-code", "Update a custom code snippet (requires Elementor Pro).", {
|
|
869
|
-
snippet_id: z.string(),
|
|
884
|
+
server.tool("update-custom-code", "Update a custom code snippet (requires Elementor Pro). Can also update Publish Settings — condition, priority, and status.", {
|
|
885
|
+
snippet_id: z.string().describe("ID of the custom code snippet"),
|
|
870
886
|
title: z.string().optional(),
|
|
871
887
|
code: z.string().optional(),
|
|
872
|
-
location: z.string().optional(),
|
|
873
|
-
status: z.string().optional(),
|
|
888
|
+
location: z.string().optional().describe("'head', 'body_start', or 'body_end'"),
|
|
889
|
+
status: z.string().optional().describe("'publish' or 'draft'"),
|
|
890
|
+
priority: z.number().optional().describe("Load priority (lower = earlier)"),
|
|
891
|
+
condition: z.enum(["entire_site", "specific_page", "post_type"]).optional().describe("Where the snippet applies"),
|
|
892
|
+
page_id: z.string().optional().describe("Required when condition='specific_page'"),
|
|
893
|
+
post_type: z.string().optional().describe("Required when condition='post_type'"),
|
|
874
894
|
site: siteParam,
|
|
875
|
-
}, async ({ snippet_id, title, code, location, status, site }) => {
|
|
895
|
+
}, async ({ snippet_id, title, code, location, status, priority, condition, page_id, post_type, site }) => {
|
|
876
896
|
try {
|
|
877
897
|
const { wpUrl, authHeader } = resolveSite(sites, site);
|
|
878
898
|
const body = {};
|
|
879
|
-
if (title)
|
|
899
|
+
if (title !== undefined)
|
|
880
900
|
body.title = title;
|
|
881
|
-
if (code)
|
|
901
|
+
if (code !== undefined)
|
|
882
902
|
body.code = code;
|
|
883
|
-
if (location)
|
|
903
|
+
if (location !== undefined)
|
|
884
904
|
body.location = location;
|
|
885
|
-
if (status)
|
|
905
|
+
if (status !== undefined)
|
|
886
906
|
body.status = status;
|
|
887
|
-
|
|
907
|
+
if (priority !== undefined)
|
|
908
|
+
body.priority = priority;
|
|
909
|
+
if (condition !== undefined)
|
|
910
|
+
body.condition = condition;
|
|
911
|
+
if (page_id !== undefined)
|
|
912
|
+
body.page_id = page_id;
|
|
913
|
+
if (post_type !== undefined)
|
|
914
|
+
body.post_type = post_type;
|
|
915
|
+
const r = await axios.put(`${wpUrl}/wp-json/erc/v1/site/custom-code/${snippet_id}`, body, { headers: { Authorization: authHeader, "Content-Type": "application/json" } });
|
|
888
916
|
return { content: [{ type: "text", text: JSON.stringify(r.data, null, 2) }] };
|
|
889
917
|
}
|
|
890
918
|
catch (e) {
|
|
@@ -1906,6 +1934,80 @@ function createMcpServer(sites) {
|
|
|
1906
1934
|
return { content: [{ type: "text", text: `Error setting Z-index: ${error.response?.data?.message || error.message}` }], isError: true };
|
|
1907
1935
|
}
|
|
1908
1936
|
});
|
|
1937
|
+
// ── 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')"),
|
|
1942
|
+
site: siteParam,
|
|
1943
|
+
}, async ({ page_ids, scope, post_type, site }) => {
|
|
1944
|
+
try {
|
|
1945
|
+
if (!page_ids && !scope && !post_type) {
|
|
1946
|
+
return { content: [{ type: "text", text: "Provide page_ids, scope='all_pages', or post_type." }], isError: true };
|
|
1947
|
+
}
|
|
1948
|
+
const { wpUrl, authHeader } = resolveSite(sites, site);
|
|
1949
|
+
const body = {};
|
|
1950
|
+
if (page_ids)
|
|
1951
|
+
body.page_ids = page_ids;
|
|
1952
|
+
if (scope)
|
|
1953
|
+
body.scope = scope;
|
|
1954
|
+
if (post_type)
|
|
1955
|
+
body.post_type = post_type;
|
|
1956
|
+
const r = await axios.post(`${wpUrl}/wp-json/erc/v1/site/extract-text`, body, { headers: { Authorization: authHeader, "Content-Type": "application/json" } });
|
|
1957
|
+
return { content: [{ type: "text", text: JSON.stringify(r.data, null, 2) }] };
|
|
1958
|
+
}
|
|
1959
|
+
catch (error) {
|
|
1960
|
+
return { content: [{ type: "text", text: `Error extracting page text: ${error.response?.data?.message || error.message}` }], isError: true };
|
|
1961
|
+
}
|
|
1962
|
+
});
|
|
1963
|
+
server.tool("apply-text-fixes", "Write text corrections (grammar/spelling/sentence fixes) back to many Elementor pages in one call. Pair with extract-pages-text. Each fix uses 'old_text' for safe substring replacement (recommended — preserves surrounding HTML/markup and only touches the broken phrase). Omit old_text to fully overwrite the field with new_text.", {
|
|
1964
|
+
fixes: z.string().describe('JSON array of fix objects. Schema: [{"page_id": number, "element_id": string, "field_path": string, "old_text"?: string, "new_text": string}]. ' +
|
|
1965
|
+
"Recommended: always provide old_text — only that exact substring is replaced, leaving surrounding HTML untouched. " +
|
|
1966
|
+
"field_path supports nested paths returned by extract-pages-text (e.g. 'editor', 'title', 'tabs.0.tab_title')."),
|
|
1967
|
+
site: siteParam,
|
|
1968
|
+
}, async ({ fixes, site }) => {
|
|
1969
|
+
let parsed;
|
|
1970
|
+
try {
|
|
1971
|
+
parsed = JSON.parse(fixes);
|
|
1972
|
+
}
|
|
1973
|
+
catch {
|
|
1974
|
+
return { content: [{ type: "text", text: "Invalid JSON in fixes parameter." }], isError: true };
|
|
1975
|
+
}
|
|
1976
|
+
if (!Array.isArray(parsed) || parsed.length === 0) {
|
|
1977
|
+
return { content: [{ type: "text", text: "fixes must be a non-empty JSON array." }], isError: true };
|
|
1978
|
+
}
|
|
1979
|
+
try {
|
|
1980
|
+
const { wpUrl, authHeader } = resolveSite(sites, site);
|
|
1981
|
+
const r = await axios.post(`${wpUrl}/wp-json/erc/v1/site/apply-text-fixes`, { fixes: parsed }, { headers: { Authorization: authHeader, "Content-Type": "application/json" } });
|
|
1982
|
+
return { content: [{ type: "text", text: JSON.stringify(r.data, null, 2) }] };
|
|
1983
|
+
}
|
|
1984
|
+
catch (error) {
|
|
1985
|
+
return { content: [{ type: "text", text: `Error applying text fixes: ${error.response?.data?.message || error.message}` }], isError: true };
|
|
1986
|
+
}
|
|
1987
|
+
});
|
|
1988
|
+
// ── Group 26: Favicon ─────────────────────────────────────────────────────────
|
|
1989
|
+
server.tool("upload-favicon", "Set the WordPress Site Icon (favicon) — equivalent to Customize → Site Identity → Select Site Icon. Accepts either a URL of an image already in the media library, a media attachment ID, or a remote URL to fetch and upload first.", {
|
|
1990
|
+
favicon_url: z.string().optional().describe("URL of the favicon image. If the URL is from the same WP media library, the existing attachment is reused; otherwise the image is downloaded and uploaded as a new media item."),
|
|
1991
|
+
favicon_id: z.number().optional().describe("WordPress media attachment ID of an already-uploaded favicon image (preferred when known)"),
|
|
1992
|
+
site: siteParam,
|
|
1993
|
+
}, async ({ favicon_url, favicon_id, site }) => {
|
|
1994
|
+
try {
|
|
1995
|
+
if (!favicon_url && !favicon_id) {
|
|
1996
|
+
return { content: [{ type: "text", text: "Provide either favicon_url or favicon_id." }], isError: true };
|
|
1997
|
+
}
|
|
1998
|
+
const { wpUrl, authHeader } = resolveSite(sites, site);
|
|
1999
|
+
const body = {};
|
|
2000
|
+
if (favicon_id)
|
|
2001
|
+
body.favicon_id = favicon_id;
|
|
2002
|
+
if (favicon_url)
|
|
2003
|
+
body.favicon_url = favicon_url;
|
|
2004
|
+
const r = await axios.post(`${wpUrl}/wp-json/erc/v1/site/favicon`, body, { headers: { Authorization: authHeader, "Content-Type": "application/json" } });
|
|
2005
|
+
return { content: [{ type: "text", text: JSON.stringify(r.data, null, 2) }] };
|
|
2006
|
+
}
|
|
2007
|
+
catch (error) {
|
|
2008
|
+
return { content: [{ type: "text", text: `Error setting favicon: ${error.response?.data?.message || error.message}` }], isError: true };
|
|
2009
|
+
}
|
|
2010
|
+
});
|
|
1909
2011
|
// ── Group 25: Video Overlay ───────────────────────────────────────────────────
|
|
1910
2012
|
server.tool("update-video-overlay", "Set the image overlay on an Elementor video widget so the video appears to load immediately on page load. The overlay image (typically the video thumbnail or hero image) is shown until the user clicks play. This is the correct fix for 'video not loading immediately' QA issues — not changing the video URL.", {
|
|
1911
2013
|
page_id: z.string().describe("WordPress Page ID containing the video widget"),
|
package/package.json
CHANGED