@yashwant.dharmdas/elementor-mcp 3.1.0 → 3.2.1

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 +115 -0
  2. package/package.json +2 -1
package/dist/index.js CHANGED
@@ -992,6 +992,121 @@ function createMcpServer(sites) {
992
992
  return { content: [{ type: "text", text: `Error enabling Elementor: ${error.response?.data?.message || error.message}` }], isError: true };
993
993
  }
994
994
  });
995
+ // ── Group 9: Screenshot ──────────────────────────────────────────────────────
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
+ page_id: z.string().describe("WordPress Page ID"),
998
+ full_page: z.boolean().optional().describe("Capture full scrollable page (default: true)"),
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
+ site: siteParam,
1004
+ }, async ({ page_id, full_page, width, format, quality, max_height, site }) => {
1005
+ try {
1006
+ const { wpUrl, authHeader } = resolveSite(sites, site);
1007
+ // ── 1. Resolve the page's public URL ─────────────────────────────
1008
+ const pageRes = await axios.get(`${wpUrl}/wp-json/wp/v2/pages/${page_id}`, {
1009
+ headers: { Authorization: authHeader },
1010
+ });
1011
+ const pageUrl = pageRes.data.link;
1012
+ if (!pageUrl)
1013
+ throw new Error("Could not resolve public URL for this page.");
1014
+ // ── 2. Detect system Chrome ───────────────────────────────────────
1015
+ const chromePaths = [
1016
+ "C:\\Program Files\\Google\\Chrome\\Application\\chrome.exe",
1017
+ "C:\\Program Files (x86)\\Google\\Chrome\\Application\\chrome.exe",
1018
+ "/Applications/Google Chrome.app/Contents/MacOS/Google Chrome",
1019
+ "/usr/bin/google-chrome",
1020
+ "/usr/bin/google-chrome-stable",
1021
+ "/usr/bin/chromium-browser",
1022
+ "/usr/bin/chromium",
1023
+ ];
1024
+ let executablePath;
1025
+ for (const p of chromePaths) {
1026
+ if (fs.existsSync(p)) {
1027
+ executablePath = p;
1028
+ break;
1029
+ }
1030
+ }
1031
+ if (!executablePath) {
1032
+ throw new Error("Google Chrome not found. Install it from https://www.google.com/chrome/\n" +
1033
+ "Checked:\n" + chromePaths.map(p => ` • ${p}`).join("\n"));
1034
+ }
1035
+ // ── 3. Ensure screenshots directory exists ───────────────────────
1036
+ const screenshotsDir = path.join(CONFIG_DIR, "screenshots");
1037
+ if (!fs.existsSync(screenshotsDir))
1038
+ fs.mkdirSync(screenshotsDir, { recursive: true });
1039
+ // ── 4. Launch puppeteer-core ──────────────────────────────────────
1040
+ let puppeteer;
1041
+ try {
1042
+ puppeteer = await import("puppeteer-core");
1043
+ }
1044
+ catch {
1045
+ throw new Error("puppeteer-core is not installed. Reinstall @yashwant.dharmdas/elementor-mcp to get it.");
1046
+ }
1047
+ const browser = await puppeteer.launch({
1048
+ executablePath,
1049
+ headless: true,
1050
+ args: ["--no-sandbox", "--disable-setuid-sandbox", "--disable-dev-shm-usage", "--disable-gpu"],
1051
+ });
1052
+ try {
1053
+ const page = await browser.newPage();
1054
+ const vpWidth = width ?? 1440;
1055
+ const vpHeight = max_height ?? 900;
1056
+ await page.setViewport({ width: vpWidth, height: vpHeight, deviceScaleFactor: 1 });
1057
+ await page.goto(pageUrl, { waitUntil: "networkidle2", timeout: 30_000 });
1058
+ await new Promise(resolve => setTimeout(resolve, 1000));
1059
+ const fmt = format ?? "jpeg";
1060
+ const screenshotOptions = { type: fmt, fullPage: !max_height && (full_page ?? true) };
1061
+ if (fmt === "jpeg")
1062
+ screenshotOptions.quality = quality ?? 82;
1063
+ if (max_height)
1064
+ screenshotOptions.clip = { x: 0, y: 0, width: vpWidth, height: vpHeight };
1065
+ const screenshotBuffer = await page.screenshot(screenshotOptions);
1066
+ const buf = Buffer.isBuffer(screenshotBuffer) ? screenshotBuffer : Buffer.from(screenshotBuffer);
1067
+ // ── 5. Save to disk ─────────────────────────────────────────────
1068
+ const ext = fmt === "jpeg" ? "jpg" : "png";
1069
+ const filename = `page_${page_id}_${Date.now()}.${ext}`;
1070
+ const filePath = path.join(screenshotsDir, filename);
1071
+ fs.writeFileSync(filePath, buf);
1072
+ const sizeKB = Math.round(buf.length / 1024);
1073
+ const mimeType = `image/${fmt}`;
1074
+ // ── 6. Return image preview if small enough, always return path ─
1075
+ const PREVIEW_LIMIT = 750 * 1024; // 750 KB raw → ~1 MB base64
1076
+ const content = [
1077
+ {
1078
+ type: "text",
1079
+ text: [
1080
+ `✅ Screenshot saved successfully.`,
1081
+ `📁 file_path: ${filePath}`,
1082
+ `📐 Size: ${sizeKB} KB | Format: ${ext.toUpperCase()} | URL: ${pageUrl}`,
1083
+ ``,
1084
+ `👉 To upload to Basecamp, call upload_attachment with:`,
1085
+ ` file_path = "${filePath}"`,
1086
+ ` project_id = <your Basecamp project ID>`,
1087
+ ].join("\n"),
1088
+ },
1089
+ ];
1090
+ if (buf.length <= PREVIEW_LIMIT) {
1091
+ content.unshift({
1092
+ type: "image",
1093
+ data: buf.toString("base64"),
1094
+ mimeType,
1095
+ });
1096
+ }
1097
+ return { content };
1098
+ }
1099
+ finally {
1100
+ await browser.close();
1101
+ }
1102
+ }
1103
+ catch (error) {
1104
+ return {
1105
+ content: [{ type: "text", text: `Error taking screenshot: ${error.message}` }],
1106
+ isError: true,
1107
+ };
1108
+ }
1109
+ });
995
1110
  return server;
996
1111
  }
997
1112
  // ─── Entry Point ──────────────────────────────────────────────────────────────
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@yashwant.dharmdas/elementor-mcp",
3
- "version": "3.1.0",
3
+ "version": "3.2.1",
4
4
  "description": "MCP server for controlling Elementor via Claude — supports multiple WordPress sites",
5
5
  "main": "dist/index.js",
6
6
  "type": "module",
@@ -19,6 +19,7 @@
19
19
  "@modelcontextprotocol/sdk": "^1.26.0",
20
20
  "ajv": "^8.12.0",
21
21
  "axios": "^1.6.0",
22
+ "puppeteer-core": "^22.15.0",
22
23
  "zod": "^3.22.4"
23
24
  },
24
25
  "devDependencies": {