@yashwant.dharmdas/elementor-mcp 3.14.0 → 3.15.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 +351 -62
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -1046,8 +1046,291 @@ function createMcpServer(sites) {
|
|
|
1046
1046
|
// ═══════════════════════════════════════════════════════════════════════════
|
|
1047
1047
|
// ── COMPOUND BUILDERS — scaffold multi-step ops into one tool ─────────────
|
|
1048
1048
|
// ═══════════════════════════════════════════════════════════════════════════
|
|
1049
|
+
// ── Helper: typography settings builder for child widgets ─────────────────
|
|
1050
|
+
// Maps clean params (typography_global, font_size, color, etc.) → Elementor's
|
|
1051
|
+
// typography_* keys + __globals__ entries. Used by create-section children.
|
|
1052
|
+
const buildTypography = (opts, prefix = "typography") => {
|
|
1053
|
+
const settings = {};
|
|
1054
|
+
const globals = {};
|
|
1055
|
+
if (!opts)
|
|
1056
|
+
return { settings, globals };
|
|
1057
|
+
// Global typography token wins over everything else
|
|
1058
|
+
if (opts.typography_global) {
|
|
1059
|
+
globals[`${prefix}_typography`] = `globals/typography?id=${opts.typography_global}`;
|
|
1060
|
+
return { settings, globals };
|
|
1061
|
+
}
|
|
1062
|
+
// Otherwise build custom typography
|
|
1063
|
+
let touched = false;
|
|
1064
|
+
const fontKey = `${prefix}_font_family`;
|
|
1065
|
+
const sizeKey = `${prefix}_font_size`;
|
|
1066
|
+
if (opts.font_family) {
|
|
1067
|
+
settings[fontKey] = opts.font_family;
|
|
1068
|
+
touched = true;
|
|
1069
|
+
}
|
|
1070
|
+
const fs1 = parseSize(opts.font_size);
|
|
1071
|
+
if (fs1) {
|
|
1072
|
+
settings[sizeKey] = fs1;
|
|
1073
|
+
touched = true;
|
|
1074
|
+
}
|
|
1075
|
+
const fs2 = parseSize(opts.font_size_tablet);
|
|
1076
|
+
if (fs2) {
|
|
1077
|
+
settings[`${sizeKey}_tablet`] = fs2;
|
|
1078
|
+
touched = true;
|
|
1079
|
+
}
|
|
1080
|
+
const fs3 = parseSize(opts.font_size_mobile);
|
|
1081
|
+
if (fs3) {
|
|
1082
|
+
settings[`${sizeKey}_mobile`] = fs3;
|
|
1083
|
+
touched = true;
|
|
1084
|
+
}
|
|
1085
|
+
if (opts.font_weight) {
|
|
1086
|
+
settings[`${prefix}_font_weight`] = String(opts.font_weight);
|
|
1087
|
+
touched = true;
|
|
1088
|
+
}
|
|
1089
|
+
if (opts.letter_spacing !== undefined) {
|
|
1090
|
+
settings[`${prefix}_letter_spacing`] = { unit: "px", size: opts.letter_spacing };
|
|
1091
|
+
touched = true;
|
|
1092
|
+
}
|
|
1093
|
+
const lh = parseSize(opts.line_height);
|
|
1094
|
+
if (lh) {
|
|
1095
|
+
settings[`${prefix}_line_height`] = lh;
|
|
1096
|
+
touched = true;
|
|
1097
|
+
}
|
|
1098
|
+
if (opts.text_transform) {
|
|
1099
|
+
settings[`${prefix}_text_transform`] = opts.text_transform;
|
|
1100
|
+
touched = true;
|
|
1101
|
+
}
|
|
1102
|
+
if (opts.text_decoration) {
|
|
1103
|
+
settings[`${prefix}_text_decoration`] = opts.text_decoration;
|
|
1104
|
+
touched = true;
|
|
1105
|
+
}
|
|
1106
|
+
if (touched)
|
|
1107
|
+
settings[`${prefix}_typography`] = "custom";
|
|
1108
|
+
return { settings, globals };
|
|
1109
|
+
};
|
|
1110
|
+
// ── Helper: build container settings from clean params ────────────────────
|
|
1111
|
+
const buildContainerSettings = (opts) => {
|
|
1112
|
+
const s = {};
|
|
1113
|
+
const g = {};
|
|
1114
|
+
if (opts.content_width)
|
|
1115
|
+
s.content_width = opts.content_width;
|
|
1116
|
+
if (opts.direction)
|
|
1117
|
+
s.flex_direction = opts.direction;
|
|
1118
|
+
if (opts.direction_mobile)
|
|
1119
|
+
s.flex_direction_mobile = opts.direction_mobile;
|
|
1120
|
+
if (opts.justify)
|
|
1121
|
+
s.flex_justify_content = opts.justify;
|
|
1122
|
+
if (opts.align)
|
|
1123
|
+
s.flex_align_items = opts.align;
|
|
1124
|
+
if (opts.wrap)
|
|
1125
|
+
s.flex_wrap = opts.wrap;
|
|
1126
|
+
const bw = parseSize(opts.boxed_width);
|
|
1127
|
+
if (bw)
|
|
1128
|
+
s.boxed_width = bw;
|
|
1129
|
+
const p1 = parseSpacing(opts.padding);
|
|
1130
|
+
if (p1)
|
|
1131
|
+
s.padding = p1;
|
|
1132
|
+
const p2 = parseSpacing(opts.padding_tablet);
|
|
1133
|
+
if (p2)
|
|
1134
|
+
s.padding_tablet = p2;
|
|
1135
|
+
const p3 = parseSpacing(opts.padding_mobile);
|
|
1136
|
+
if (p3)
|
|
1137
|
+
s.padding_mobile = p3;
|
|
1138
|
+
const g1 = parseGap(opts.gap);
|
|
1139
|
+
if (g1)
|
|
1140
|
+
s.flex_gap = g1;
|
|
1141
|
+
const g2 = parseGap(opts.gap_tablet);
|
|
1142
|
+
if (g2)
|
|
1143
|
+
s.flex_gap_tablet = g2;
|
|
1144
|
+
const g3 = parseGap(opts.gap_mobile);
|
|
1145
|
+
if (g3)
|
|
1146
|
+
s.flex_gap_mobile = g3;
|
|
1147
|
+
const mh1 = parseSize(opts.min_height);
|
|
1148
|
+
if (mh1)
|
|
1149
|
+
s.min_height = mh1;
|
|
1150
|
+
const mh2 = parseSize(opts.min_height_tablet);
|
|
1151
|
+
if (mh2)
|
|
1152
|
+
s.min_height_tablet = mh2;
|
|
1153
|
+
const mh3 = parseSize(opts.min_height_mobile);
|
|
1154
|
+
if (mh3)
|
|
1155
|
+
s.min_height_mobile = mh3;
|
|
1156
|
+
const w1 = parseSize(opts.width);
|
|
1157
|
+
if (w1)
|
|
1158
|
+
s.width = w1;
|
|
1159
|
+
const w2 = parseSize(opts.width_tablet);
|
|
1160
|
+
if (w2)
|
|
1161
|
+
s.width_tablet = w2;
|
|
1162
|
+
const w3 = parseSize(opts.width_mobile);
|
|
1163
|
+
if (w3)
|
|
1164
|
+
s.width_mobile = w3;
|
|
1165
|
+
if (opts.background_color) {
|
|
1166
|
+
s.background_background = "classic";
|
|
1167
|
+
const c = resolveColor(opts.background_color);
|
|
1168
|
+
if (c.value)
|
|
1169
|
+
s.background_color = c.value;
|
|
1170
|
+
if (c.global_id)
|
|
1171
|
+
g.background_color = c.global_id;
|
|
1172
|
+
}
|
|
1173
|
+
if (opts.background_image) {
|
|
1174
|
+
s.background_background = "classic";
|
|
1175
|
+
s.background_image = { url: opts.background_image, id: opts.background_image_id ?? "" };
|
|
1176
|
+
s.background_size = "cover";
|
|
1177
|
+
s.background_position = "center center";
|
|
1178
|
+
}
|
|
1179
|
+
if (opts.overlay_color || opts.overlay_opacity !== undefined) {
|
|
1180
|
+
s.background_overlay_background = "classic";
|
|
1181
|
+
if (opts.overlay_color) {
|
|
1182
|
+
const oc = resolveColor(opts.overlay_color);
|
|
1183
|
+
if (oc.value)
|
|
1184
|
+
s.background_overlay_color = oc.value;
|
|
1185
|
+
if (oc.global_id)
|
|
1186
|
+
g.background_overlay_color = oc.global_id;
|
|
1187
|
+
}
|
|
1188
|
+
if (opts.overlay_opacity !== undefined) {
|
|
1189
|
+
s.background_overlay_opacity = { unit: "px", size: opts.overlay_opacity };
|
|
1190
|
+
}
|
|
1191
|
+
}
|
|
1192
|
+
if (Object.keys(g).length)
|
|
1193
|
+
s.__globals__ = { ...(s.__globals__ || {}), ...g };
|
|
1194
|
+
return s;
|
|
1195
|
+
};
|
|
1196
|
+
// ── Helper: recursive child-element builder ───────────────────────────────
|
|
1197
|
+
// Accepts a ChildDef object and returns an Elementor element JSON node.
|
|
1198
|
+
// Supported `type` values: container, heading, text, button, image, spacer.
|
|
1199
|
+
// Containers can nest further children. Unknown types throw.
|
|
1200
|
+
const buildChild = (child) => {
|
|
1201
|
+
if (!child || typeof child !== "object") {
|
|
1202
|
+
throw new Error("create-section child must be an object with a 'type' field.");
|
|
1203
|
+
}
|
|
1204
|
+
const t = String(child.type || "").toLowerCase();
|
|
1205
|
+
// ── container ───────────────────────────────────────────────────────
|
|
1206
|
+
if (t === "container") {
|
|
1207
|
+
const s = buildContainerSettings(child);
|
|
1208
|
+
const node = {
|
|
1209
|
+
elType: "container",
|
|
1210
|
+
settings: s,
|
|
1211
|
+
elements: [],
|
|
1212
|
+
isInner: true,
|
|
1213
|
+
};
|
|
1214
|
+
if (Array.isArray(child.children)) {
|
|
1215
|
+
node.elements = child.children.map((c) => buildChild(c));
|
|
1216
|
+
}
|
|
1217
|
+
return node;
|
|
1218
|
+
}
|
|
1219
|
+
// ── heading ─────────────────────────────────────────────────────────
|
|
1220
|
+
if (t === "heading") {
|
|
1221
|
+
const s = { title: String(child.title ?? "") };
|
|
1222
|
+
if (child.header_size)
|
|
1223
|
+
s.header_size = child.header_size;
|
|
1224
|
+
if (child.align)
|
|
1225
|
+
s.align = child.align;
|
|
1226
|
+
const typo = buildTypography(child);
|
|
1227
|
+
Object.assign(s, typo.settings);
|
|
1228
|
+
if (Object.keys(typo.globals).length)
|
|
1229
|
+
s.__globals__ = { ...(s.__globals__ || {}), ...typo.globals };
|
|
1230
|
+
if (child.color) {
|
|
1231
|
+
const c = resolveColor(child.color);
|
|
1232
|
+
if (c.value)
|
|
1233
|
+
s.title_color = c.value;
|
|
1234
|
+
if (c.global_id)
|
|
1235
|
+
s.__globals__ = { ...(s.__globals__ || {}), title_color: c.global_id };
|
|
1236
|
+
}
|
|
1237
|
+
return { elType: "widget", widgetType: "heading", settings: s, elements: [] };
|
|
1238
|
+
}
|
|
1239
|
+
// ── text-editor (paragraph / rich text) ──────────────────────────────
|
|
1240
|
+
if (t === "text" || t === "text-editor") {
|
|
1241
|
+
const s = { editor: String(child.content ?? "") };
|
|
1242
|
+
const typo = buildTypography(child);
|
|
1243
|
+
Object.assign(s, typo.settings);
|
|
1244
|
+
if (Object.keys(typo.globals).length)
|
|
1245
|
+
s.__globals__ = { ...(s.__globals__ || {}), ...typo.globals };
|
|
1246
|
+
if (child.color) {
|
|
1247
|
+
const c = resolveColor(child.color);
|
|
1248
|
+
if (c.value)
|
|
1249
|
+
s.text_color = c.value;
|
|
1250
|
+
if (c.global_id)
|
|
1251
|
+
s.__globals__ = { ...(s.__globals__ || {}), text_color: c.global_id };
|
|
1252
|
+
}
|
|
1253
|
+
if (child.align)
|
|
1254
|
+
s.align = child.align;
|
|
1255
|
+
return { elType: "widget", widgetType: "text-editor", settings: s, elements: [] };
|
|
1256
|
+
}
|
|
1257
|
+
// ── button ───────────────────────────────────────────────────────────
|
|
1258
|
+
if (t === "button") {
|
|
1259
|
+
const s = { text: String(child.text ?? "") };
|
|
1260
|
+
if (child.link)
|
|
1261
|
+
s.link = { url: child.link };
|
|
1262
|
+
if (child.align)
|
|
1263
|
+
s.align = child.align;
|
|
1264
|
+
const typo = buildTypography(child);
|
|
1265
|
+
Object.assign(s, typo.settings);
|
|
1266
|
+
if (Object.keys(typo.globals).length)
|
|
1267
|
+
s.__globals__ = { ...(s.__globals__ || {}), ...typo.globals };
|
|
1268
|
+
if (child.text_color) {
|
|
1269
|
+
const c = resolveColor(child.text_color);
|
|
1270
|
+
if (c.value)
|
|
1271
|
+
s.button_text_color = c.value;
|
|
1272
|
+
if (c.global_id)
|
|
1273
|
+
s.__globals__ = { ...(s.__globals__ || {}), button_text_color: c.global_id };
|
|
1274
|
+
}
|
|
1275
|
+
if (child.background_color) {
|
|
1276
|
+
const c = resolveColor(child.background_color);
|
|
1277
|
+
if (c.value)
|
|
1278
|
+
s.background_color = c.value;
|
|
1279
|
+
if (c.global_id)
|
|
1280
|
+
s.__globals__ = { ...(s.__globals__ || {}), background_color: c.global_id };
|
|
1281
|
+
}
|
|
1282
|
+
if (child.border) {
|
|
1283
|
+
s.border_border = child.border.style || "solid";
|
|
1284
|
+
if (child.border.color) {
|
|
1285
|
+
const c = resolveColor(child.border.color);
|
|
1286
|
+
if (c.value)
|
|
1287
|
+
s.border_color = c.value;
|
|
1288
|
+
if (c.global_id)
|
|
1289
|
+
s.__globals__ = { ...(s.__globals__ || {}), border_color: c.global_id };
|
|
1290
|
+
}
|
|
1291
|
+
if (child.border.width !== undefined) {
|
|
1292
|
+
const w = Number(child.border.width);
|
|
1293
|
+
s.border_width = { unit: "px", top: String(w), right: String(w), bottom: String(w), left: String(w), isLinked: true };
|
|
1294
|
+
}
|
|
1295
|
+
}
|
|
1296
|
+
if (child.border_radius !== undefined) {
|
|
1297
|
+
const r = Number(child.border_radius);
|
|
1298
|
+
s.border_radius = { unit: "px", top: String(r), right: String(r), bottom: String(r), left: String(r), isLinked: true };
|
|
1299
|
+
}
|
|
1300
|
+
const p1 = parseSpacing(child.padding);
|
|
1301
|
+
if (p1)
|
|
1302
|
+
s.padding = p1;
|
|
1303
|
+
return { elType: "widget", widgetType: "button", settings: s, elements: [] };
|
|
1304
|
+
}
|
|
1305
|
+
// ── image ────────────────────────────────────────────────────────────
|
|
1306
|
+
if (t === "image") {
|
|
1307
|
+
const s = {
|
|
1308
|
+
image: { url: String(child.url ?? ""), id: child.attachment_id ?? "" },
|
|
1309
|
+
image_size: child.image_size || "full",
|
|
1310
|
+
};
|
|
1311
|
+
if (child.alt)
|
|
1312
|
+
s.alt = child.alt;
|
|
1313
|
+
if (child.link)
|
|
1314
|
+
s.link = { url: child.link };
|
|
1315
|
+
if (child.align)
|
|
1316
|
+
s.align = child.align;
|
|
1317
|
+
const w = parseSize(child.width);
|
|
1318
|
+
if (w)
|
|
1319
|
+
s.width = w;
|
|
1320
|
+
return { elType: "widget", widgetType: "image", settings: s, elements: [] };
|
|
1321
|
+
}
|
|
1322
|
+
// ── spacer ───────────────────────────────────────────────────────────
|
|
1323
|
+
if (t === "spacer") {
|
|
1324
|
+
const s = {};
|
|
1325
|
+
const h = parseSize(child.height);
|
|
1326
|
+
if (h)
|
|
1327
|
+
s.space = h;
|
|
1328
|
+
return { elType: "widget", widgetType: "spacer", settings: s, elements: [] };
|
|
1329
|
+
}
|
|
1330
|
+
throw new Error(`Unknown child type '${child.type}'. Supported: container, heading, text, button, image, spacer.`);
|
|
1331
|
+
};
|
|
1049
1332
|
// ── 13. create-section ────────────────────────────────────────────────────
|
|
1050
|
-
server.tool("create-section", "Scaffold a
|
|
1333
|
+
server.tool("create-section", "Scaffold a section container — optionally with its FULL subtree of nested containers + widgets — in ONE call. Pass top-level settings (padding, bg, etc.) AND an optional 'children' array of typed widget defs. This replaces 50+ round-trips (insert-element + N×set-X) with a single call. Supported child types: 'container' (nests further), 'heading', 'text' (text-editor), 'button', 'image', 'spacer'. Each child accepts typography_global (preferred — uses kit globals) OR custom typography props (font_size, font_weight, etc.). Colors accept either hex ('#FFF') or kit-global slug ('primary').", {
|
|
1051
1334
|
page_id: z.string().describe("WordPress Page ID"),
|
|
1052
1335
|
parent_id: z.string().optional().describe("Parent container ID. Omit for top-level section."),
|
|
1053
1336
|
position: z.enum(["before", "after", "first_child", "last_child"]).optional().describe("Default 'last_child' (append to parent or page end)"),
|
|
@@ -1065,78 +1348,38 @@ function createMcpServer(sites) {
|
|
|
1065
1348
|
min_height: z.string().optional().describe("Min-height — '420px', '80vh'"),
|
|
1066
1349
|
min_height_tablet: z.string().optional(),
|
|
1067
1350
|
min_height_mobile: z.string().optional(),
|
|
1351
|
+
justify: z.enum(["flex-start", "center", "flex-end", "space-between", "space-around", "space-evenly"]).optional().describe("Justify content along the flex main axis"),
|
|
1352
|
+
align: z.enum(["flex-start", "center", "flex-end", "stretch", "baseline"]).optional().describe("Align items along the flex cross axis"),
|
|
1353
|
+
wrap: z.enum(["wrap", "nowrap"]).optional().describe("Flex wrap. Default 'nowrap'."),
|
|
1068
1354
|
background_color: z.string().optional().describe("Background color — hex or global slug"),
|
|
1069
1355
|
background_image: z.string().optional().describe("Background image URL"),
|
|
1356
|
+
background_image_id: z.number().optional().describe("WP attachment ID for the bg image (enables srcset). Use the id returned by list-media-library."),
|
|
1070
1357
|
overlay_color: z.string().optional().describe("Overlay color — hex or global slug"),
|
|
1071
1358
|
overlay_opacity: z.number().optional().describe("Overlay opacity 0–1"),
|
|
1359
|
+
children: z.array(z.any()).optional().describe("Optional array of child elements to build inside this section in the SAME call. Each child: { type: 'container'|'heading'|'text'|'button'|'image'|'spacer', ...settings }. Containers can nest further via their own 'children' array. Prefer typography_global ('primary-text', 'body') over hardcoded typography. Prefer kit-global color slugs ('primary', 'accent') over hex. This is the fastest way to build a section — ~50x fewer round-trips than the per-widget tools."),
|
|
1072
1360
|
site: siteParam,
|
|
1073
|
-
}, async (
|
|
1361
|
+
}, async (args) => {
|
|
1074
1362
|
try {
|
|
1363
|
+
const { page_id, parent_id, position, reference_id, children, site } = args;
|
|
1075
1364
|
const { wpUrl, authHeader } = resolveSite(sites, site);
|
|
1076
|
-
|
|
1077
|
-
|
|
1078
|
-
|
|
1365
|
+
// Default content_width "boxed" / direction "column" for top-level sections
|
|
1366
|
+
const containerOpts = {
|
|
1367
|
+
...args,
|
|
1368
|
+
content_width: args.content_width || "boxed",
|
|
1369
|
+
direction: args.direction || "column",
|
|
1079
1370
|
};
|
|
1080
|
-
|
|
1081
|
-
|
|
1082
|
-
|
|
1083
|
-
if (
|
|
1084
|
-
|
|
1085
|
-
|
|
1086
|
-
if (p1)
|
|
1087
|
-
settings.padding = p1;
|
|
1088
|
-
const p2 = parseSpacing(padding_tablet);
|
|
1089
|
-
if (p2)
|
|
1090
|
-
settings.padding_tablet = p2;
|
|
1091
|
-
const p3 = parseSpacing(padding_mobile);
|
|
1092
|
-
if (p3)
|
|
1093
|
-
settings.padding_mobile = p3;
|
|
1094
|
-
const g1 = parseGap(gap);
|
|
1095
|
-
if (g1)
|
|
1096
|
-
settings.flex_gap = g1;
|
|
1097
|
-
const g2 = parseGap(gap_tablet);
|
|
1098
|
-
if (g2)
|
|
1099
|
-
settings.flex_gap_tablet = g2;
|
|
1100
|
-
const g3 = parseGap(gap_mobile);
|
|
1101
|
-
if (g3)
|
|
1102
|
-
settings.flex_gap_mobile = g3;
|
|
1103
|
-
const mh1 = parseSize(min_height);
|
|
1104
|
-
if (mh1)
|
|
1105
|
-
settings.min_height = mh1;
|
|
1106
|
-
const mh2 = parseSize(min_height_tablet);
|
|
1107
|
-
if (mh2)
|
|
1108
|
-
settings.min_height_tablet = mh2;
|
|
1109
|
-
const mh3 = parseSize(min_height_mobile);
|
|
1110
|
-
if (mh3)
|
|
1111
|
-
settings.min_height_mobile = mh3;
|
|
1112
|
-
if (background_color) {
|
|
1113
|
-
settings.background_background = "classic";
|
|
1114
|
-
const c = resolveColor(background_color);
|
|
1115
|
-
if (c.value)
|
|
1116
|
-
settings.background_color = c.value;
|
|
1117
|
-
if (c.global_id)
|
|
1118
|
-
settings.__globals__ = { ...(settings.__globals__ || {}), background_color: c.global_id };
|
|
1119
|
-
}
|
|
1120
|
-
if (background_image) {
|
|
1121
|
-
settings.background_background = "classic";
|
|
1122
|
-
settings.background_image = { url: background_image, id: "" };
|
|
1123
|
-
settings.background_size = "cover";
|
|
1124
|
-
settings.background_position = "center center";
|
|
1125
|
-
}
|
|
1126
|
-
if (overlay_color || overlay_opacity !== undefined) {
|
|
1127
|
-
settings.background_overlay_background = "classic";
|
|
1128
|
-
if (overlay_color) {
|
|
1129
|
-
const oc = resolveColor(overlay_color);
|
|
1130
|
-
if (oc.value)
|
|
1131
|
-
settings.background_overlay_color = oc.value;
|
|
1132
|
-
if (oc.global_id)
|
|
1133
|
-
settings.__globals__ = { ...(settings.__globals__ || {}), background_overlay_color: oc.global_id };
|
|
1371
|
+
const settings = buildContainerSettings(containerOpts);
|
|
1372
|
+
// Build any inline children recursively
|
|
1373
|
+
let elements = [];
|
|
1374
|
+
if (Array.isArray(children) && children.length) {
|
|
1375
|
+
try {
|
|
1376
|
+
elements = children.map((c) => buildChild(c));
|
|
1134
1377
|
}
|
|
1135
|
-
|
|
1136
|
-
|
|
1378
|
+
catch (err) {
|
|
1379
|
+
return { content: [{ type: "text", text: `Error building children: ${err.message}` }], isError: true };
|
|
1137
1380
|
}
|
|
1138
1381
|
}
|
|
1139
|
-
const newElement = { elType: "container", settings, elements
|
|
1382
|
+
const newElement = { elType: "container", settings, elements };
|
|
1140
1383
|
const body = { element: newElement, position: position || "last_child" };
|
|
1141
1384
|
if (parent_id)
|
|
1142
1385
|
body.parent_id = parent_id;
|
|
@@ -3532,6 +3775,52 @@ function createMcpServer(sites) {
|
|
|
3532
3775
|
return { content: [{ type: "text", text: `Error updating video overlay: ${error.response?.data?.message || error.message}` }], isError: true };
|
|
3533
3776
|
}
|
|
3534
3777
|
});
|
|
3778
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
3779
|
+
// ── v3.15.0 — Media library browse + page-structure validation ────────────
|
|
3780
|
+
// Replaces the "export images from Figma → upload" workflow with a
|
|
3781
|
+
// "developer uploads clean source images, AI matches by name" workflow.
|
|
3782
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
3783
|
+
// ── list-media-library ────────────────────────────────────────────────────
|
|
3784
|
+
server.tool("list-media-library", "List images already uploaded to the WordPress media library. THIS IS THE PREFERRED way to source images for a Figma-→-Elementor build. Workflow: 1) Developer/designer uploads clean source images to WP media (bulk upload via WP admin → Media → Add New), 2) Call this tool to get the inventory, 3) Match each Figma layer name to a media item by 'normalised_slug' (kebab-cased filename) or fuzzy substring, 4) Pass the returned attachment_id + url to set-image-widget-src / set-container-background. NEVER export images from Figma — exports bake in overlays, text, and effects.", {
|
|
3785
|
+
search: z.string().optional().describe("Substring to filter by — matches title, filename, normalised_slug. e.g. 'hero' returns 'hero-bg.jpg', 'home-hero.webp'."),
|
|
3786
|
+
mime: z.string().optional().describe("MIME prefix filter (default 'image'). Use '' for all types, 'image/png' to narrow further."),
|
|
3787
|
+
per_page: z.number().optional().describe("Items per page (default 100, max 500)"),
|
|
3788
|
+
page: z.number().optional().describe("Page number for pagination (default 1)"),
|
|
3789
|
+
site: siteParam,
|
|
3790
|
+
}, async ({ search, mime, per_page, page, site }) => {
|
|
3791
|
+
try {
|
|
3792
|
+
const { wpUrl, authHeader } = resolveSite(sites, site);
|
|
3793
|
+
const r = await axios.get(`${wpUrl}/wp-json/erc/v1/media/list`, {
|
|
3794
|
+
headers: { Authorization: authHeader },
|
|
3795
|
+
params: {
|
|
3796
|
+
search: search ?? "",
|
|
3797
|
+
mime: mime ?? "image",
|
|
3798
|
+
per_page: per_page ?? 100,
|
|
3799
|
+
page: page ?? 1,
|
|
3800
|
+
},
|
|
3801
|
+
});
|
|
3802
|
+
return { content: [{ type: "text", text: JSON.stringify(r.data, null, 2) }] };
|
|
3803
|
+
}
|
|
3804
|
+
catch (error) {
|
|
3805
|
+
return { content: [{ type: "text", text: `Error listing media library: ${error.response?.data?.message || error.message}` }], isError: true };
|
|
3806
|
+
}
|
|
3807
|
+
});
|
|
3808
|
+
// ── validate-page-structure ───────────────────────────────────────────────
|
|
3809
|
+
server.tool("validate-page-structure", "Post-build sanity check on a page. Returns a report of structural / standards violations: header widgets in page body (should be a template), images with external URLs missing WP attachment IDs, hardcoded fonts/colors that should use kit globals, legacy _element_width on flex children, missing image alt text, suspicious min-heights. Call this AFTER building a page from Figma. If errors are found, fix them with the typed tools and re-run. If only warnings remain, surface them to the human for review.", {
|
|
3810
|
+
page_id: z.string().describe("WordPress Page ID to validate"),
|
|
3811
|
+
site: siteParam,
|
|
3812
|
+
}, async ({ page_id, site }) => {
|
|
3813
|
+
try {
|
|
3814
|
+
const { wpUrl, authHeader } = resolveSite(sites, site);
|
|
3815
|
+
const r = await axios.get(`${wpUrl}/wp-json/erc/v1/pages/${page_id}/validate-structure`, {
|
|
3816
|
+
headers: { Authorization: authHeader },
|
|
3817
|
+
});
|
|
3818
|
+
return { content: [{ type: "text", text: JSON.stringify(r.data, null, 2) }] };
|
|
3819
|
+
}
|
|
3820
|
+
catch (error) {
|
|
3821
|
+
return { content: [{ type: "text", text: `Error validating page structure: ${error.response?.data?.message || error.message}` }], isError: true };
|
|
3822
|
+
}
|
|
3823
|
+
});
|
|
3535
3824
|
return server;
|
|
3536
3825
|
}
|
|
3537
3826
|
// ─── Entry Point ──────────────────────────────────────────────────────────────
|
package/package.json
CHANGED