@yashwant.dharmdas/elementor-mcp 3.2.0 → 3.2.2
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 +98 -39
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -993,29 +993,31 @@ function createMcpServer(sites) {
|
|
|
993
993
|
}
|
|
994
994
|
});
|
|
995
995
|
// ── Group 9: Screenshot ──────────────────────────────────────────────────────
|
|
996
|
-
server.tool("screenshot-page", "Take a
|
|
996
|
+
server.tool("screenshot-page", "Take a screenshot of a published WordPress page using system Chrome. Saves the file to disk and returns both a preview image and the saved file path. Pass the file_path to Basecamp MCP's upload_attachment tool to attach it — never relay the raw image data yourself.", {
|
|
997
997
|
page_id: z.string().describe("WordPress Page ID"),
|
|
998
|
-
full_page: z.boolean().optional().describe("Capture
|
|
998
|
+
full_page: z.boolean().optional().describe("Capture full scrollable page (default: true)"),
|
|
999
999
|
width: z.number().optional().describe("Viewport width in pixels (default: 1440)"),
|
|
1000
|
+
format: z.enum(["jpeg", "png"]).optional().describe("Image format — jpeg is much smaller and recommended (default: jpeg)"),
|
|
1001
|
+
quality: z.number().min(1).max(100).optional().describe("JPEG quality 1–100 (default: 82). Lower = smaller file size."),
|
|
1002
|
+
max_height: z.number().optional().describe("Cap the captured height in pixels (e.g. 4000). Useful for very long pages to stay under size limits."),
|
|
1003
|
+
wait: z.number().optional().describe("Extra milliseconds to wait after page load + auto-scroll before capturing (default: 2000). Increase for heavy animations/videos."),
|
|
1004
|
+
auto_scroll: z.boolean().optional().describe("Scroll through the page to trigger lazy-loaded images and content (default: true). Disable only for pages with infinite scroll."),
|
|
1000
1005
|
site: siteParam,
|
|
1001
|
-
}, async ({ page_id, full_page, width, site }) => {
|
|
1006
|
+
}, async ({ page_id, full_page, width, format, quality, max_height, wait, auto_scroll, site }) => {
|
|
1002
1007
|
try {
|
|
1003
1008
|
const { wpUrl, authHeader } = resolveSite(sites, site);
|
|
1004
|
-
// ── 1. Resolve the page's public URL
|
|
1009
|
+
// ── 1. Resolve the page's public URL ─────────────────────────────
|
|
1005
1010
|
const pageRes = await axios.get(`${wpUrl}/wp-json/wp/v2/pages/${page_id}`, {
|
|
1006
1011
|
headers: { Authorization: authHeader },
|
|
1007
1012
|
});
|
|
1008
1013
|
const pageUrl = pageRes.data.link;
|
|
1009
1014
|
if (!pageUrl)
|
|
1010
1015
|
throw new Error("Could not resolve public URL for this page.");
|
|
1011
|
-
// ── 2. Detect system Chrome
|
|
1016
|
+
// ── 2. Detect system Chrome ───────────────────────────────────────
|
|
1012
1017
|
const chromePaths = [
|
|
1013
|
-
// Windows
|
|
1014
1018
|
"C:\\Program Files\\Google\\Chrome\\Application\\chrome.exe",
|
|
1015
1019
|
"C:\\Program Files (x86)\\Google\\Chrome\\Application\\chrome.exe",
|
|
1016
|
-
// macOS
|
|
1017
1020
|
"/Applications/Google Chrome.app/Contents/MacOS/Google Chrome",
|
|
1018
|
-
// Linux
|
|
1019
1021
|
"/usr/bin/google-chrome",
|
|
1020
1022
|
"/usr/bin/google-chrome-stable",
|
|
1021
1023
|
"/usr/bin/chromium-browser",
|
|
@@ -1029,51 +1031,108 @@ function createMcpServer(sites) {
|
|
|
1029
1031
|
}
|
|
1030
1032
|
}
|
|
1031
1033
|
if (!executablePath) {
|
|
1032
|
-
throw new Error("Google Chrome not found
|
|
1033
|
-
"
|
|
1034
|
-
"Checked paths:\n" + chromePaths.map(p => ` • ${p}`).join("\n"));
|
|
1034
|
+
throw new Error("Google Chrome not found. Install it from https://www.google.com/chrome/\n" +
|
|
1035
|
+
"Checked:\n" + chromePaths.map(p => ` • ${p}`).join("\n"));
|
|
1035
1036
|
}
|
|
1036
|
-
// ── 3.
|
|
1037
|
+
// ── 3. Ensure screenshots directory exists ───────────────────────
|
|
1038
|
+
const screenshotsDir = path.join(CONFIG_DIR, "screenshots");
|
|
1039
|
+
if (!fs.existsSync(screenshotsDir))
|
|
1040
|
+
fs.mkdirSync(screenshotsDir, { recursive: true });
|
|
1041
|
+
// ── 4. Launch puppeteer-core ──────────────────────────────────────
|
|
1037
1042
|
let puppeteer;
|
|
1038
1043
|
try {
|
|
1039
1044
|
puppeteer = await import("puppeteer-core");
|
|
1040
1045
|
}
|
|
1041
1046
|
catch {
|
|
1042
|
-
throw new Error("puppeteer-core is not installed.
|
|
1043
|
-
"(or reinstall @yashwant.dharmdas/elementor-mcp — it is bundled as a dependency)");
|
|
1047
|
+
throw new Error("puppeteer-core is not installed. Reinstall @yashwant.dharmdas/elementor-mcp to get it.");
|
|
1044
1048
|
}
|
|
1045
1049
|
const browser = await puppeteer.launch({
|
|
1046
1050
|
executablePath,
|
|
1047
1051
|
headless: true,
|
|
1048
|
-
args: [
|
|
1049
|
-
"--no-sandbox",
|
|
1050
|
-
"--disable-setuid-sandbox",
|
|
1051
|
-
"--disable-dev-shm-usage",
|
|
1052
|
-
"--disable-gpu",
|
|
1053
|
-
],
|
|
1052
|
+
args: ["--no-sandbox", "--disable-setuid-sandbox", "--disable-dev-shm-usage", "--disable-gpu"],
|
|
1054
1053
|
});
|
|
1055
1054
|
try {
|
|
1056
1055
|
const page = await browser.newPage();
|
|
1057
|
-
|
|
1058
|
-
|
|
1059
|
-
|
|
1060
|
-
|
|
1061
|
-
|
|
1062
|
-
|
|
1063
|
-
|
|
1056
|
+
const vpWidth = width ?? 1440;
|
|
1057
|
+
const vpHeight = max_height ?? 900;
|
|
1058
|
+
await page.setViewport({ width: vpWidth, height: vpHeight, deviceScaleFactor: 1 });
|
|
1059
|
+
// Navigate and wait for network to go idle
|
|
1060
|
+
await page.goto(pageUrl, { waitUntil: "networkidle2", timeout: 45_000 });
|
|
1061
|
+
// ── Auto-scroll: trigger lazy-loaded images/content ─────────────
|
|
1062
|
+
if (auto_scroll !== false) {
|
|
1063
|
+
await page.evaluate(async () => {
|
|
1064
|
+
await new Promise((resolve) => {
|
|
1065
|
+
let totalHeight = 0;
|
|
1066
|
+
const distance = 200;
|
|
1067
|
+
const timer = setInterval(() => {
|
|
1068
|
+
const scrollHeight = document.body.scrollHeight;
|
|
1069
|
+
window.scrollBy(0, distance);
|
|
1070
|
+
totalHeight += distance;
|
|
1071
|
+
if (totalHeight >= scrollHeight) {
|
|
1072
|
+
clearInterval(timer);
|
|
1073
|
+
resolve();
|
|
1074
|
+
}
|
|
1075
|
+
}, 80);
|
|
1076
|
+
});
|
|
1077
|
+
});
|
|
1078
|
+
// Scroll back to top for the capture
|
|
1079
|
+
await page.evaluate(() => window.scrollTo(0, 0));
|
|
1080
|
+
}
|
|
1081
|
+
// ── Wait for all <img> tags to finish loading ──────────────────
|
|
1082
|
+
await page.evaluate(async () => {
|
|
1083
|
+
const images = Array.from(document.querySelectorAll("img"));
|
|
1084
|
+
await Promise.all(images.map((img) => {
|
|
1085
|
+
if (img.complete && img.naturalWidth > 0)
|
|
1086
|
+
return Promise.resolve();
|
|
1087
|
+
return new Promise((resolve) => {
|
|
1088
|
+
img.addEventListener("load", () => resolve(), { once: true });
|
|
1089
|
+
img.addEventListener("error", () => resolve(), { once: true });
|
|
1090
|
+
// Safety timeout per image
|
|
1091
|
+
setTimeout(() => resolve(), 5000);
|
|
1092
|
+
});
|
|
1093
|
+
}));
|
|
1064
1094
|
});
|
|
1065
|
-
|
|
1066
|
-
|
|
1067
|
-
|
|
1068
|
-
|
|
1069
|
-
|
|
1070
|
-
|
|
1071
|
-
|
|
1072
|
-
|
|
1073
|
-
|
|
1074
|
-
|
|
1075
|
-
|
|
1076
|
-
|
|
1095
|
+
// Extra settle time for fonts, animations, videos
|
|
1096
|
+
await new Promise(resolve => setTimeout(resolve, wait ?? 2000));
|
|
1097
|
+
const fmt = format ?? "jpeg";
|
|
1098
|
+
const screenshotOptions = { type: fmt, fullPage: !max_height && (full_page ?? true) };
|
|
1099
|
+
if (fmt === "jpeg")
|
|
1100
|
+
screenshotOptions.quality = quality ?? 82;
|
|
1101
|
+
if (max_height)
|
|
1102
|
+
screenshotOptions.clip = { x: 0, y: 0, width: vpWidth, height: vpHeight };
|
|
1103
|
+
const screenshotBuffer = await page.screenshot(screenshotOptions);
|
|
1104
|
+
const buf = Buffer.isBuffer(screenshotBuffer) ? screenshotBuffer : Buffer.from(screenshotBuffer);
|
|
1105
|
+
// ── 5. Save to disk ─────────────────────────────────────────────
|
|
1106
|
+
const ext = fmt === "jpeg" ? "jpg" : "png";
|
|
1107
|
+
const filename = `page_${page_id}_${Date.now()}.${ext}`;
|
|
1108
|
+
const filePath = path.join(screenshotsDir, filename);
|
|
1109
|
+
fs.writeFileSync(filePath, buf);
|
|
1110
|
+
const sizeKB = Math.round(buf.length / 1024);
|
|
1111
|
+
const mimeType = `image/${fmt}`;
|
|
1112
|
+
// ── 6. Return image preview if small enough, always return path ─
|
|
1113
|
+
const PREVIEW_LIMIT = 750 * 1024; // 750 KB raw → ~1 MB base64
|
|
1114
|
+
const content = [
|
|
1115
|
+
{
|
|
1116
|
+
type: "text",
|
|
1117
|
+
text: [
|
|
1118
|
+
`✅ Screenshot saved successfully.`,
|
|
1119
|
+
`📁 file_path: ${filePath}`,
|
|
1120
|
+
`📐 Size: ${sizeKB} KB | Format: ${ext.toUpperCase()} | URL: ${pageUrl}`,
|
|
1121
|
+
``,
|
|
1122
|
+
`👉 To upload to Basecamp, call upload_attachment with:`,
|
|
1123
|
+
` file_path = "${filePath}"`,
|
|
1124
|
+
` project_id = <your Basecamp project ID>`,
|
|
1125
|
+
].join("\n"),
|
|
1126
|
+
},
|
|
1127
|
+
];
|
|
1128
|
+
if (buf.length <= PREVIEW_LIMIT) {
|
|
1129
|
+
content.unshift({
|
|
1130
|
+
type: "image",
|
|
1131
|
+
data: buf.toString("base64"),
|
|
1132
|
+
mimeType,
|
|
1133
|
+
});
|
|
1134
|
+
}
|
|
1135
|
+
return { content };
|
|
1077
1136
|
}
|
|
1078
1137
|
finally {
|
|
1079
1138
|
await browser.close();
|
package/package.json
CHANGED