@elementor/elementor-v3-mcp 4.1.0-754

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 ADDED
@@ -0,0 +1,1161 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+
20
+ // src/index.ts
21
+ var index_exports = {};
22
+ __export(index_exports, {
23
+ RESOURCE_NAME_ELEMENT_SETTINGS: () => RESOURCE_NAME_ELEMENT_SETTINGS,
24
+ RESOURCE_NAME_PAGE_OVERVIEW: () => RESOURCE_NAME_PAGE_OVERVIEW,
25
+ RESOURCE_NAME_PAGE_SETTINGS: () => RESOURCE_NAME_PAGE_SETTINGS,
26
+ RESOURCE_NAME_WIDGET_CONFIG: () => RESOURCE_NAME_WIDGET_CONFIG,
27
+ RESOURCE_URI_ELEMENT_SETTINGS_TEMPLATE: () => RESOURCE_URI_ELEMENT_SETTINGS_TEMPLATE,
28
+ RESOURCE_URI_PAGE_OVERVIEW: () => RESOURCE_URI_PAGE_OVERVIEW,
29
+ RESOURCE_URI_PAGE_SETTINGS: () => RESOURCE_URI_PAGE_SETTINGS,
30
+ RESOURCE_URI_WIDGET_CONFIG_TEMPLATE: () => RESOURCE_URI_WIDGET_CONFIG_TEMPLATE,
31
+ createElementorServer: () => createElementorServer,
32
+ init: () => init
33
+ });
34
+ module.exports = __toCommonJS(index_exports);
35
+
36
+ // src/elementor-mcp-server.ts
37
+ var import_editor_mcp = require("@elementor/editor-mcp");
38
+ var import_elementor_mcp_common = require("@elementor/elementor-mcp-common");
39
+ var import_mcp2 = require("@modelcontextprotocol/sdk/server/mcp.js");
40
+
41
+ // src/resources.ts
42
+ var import_mcp = require("@modelcontextprotocol/sdk/server/mcp.js");
43
+
44
+ // src/utils.ts
45
+ var getElementor = () => window.elementor;
46
+ var get$e = () => window.$e;
47
+ var getElementorCommon = () => window.elementorCommon;
48
+ function encodeToolJson(data) {
49
+ return JSON.stringify(data).replaceAll('"', "'");
50
+ }
51
+
52
+ // src/context.ts
53
+ function deepMerge(target, source) {
54
+ if (!source || typeof source !== "object") {
55
+ return target;
56
+ }
57
+ if (!target || typeof target !== "object") {
58
+ return source;
59
+ }
60
+ const result = { ...target };
61
+ for (const key of Object.keys(source)) {
62
+ const sourceValue = source[key];
63
+ const targetValue = target[key];
64
+ if (Array.isArray(sourceValue)) {
65
+ result[key] = sourceValue;
66
+ } else if (sourceValue && typeof sourceValue === "object") {
67
+ result[key] = deepMerge(targetValue, sourceValue);
68
+ } else {
69
+ result[key] = sourceValue;
70
+ }
71
+ }
72
+ return result;
73
+ }
74
+ function getPageOverView() {
75
+ const document = getElementor()?.documents?.getCurrent();
76
+ if (!document) {
77
+ return { error: "No active document found" };
78
+ }
79
+ function extractElementData(element) {
80
+ if (!element || !element.model) {
81
+ return null;
82
+ }
83
+ const model = element.model.attributes;
84
+ if (!model) {
85
+ return null;
86
+ }
87
+ const result = {
88
+ id: model.id,
89
+ elType: model.elType,
90
+ widgetType: model.widgetType || void 0
91
+ };
92
+ const title = model.title || element.model.editor_settings?.title;
93
+ if (title) {
94
+ result.title = title;
95
+ }
96
+ if (element.children && element.children.length > 0) {
97
+ result.children = element.children.map((child) => extractElementData(child)).filter((child) => child !== null);
98
+ }
99
+ return result;
100
+ }
101
+ const containers = document.container.children ?? [];
102
+ const elements = containers.map((container) => extractElementData(container));
103
+ return {
104
+ documentId: document.id,
105
+ documentType: document.config?.type ?? "unknown",
106
+ title: document.config?.settings?.post_title || "Untitled",
107
+ elements: elements.filter((el) => el !== null)
108
+ };
109
+ }
110
+ function mapControlsToSchema(controlsData) {
111
+ if (!controlsData || typeof controlsData !== "object") {
112
+ return {};
113
+ }
114
+ return Object.fromEntries(
115
+ Object.entries(controlsData).filter(([, control]) => !["section", "tab", "tabs"].includes(control.type)).map(([controlKey, control]) => {
116
+ let options;
117
+ const controlConfig = getElementor()?.config?.controls?.[control.type] || {};
118
+ const completeConfig = deepMerge(controlConfig, control);
119
+ const returnValue = completeConfig.return_value;
120
+ if (completeConfig.options) {
121
+ options = Object.keys(completeConfig.options);
122
+ }
123
+ let fields;
124
+ if (completeConfig.fields) {
125
+ fields = mapControlsToSchema(completeConfig.fields);
126
+ }
127
+ const returnConfig = {
128
+ type: control.type,
129
+ default: completeConfig.default
130
+ };
131
+ if (options) {
132
+ returnConfig.options = options;
133
+ }
134
+ if (fields) {
135
+ returnConfig.fields = fields;
136
+ }
137
+ if (completeConfig.size_units) {
138
+ returnConfig.size_units = completeConfig.size_units;
139
+ }
140
+ if (returnValue !== void 0) {
141
+ returnConfig.onValue = returnValue;
142
+ }
143
+ return [controlKey, returnConfig];
144
+ })
145
+ );
146
+ }
147
+ function getDocumentSchema(documentId) {
148
+ const document = getElementor()?.documents?.get?.(documentId);
149
+ if (!document) {
150
+ return;
151
+ }
152
+ const controls = document.config?.settings?.controls;
153
+ return mapControlsToSchema(controls);
154
+ }
155
+ function loadDocumentSettings(documentId) {
156
+ const document = getElementor()?.documents?.get?.(documentId);
157
+ if (!document) {
158
+ return;
159
+ }
160
+ const allSettings = document.config?.settings?.settings || {};
161
+ const controls = document.config?.settings?.controls || {};
162
+ const result = {};
163
+ Object.entries(allSettings).forEach(([controlKey, value]) => {
164
+ const control = controls[controlKey];
165
+ if (value !== control?.default) {
166
+ result[controlKey] = value;
167
+ }
168
+ });
169
+ return result;
170
+ }
171
+ function loadDocumentSchema(documentId) {
172
+ return getDocumentSchema(documentId);
173
+ }
174
+
175
+ // src/resources.ts
176
+ var RESOURCE_NAME_ELEMENT_SETTINGS = "elementor-element-settings";
177
+ var RESOURCE_URI_ELEMENT_SETTINGS_TEMPLATE = "elementor://editor/element-settings/{elementId}";
178
+ var RESOURCE_NAME_WIDGET_CONFIG = "elementor-widget-config";
179
+ var RESOURCE_URI_WIDGET_CONFIG_TEMPLATE = "elementor://editor/widget-config/{widgetType}";
180
+ var RESOURCE_NAME_PAGE_OVERVIEW = "elementor-page-overview";
181
+ var RESOURCE_URI_PAGE_OVERVIEW = "elementor://editor/page-overview";
182
+ var RESOURCE_NAME_PAGE_SETTINGS = "elementor-page-settings";
183
+ var RESOURCE_URI_PAGE_SETTINGS = "elementor://editor/page-settings";
184
+ function decodeResourceVariable(value) {
185
+ try {
186
+ let decoded = decodeURIComponent(value).replace(/[·]/g, "");
187
+ decoded = decoded.replace(/^{|}$/g, "");
188
+ return decoded;
189
+ } catch {
190
+ return value;
191
+ }
192
+ }
193
+ async function handleGetWidgetSettings(params) {
194
+ const elementor = getElementor();
195
+ const container = elementor?.getContainer(params.elementId);
196
+ if (!container) {
197
+ throw new Error(`Element with ID ${params.elementId} not found.`);
198
+ }
199
+ const settings = container.settings.attributes || {};
200
+ return {
201
+ content: [{ type: "text", text: encodeToolJson(settings) }]
202
+ };
203
+ }
204
+ async function handleGetWidgetSchema(params) {
205
+ const elementor = getElementor();
206
+ const controls = elementor?.widgetsCache[params.widgetType]?.controls;
207
+ if (!controls) {
208
+ throw new Error(`Widget type ${params.widgetType} not found.`);
209
+ }
210
+ return {
211
+ content: [{ type: "text", text: encodeToolJson(controls) }]
212
+ };
213
+ }
214
+ function addElementorResources(server) {
215
+ server.resource(
216
+ RESOURCE_NAME_PAGE_OVERVIEW,
217
+ RESOURCE_URI_PAGE_OVERVIEW,
218
+ {
219
+ description: "Complete page structure showing all elements, containers, and their hierarchical relationships on the current Elementor page"
220
+ },
221
+ async (uri) => {
222
+ const overview = getPageOverView();
223
+ if (!overview || overview.error) {
224
+ throw new Error(overview?.error || "Failed to retrieve page overview");
225
+ }
226
+ return {
227
+ contents: [
228
+ {
229
+ uri: uri.toString(),
230
+ mimeType: "application/json",
231
+ text: encodeToolJson(overview)
232
+ }
233
+ ]
234
+ };
235
+ }
236
+ );
237
+ server.resource(
238
+ RESOURCE_NAME_PAGE_SETTINGS,
239
+ RESOURCE_URI_PAGE_SETTINGS,
240
+ {
241
+ description: "Page/document settings schema and current values including title, template, margins, padding, and backgrounds"
242
+ },
243
+ async (uri) => {
244
+ const elementor = getElementor();
245
+ const currentDocument = elementor?.documents?.getCurrent();
246
+ if (!currentDocument) {
247
+ throw new Error("No active document found");
248
+ }
249
+ const documentSchema = loadDocumentSchema(currentDocument.id);
250
+ const documentSettings = loadDocumentSettings(currentDocument.id);
251
+ if (!documentSchema || !documentSettings) {
252
+ throw new Error("Failed to retrieve page settings");
253
+ }
254
+ const result = {
255
+ schema: documentSchema,
256
+ currentSettings: documentSettings
257
+ };
258
+ return {
259
+ contents: [
260
+ {
261
+ uri: uri.toString(),
262
+ mimeType: "application/json",
263
+ text: encodeToolJson(result)
264
+ }
265
+ ]
266
+ };
267
+ }
268
+ );
269
+ server.resource(
270
+ RESOURCE_NAME_ELEMENT_SETTINGS,
271
+ new import_mcp.ResourceTemplate(RESOURCE_URI_ELEMENT_SETTINGS_TEMPLATE, {
272
+ list: void 0
273
+ }),
274
+ {
275
+ description: "Complete settings schema and current values for a specific element, including all available configuration options and their current state"
276
+ },
277
+ async (uri, variables) => {
278
+ let elementId = Array.isArray(variables.elementId) ? variables.elementId[0] : variables.elementId;
279
+ if (!elementId) {
280
+ throw new Error("Element ID is required");
281
+ }
282
+ elementId = decodeResourceVariable(elementId);
283
+ const result = await handleGetWidgetSettings({ elementId, action: "get-widget-settings" });
284
+ return {
285
+ contents: [
286
+ {
287
+ uri: uri.toString(),
288
+ mimeType: "text/plain",
289
+ text: result.content[0].text
290
+ }
291
+ ]
292
+ };
293
+ }
294
+ );
295
+ server.resource(
296
+ RESOURCE_NAME_WIDGET_CONFIG,
297
+ new import_mcp.ResourceTemplate(RESOURCE_URI_WIDGET_CONFIG_TEMPLATE, {
298
+ list: void 0
299
+ }),
300
+ {
301
+ description: "Complete configuration schema for a specific widget type, showing all available settings, properties, and their expected formats"
302
+ },
303
+ async (uri, variables) => {
304
+ let widgetType = Array.isArray(variables.widgetType) ? variables.widgetType[0] : variables.widgetType;
305
+ if (!widgetType) {
306
+ throw new Error("Widget type is required");
307
+ }
308
+ widgetType = decodeResourceVariable(widgetType);
309
+ const result = await handleGetWidgetSchema({ widgetType, action: "get-widget-schema" });
310
+ return {
311
+ contents: [
312
+ {
313
+ uri: uri.toString(),
314
+ mimeType: "text/plain",
315
+ text: result.content[0].text
316
+ }
317
+ ]
318
+ };
319
+ }
320
+ );
321
+ }
322
+
323
+ // src/tools/ai-tool.ts
324
+ var import_schema = require("@elementor/schema");
325
+ function addAiTool(server) {
326
+ server.registerTool(
327
+ "ai",
328
+ {
329
+ description: "Manage Elementor AI integration features and interfaces.",
330
+ inputSchema: {
331
+ action: import_schema.z.enum(["open-brand-voice", "open-choose-element", "open-text-to-elementor"]).describe("The AI operation to perform")
332
+ },
333
+ annotations: {
334
+ title: "Manage AI Integration"
335
+ }
336
+ },
337
+ async (params) => {
338
+ switch (params.action) {
339
+ case "open-brand-voice":
340
+ return await handleOpenBrandVoice();
341
+ case "open-choose-element":
342
+ return await handleOpenChooseElement();
343
+ case "open-text-to-elementor":
344
+ return await handleOpenTextToElementor();
345
+ default:
346
+ throw new Error(`Unknown action: ${params.action}`);
347
+ }
348
+ }
349
+ );
350
+ }
351
+ async function handleOpenBrandVoice() {
352
+ await get$e()?.run("ai-integration/open-brand-voice");
353
+ return {
354
+ content: [{ type: "text", text: "Brand Voice interface opened." }]
355
+ };
356
+ }
357
+ async function handleOpenChooseElement() {
358
+ await get$e()?.run("ai-integration/open-choose-element");
359
+ return {
360
+ content: [{ type: "text", text: "Choose Element interface opened." }]
361
+ };
362
+ }
363
+ async function handleOpenTextToElementor() {
364
+ await get$e()?.run("ai-integration/open-text-to-elementor");
365
+ return {
366
+ content: [{ type: "text", text: "Text to Elementor interface opened." }]
367
+ };
368
+ }
369
+
370
+ // src/tools/dynamic-tool.ts
371
+ var import_schema2 = require("@elementor/schema");
372
+
373
+ // src/validation-helpers.ts
374
+ function validateDocumentSettingsUpdated(container) {
375
+ const updatedSettings = container.settings.attributes;
376
+ if (!updatedSettings) {
377
+ throw new Error("Document settings update failed: Settings not accessible");
378
+ }
379
+ }
380
+ function validateDynamicTagEnabled(container, controlName) {
381
+ const modelSettings = container.model?.attributes?.settings;
382
+ const controlValue = modelSettings?.[controlName];
383
+ const hasDynamic = controlValue && typeof controlValue === "object" && "__dynamic__" in controlValue;
384
+ if (!hasDynamic) {
385
+ throw new Error(`Dynamic tag enable failed: Tag not found for ${controlName}`);
386
+ }
387
+ }
388
+ function validateDynamicTagDisabled(container, controlName) {
389
+ const modelSettings = container.model?.attributes?.settings;
390
+ const controlValue = modelSettings?.[controlName];
391
+ const stillHasDynamic = controlValue && typeof controlValue === "object" && "__dynamic__" in controlValue;
392
+ if (stillHasDynamic) {
393
+ throw new Error(`Dynamic tag disable failed: Tag still exists for ${controlName}`);
394
+ }
395
+ }
396
+
397
+ // src/tools/dynamic-tool.ts
398
+ function addDynamicTool(server) {
399
+ server.registerTool(
400
+ "dynamic",
401
+ {
402
+ description: "Manage dynamic-tags content for Elementor elements including getting dynamic settings, enabling and disabling dynamic tags.",
403
+ inputSchema: {
404
+ action: import_schema2.z.enum(["get-settings", "enable", "disable"]).describe("The dynamic content operation to perform"),
405
+ elementId: import_schema2.z.string().describe("The ID of the element to modify"),
406
+ controlName: import_schema2.z.string().describe("The name of the control/setting to make dynamic"),
407
+ dynamicName: import_schema2.z.string().optional().describe(
408
+ "The name of the dynamic tag to enable. Required for enable action. Output of get-settings action."
409
+ ),
410
+ settings: import_schema2.z.object({}).catchall(import_schema2.z.unknown()).optional().describe(
411
+ "The settings to apply to the dynamic tag. Used with enable action. Output of get-settings action."
412
+ ),
413
+ hasRunGetDynamicSettings: import_schema2.z.boolean().optional().describe(
414
+ "Whether the get-settings action has already been run. Must be set to true when using enable action."
415
+ )
416
+ },
417
+ annotations: {
418
+ title: "Manage Dynamic Content"
419
+ }
420
+ },
421
+ async (params) => {
422
+ switch (params.action) {
423
+ case "get-settings":
424
+ return await handleGetDynamicSettings(params);
425
+ case "enable":
426
+ if (params.hasRunGetDynamicSettings !== true) {
427
+ throw new Error(
428
+ "get-dynamic-settings action has not been run. Run it first before using the enable action."
429
+ );
430
+ }
431
+ if (!params.elementId || !params.controlName || !params.dynamicName || !params.settings) {
432
+ throw new Error(
433
+ "elementId, controlName, dynamicName, and settings are required for dynamic enable"
434
+ );
435
+ }
436
+ return await handleDynamicEnable(params);
437
+ case "disable":
438
+ if (!params.elementId || !params.controlName) {
439
+ throw new Error("elementId and controlName are required for dynamic disable");
440
+ }
441
+ return await handleDynamicDisable(params);
442
+ default:
443
+ throw new Error(`Unknown action: ${params.action}`);
444
+ }
445
+ }
446
+ );
447
+ }
448
+ async function handleGetDynamicSettings(params) {
449
+ if (!params.elementId || !params.controlName) {
450
+ throw new Error("elementId and controlName are required for get-settings");
451
+ }
452
+ const elementor = getElementor();
453
+ const container = elementor?.getContainer(params.elementId);
454
+ if (!container) {
455
+ throw new Error(`Element with ID ${params.elementId} not found.`);
456
+ }
457
+ const controls = container.settings.controls;
458
+ const control = controls[params.controlName];
459
+ if (!control) {
460
+ throw new Error(`Control "${params.controlName}" not found on element ${params.elementId}.`);
461
+ }
462
+ if (!control.dynamic?.categories) {
463
+ throw new Error(`Control "${params.controlName}" does not support dynamic content.`);
464
+ }
465
+ const { categories } = control.dynamic;
466
+ const dynamicTags = elementor?.dynamicTags;
467
+ if (!dynamicTags?.getConfig) {
468
+ throw new Error("Dynamic tags API is not available.");
469
+ }
470
+ const tags = dynamicTags.getConfig("tags");
471
+ const relevantTags = Object.values(tags).filter(
472
+ (tag) => tag.categories.find((category) => categories.includes(category))
473
+ );
474
+ return {
475
+ content: [{ type: "text", text: JSON.stringify(relevantTags, null, 2) }]
476
+ };
477
+ }
478
+ async function handleDynamicEnable(params) {
479
+ if (!params.elementId || !params.controlName || !params.dynamicName) {
480
+ throw new Error("elementId, controlName, and dynamicName are required for dynamic enable");
481
+ }
482
+ if (params.hasRunGetDynamicSettings !== true) {
483
+ throw new Error("get-dynamic-settings action has not been run. Run it first before using the enable action.");
484
+ }
485
+ const elementor = getElementor();
486
+ const container = elementor?.getContainer(params.elementId);
487
+ if (!container) {
488
+ throw new Error(`Element with ID ${params.elementId} not found.`);
489
+ }
490
+ const dynamicName = params.dynamicName.toLowerCase().replace(/\s+/g, "-").replace(/_/g, "-").replace(/[^a-z0-9-]/g, "");
491
+ const settings = params.settings || {};
492
+ settings.toJSON = () => settings;
493
+ const elementorCommon = getElementorCommon();
494
+ if (!elementorCommon?.helpers?.getUniqueId) {
495
+ throw new Error("Elementor Common API is not available.");
496
+ }
497
+ const uniqueId = elementorCommon.helpers.getUniqueId();
498
+ const dynamicTags = elementor?.dynamicTags;
499
+ if (!dynamicTags?.tagDataToTagText) {
500
+ throw new Error("Dynamic tags API is not available.");
501
+ }
502
+ const tagText = dynamicTags.tagDataToTagText(String(uniqueId), dynamicName, settings);
503
+ await get$e()?.run("document/dynamic/enable", {
504
+ container,
505
+ settings: { [params.controlName]: tagText }
506
+ });
507
+ validateDynamicTagEnabled(container, params.controlName);
508
+ return {
509
+ content: [
510
+ {
511
+ type: "text",
512
+ text: `Dynamic content enabled for element ${params.elementId}, control "${params.controlName}" with dynamic tag "${dynamicName}": ${tagText}`
513
+ }
514
+ ]
515
+ };
516
+ }
517
+ async function handleDynamicDisable(params) {
518
+ if (!params.elementId || !params.controlName) {
519
+ throw new Error("elementId and controlName are required for dynamic disable");
520
+ }
521
+ const container = getElementor()?.getContainer(params.elementId);
522
+ if (!container) {
523
+ throw new Error(`Element with ID ${params.elementId} not found.`);
524
+ }
525
+ const getElementDynamicSetting = (elementContainer) => {
526
+ const modelSettings = elementContainer.model?.attributes?.settings || {};
527
+ const dynamicContent = {};
528
+ Object.keys(modelSettings).forEach((key) => {
529
+ const value = modelSettings[key];
530
+ let dynamicData = null;
531
+ if (value && typeof value === "object" && value.__dynamic__) {
532
+ dynamicData = value.__dynamic__;
533
+ }
534
+ if (dynamicData) {
535
+ dynamicContent[key] = dynamicData;
536
+ }
537
+ });
538
+ return {
539
+ settingsNames: Object.keys(
540
+ dynamicContent.attributes || {}
541
+ ),
542
+ dynamicContent
543
+ };
544
+ };
545
+ const availableDynamicSettingNames = getElementDynamicSetting(container).settingsNames;
546
+ if (!availableDynamicSettingNames.includes(params.controlName)) {
547
+ throw new Error(
548
+ `Setting "${params.controlName}" on element ${params.elementId} does not have dynamic content enabled. here is the list of dynamic settings available: ${JSON.stringify(
549
+ availableDynamicSettingNames,
550
+ null,
551
+ 2
552
+ )}`
553
+ );
554
+ }
555
+ await get$e()?.run("document/dynamic/disable", {
556
+ container,
557
+ settings: {
558
+ [params.controlName]: ""
559
+ }
560
+ });
561
+ validateDynamicTagDisabled(container, params.controlName);
562
+ return {
563
+ content: [
564
+ {
565
+ type: "text",
566
+ text: `Dynamic content disabled for setting "${params.controlName}" on element ${params.elementId}.`
567
+ }
568
+ ]
569
+ };
570
+ }
571
+
572
+ // src/tools/page-tool.ts
573
+ var import_schema3 = require("@elementor/schema");
574
+ function addPageTool(server) {
575
+ server.registerTool(
576
+ "page",
577
+ {
578
+ description: `Manage page and document operations including undo/redo, saving, page settings, and page information. Use this tool when you need to:
579
+ - Undo or redo changes (history-undo, history-redo, history-undo-all) - Use these when user asks to undo, revert, or redo recent changes
580
+ - Save page changes (save-draft, save-publish, save-update, save-discard)
581
+ - Get or update page settings like page title, description, keywords, styling (get-settings, update-settings)
582
+ - Open or preview pages (open, preview)
583
+ This tool handles document-level operations and change history.`,
584
+ inputSchema: {
585
+ action: import_schema3.z.enum([
586
+ "save-draft",
587
+ "save-publish",
588
+ "save-update",
589
+ "save-discard",
590
+ "history-undo",
591
+ "history-redo",
592
+ "history-undo-all",
593
+ "get-settings",
594
+ "update-settings",
595
+ "open",
596
+ "preview"
597
+ ]).describe(
598
+ 'The page operation to perform: history-undo (revert the last change - use when user says "undo"), history-redo (reapply last undone change - use when user says "redo"), history-undo-all (revert all changes), save-draft (save as draft), save-publish (publish page), save-update (update published page), save-discard (discard unsaved changes), get-settings (retrieve page settings), update-settings (modify page settings), open (open a different page), preview (preview the page)'
599
+ ),
600
+ pageId: import_schema3.z.string().optional().describe("Page/document ID for open action"),
601
+ settings: import_schema3.z.record(import_schema3.z.unknown()).optional().describe(
602
+ 'Settings object containing the specific page settings you want to update. REQUIRED for update-settings action. Only include settings you want to change, not all settings. Example: {"hide_title": "yes", "template": "elementor_canvas"}.'
603
+ )
604
+ },
605
+ annotations: {
606
+ title: "Manage Page"
607
+ },
608
+ _meta: {
609
+ "angie:required-resources": [
610
+ {
611
+ uri: RESOURCE_URI_PAGE_SETTINGS,
612
+ whenToUse: "When updating page settings (action=update-settings) to understand the page schema, available settings, their allowed values, and current page configuration"
613
+ }
614
+ ]
615
+ }
616
+ },
617
+ async (params) => {
618
+ switch (params.action) {
619
+ case "save-draft":
620
+ return await handleSavePageDraft();
621
+ case "save-publish":
622
+ return await handleSavePagePublish();
623
+ case "save-update":
624
+ return await handleSavePageUpdate();
625
+ case "save-discard":
626
+ return await handleSavePageDiscard();
627
+ case "history-undo":
628
+ return await handleHistoryUndo();
629
+ case "history-redo":
630
+ return await handleHistoryRedo();
631
+ case "history-undo-all":
632
+ return await handleHistoryUndoAll();
633
+ case "get-settings":
634
+ return await handleGetDocumentSettings();
635
+ case "update-settings":
636
+ return await handleUpdateDocumentSettings(params);
637
+ case "open":
638
+ return await handleOpenPage(params);
639
+ case "preview":
640
+ return await handlePreviewPage();
641
+ default:
642
+ throw new Error(`Unknown action: ${params.action}`);
643
+ }
644
+ }
645
+ );
646
+ }
647
+ async function handleSavePageDraft() {
648
+ await get$e()?.run("document/save/draft");
649
+ return {
650
+ content: [{ type: "text", text: "Page saved as draft." }]
651
+ };
652
+ }
653
+ async function handleSavePagePublish() {
654
+ await get$e()?.run("document/save/publish");
655
+ return {
656
+ content: [{ type: "text", text: "Page published." }]
657
+ };
658
+ }
659
+ async function handleSavePageUpdate() {
660
+ await get$e()?.run("document/save/update");
661
+ return {
662
+ content: [{ type: "text", text: "Page updated." }]
663
+ };
664
+ }
665
+ async function handleSavePageDiscard() {
666
+ await get$e()?.run("document/save/discard");
667
+ return {
668
+ content: [{ type: "text", text: "Page changes discarded." }]
669
+ };
670
+ }
671
+ async function handleHistoryUndo() {
672
+ await get$e()?.run("document/history/undo");
673
+ return {
674
+ content: [{ type: "text", text: "Undo performed." }]
675
+ };
676
+ }
677
+ async function handleHistoryRedo() {
678
+ await get$e()?.run("document/history/redo");
679
+ return {
680
+ content: [{ type: "text", text: "Redo performed." }]
681
+ };
682
+ }
683
+ async function handleHistoryUndoAll() {
684
+ await get$e()?.run("document/history/undo-all", { document: getElementor()?.documents.getCurrent() });
685
+ return {
686
+ content: [{ type: "text", text: "All changes undone." }]
687
+ };
688
+ }
689
+ async function handleOpenPage(params) {
690
+ if (!params.pageId) {
691
+ throw new Error("pageId is required for open action");
692
+ }
693
+ await get$e()?.run("editor/documents/open", {
694
+ id: params.pageId
695
+ });
696
+ return {
697
+ content: [{ type: "text", text: `Page ${params.pageId} opened.` }]
698
+ };
699
+ }
700
+ async function handlePreviewPage() {
701
+ await get$e()?.run("editor/documents/preview");
702
+ return {
703
+ content: [{ type: "text", text: "Page preview opened." }]
704
+ };
705
+ }
706
+ async function handleGetDocumentSettings() {
707
+ const document = getElementor()?.documents?.getCurrent();
708
+ if (!document) {
709
+ throw new Error("No active document found.");
710
+ }
711
+ const settings = document.config?.settings || {};
712
+ return {
713
+ content: [
714
+ {
715
+ type: "text",
716
+ text: encodeToolJson({
717
+ documentId: document.id,
718
+ documentType: document.config?.type,
719
+ settings
720
+ })
721
+ }
722
+ ]
723
+ };
724
+ }
725
+ async function handleUpdateDocumentSettings(params) {
726
+ const currentDocument = getElementor()?.documents?.getCurrent();
727
+ if (!currentDocument) {
728
+ throw new Error("No active document found.");
729
+ }
730
+ if (!params.settings || typeof params.settings !== "object") {
731
+ throw new Error("settings object is required for update-settings action");
732
+ }
733
+ await get$e()?.run("document/elements/settings", {
734
+ container: currentDocument.container,
735
+ settings: params.settings,
736
+ options: {
737
+ external: true
738
+ }
739
+ });
740
+ const updatedDocument = getElementor()?.documents?.getCurrent();
741
+ if (!updatedDocument?.container) {
742
+ throw new Error("Document container not found after update");
743
+ }
744
+ validateDocumentSettingsUpdated(updatedDocument.container);
745
+ return {
746
+ content: [
747
+ {
748
+ type: "text",
749
+ text: JSON.stringify({
750
+ success: true,
751
+ message: `Document settings updated successfully. Settings: ${JSON.stringify(
752
+ params.settings,
753
+ null,
754
+ 2
755
+ )}`,
756
+ saveChangesSuggestion: 'Suggest the following quick user replies: "Publish Changes", "Save Draft"',
757
+ nextStep: "Page settings updated in editor. User should save the page to persist changes."
758
+ })
759
+ }
760
+ ]
761
+ };
762
+ }
763
+
764
+ // src/tools/routes-tool.ts
765
+ var import_schema4 = require("@elementor/schema");
766
+ function addRoutesTool(server) {
767
+ const $e = get$e();
768
+ const routes = $e?.routes?.getAll?.() || [];
769
+ const components = $e?.components?.getAll?.() || [];
770
+ const availableToOpenComponents = components.filter(
771
+ (component) => $e?.components?.get(component)?.getCommands?.()?.open
772
+ );
773
+ const availableToCloseComponents = components.filter(
774
+ (component) => $e?.components?.get(component)?.getCommands?.()?.close
775
+ );
776
+ server.registerTool(
777
+ "routes",
778
+ {
779
+ description: `Manage Elementor editor routing and navigation. Use this tool to open a component, navigate to a route, go back from a route or close components. Always prefer this tool when user is on the Elementor editor. Available routes to navigate to or back from: ${routes.join(", ")}`,
780
+ inputSchema: {
781
+ action: import_schema4.z.enum(["open", "navigate", "go-back", "close"]),
782
+ route: import_schema4.z.string().optional().describe(
783
+ "The route to navigate to or back from it. Do not send this parameter if you only want to open a component."
784
+ ),
785
+ componentToOpen: import_schema4.z.enum(availableToOpenComponents.length ? availableToOpenComponents : [""]).optional().describe("The component to open or navigate to."),
786
+ componentToClose: import_schema4.z.enum(availableToCloseComponents.length ? availableToCloseComponents : [""]).optional().describe("The component to close.")
787
+ },
788
+ annotations: {
789
+ title: "Manage Routes"
790
+ }
791
+ },
792
+ async (params) => {
793
+ switch (params.action) {
794
+ case "open":
795
+ return await handleOpen(params);
796
+ case "navigate":
797
+ return await handleNavigate(params);
798
+ case "go-back":
799
+ return await handleGoBack(params);
800
+ case "close":
801
+ return await handleClose(params);
802
+ default:
803
+ throw new Error(`Unknown action: ${params.action}`);
804
+ }
805
+ }
806
+ );
807
+ }
808
+ async function handleOpen(params) {
809
+ const $e = get$e();
810
+ const component = params.componentToOpen || params.route;
811
+ const openCommand = $e?.components?.get(component)?.getCommands?.()?.open?.registerConfig.command;
812
+ if (openCommand) {
813
+ await $e?.run(openCommand, {});
814
+ } else {
815
+ throw new Error("Could not open component");
816
+ }
817
+ return {
818
+ content: [{ type: "text", text: `Opened: ${component}` }]
819
+ };
820
+ }
821
+ async function handleNavigate(params) {
822
+ const $e = get$e();
823
+ const route = params.route;
824
+ const componentToOpen = params.componentToOpen;
825
+ const openCommand = $e?.components?.get(componentToOpen)?.getCommands?.()?.open?.registerConfig.command;
826
+ if (openCommand) {
827
+ await $e?.run(openCommand, {});
828
+ }
829
+ const routeComponent = $e?.routes?.getComponent?.(route);
830
+ if (routeComponent) {
831
+ $e?.routes?.saveState?.(routeComponent.getNamespace());
832
+ }
833
+ try {
834
+ $e?.routes?.to?.(route, {});
835
+ } catch {
836
+ const openCommandFallback = $e?.components?.get(route)?.getCommands?.()?.open?.registerConfig.command;
837
+ if (openCommandFallback) {
838
+ await $e?.run(openCommandFallback, {});
839
+ } else {
840
+ throw new Error("Could not navigate to route");
841
+ }
842
+ }
843
+ return {
844
+ content: [{ type: "text", text: `Navigated to: ${route}` }]
845
+ };
846
+ }
847
+ async function handleGoBack(params) {
848
+ const $e = get$e();
849
+ const route = params.route;
850
+ const component = $e?.routes?.getComponent?.(route);
851
+ if (component) {
852
+ $e?.routes?.back?.(component.getNamespace());
853
+ }
854
+ return {
855
+ content: [{ type: "text", text: `Go back to: ${route}` }]
856
+ };
857
+ }
858
+ async function handleClose(params) {
859
+ const $e = get$e();
860
+ const component = params.componentToClose;
861
+ const closeCommand = $e?.components?.get(component)?.getCommands?.()?.close?.registerConfig.command;
862
+ if (closeCommand) {
863
+ await $e?.run(closeCommand, {});
864
+ } else {
865
+ throw new Error("Could not close component");
866
+ }
867
+ return {
868
+ content: [{ type: "text", text: `Closed: ${component}` }]
869
+ };
870
+ }
871
+
872
+ // src/tools/styling-tool.ts
873
+ var import_schema5 = require("@elementor/schema");
874
+ var import_types = require("@modelcontextprotocol/sdk/types.js");
875
+ function addStylingTool(server) {
876
+ server.registerTool(
877
+ "styling",
878
+ {
879
+ description: `This tool provides AI-powered custom CSS styling for Elementor elements. Use this when users want advanced styling that goes beyond Elementor capabilities and can't be targeted using the element settings.
880
+
881
+ **When to use this tool:**
882
+ - Visual effects: shadows, filters, pseudo-elements, advanced selectors
883
+ - Complex animations with custom keyframes or CSS transitions
884
+ - Styling that requires media queries or complex CSS rules
885
+ - Click-triggered effects or other non-motion triggers
886
+ - Custom hover effects that don't involve motion (color changes, opacity, etc.)
887
+
888
+ **When NOT to use this tool:**
889
+ - Basic styling achievable through Elementor settings (colors, typography, spacing, borders, simple hover effects) -> use the elementor__elements with "update-settings" action.
890
+
891
+ **Do NOT use this tool if the user mentions motion effects with supported triggers:**
892
+ - "on hover" with motion (movement, rotation, scaling) \u2192 use motion-effects tool
893
+ - "on scroll" with motion effects \u2192 use motion-effects tool
894
+ - "mouse move" / "follow mouse" \u2192 use motion-effects tool
895
+ - "entrance" / "fade in" / "slide in" animations \u2192 use motion-effects tool
896
+
897
+ **Actions available:**
898
+ - **custom-css**: Generate and apply AI-powered custom CSS to elements
899
+
900
+ This tool generates CSS code using AI, provides preview functionality, and handles user approval workflow for applying custom styles.`,
901
+ inputSchema: {
902
+ action: import_schema5.z.enum(["custom-css"]).describe(
903
+ "The styling operation to perform. Currently supports custom-css for AI-generated CSS styling."
904
+ ),
905
+ elementId: import_schema5.z.string().describe(
906
+ "The ID of the Elementor element to apply custom styling to. This element will receive the generated CSS code."
907
+ ),
908
+ prompt: import_schema5.z.string().describe(
909
+ "A detailed description of the desired styling. Include specific visual requirements, colors, effects, layout modifications, or any custom styling needs. The more detailed the prompt, the better the generated CSS will match your requirements."
910
+ )
911
+ },
912
+ annotations: {
913
+ title: "Apply Custom Styling"
914
+ }
915
+ },
916
+ async (params) => {
917
+ switch (params.action) {
918
+ case "custom-css":
919
+ return await handleCustomCss(params.elementId, params.prompt, server);
920
+ default:
921
+ throw new Error(`Unknown action: ${params.action}`);
922
+ }
923
+ }
924
+ );
925
+ }
926
+ async function handleCustomCss(elementId, prompt, server) {
927
+ const container = getElementor()?.getContainer(elementId);
928
+ if (!container) {
929
+ throw new Error(`Element with ID ${elementId} not found.`);
930
+ }
931
+ const htmlMarkup = container.view?.el?.outerHTML || "";
932
+ const parseCSS = (css) => {
933
+ return css && css.replace(/`/g, "").replace(/^css\s*/i, "");
934
+ };
935
+ const samplingCssResult = await server.server.request(
936
+ {
937
+ method: "sampling/createMessage",
938
+ params: {
939
+ messages: [
940
+ {
941
+ role: "user",
942
+ content: {
943
+ type: "text",
944
+ text: prompt
945
+ }
946
+ }
947
+ ],
948
+ maxTokens: 1e3,
949
+ modelPreferences: {
950
+ hints: [
951
+ {
952
+ name: "elementor-css"
953
+ }
954
+ ]
955
+ },
956
+ metadata: {
957
+ element_id: elementId,
958
+ html_markup: htmlMarkup
959
+ }
960
+ }
961
+ },
962
+ import_types.SamplingMessageSchema
963
+ );
964
+ const content = samplingCssResult?.content;
965
+ const block = Array.isArray(content) ? content.find((b) => b.type === "text") : content;
966
+ const cssText = block?.type === "text" ? block.text : void 0;
967
+ if (!cssText) {
968
+ throw new Error("Failed to generate CSS: No text content received from API.");
969
+ }
970
+ const parsedCssString = parseCSS(cssText);
971
+ return {
972
+ content: [
973
+ {
974
+ type: "text",
975
+ text: JSON.stringify({
976
+ success: true,
977
+ message: "Custom CSS generated. The CSS needs to be applied through the editor.",
978
+ generatedCss: parsedCssString,
979
+ elementId
980
+ })
981
+ }
982
+ ]
983
+ };
984
+ }
985
+
986
+ // src/tools/ui-tool.ts
987
+ var import_schema6 = require("@elementor/schema");
988
+ function addUiTool(server) {
989
+ server.registerTool(
990
+ "ui",
991
+ {
992
+ description: "Manage Elementor editor UI operations. This tool provides control over editor interface actions including responsive preview modes and widget favorites. Use this when you need to: switch between device preview modes (desktop/tablet/mobile), manage favorite widgets, or paste previously copied content. Note: For undo/redo operations, use the page tool instead. The tool interacts directly with the Elementor editor UI and does not modify page content itself.",
993
+ inputSchema: {
994
+ action: import_schema6.z.enum(["change-device-mode", "toggle-favorite", "ui-paste"]).describe(
995
+ "The UI operation to perform: change-device-mode (switch responsive preview), toggle-favorite (add/remove widget from favorites), ui-paste (paste clipboard content into an existing element)"
996
+ ),
997
+ deviceMode: import_schema6.z.enum(["desktop", "tablet", "mobile"]).optional().describe("Required for change-device-mode. The device mode to switch to for responsive preview"),
998
+ widgetType: import_schema6.z.string().optional().describe(
999
+ 'Required for toggle-favorite. The widget type name (e.g., "heading", "button", "image") to add or remove from favorites'
1000
+ ),
1001
+ elementId: import_schema6.z.string().optional().describe(
1002
+ "Required for ui-paste. The ID of an existing container element where clipboard content should be pasted. Note: The element must exist and content must be in clipboard first"
1003
+ )
1004
+ },
1005
+ annotations: {
1006
+ title: "Manage UI"
1007
+ }
1008
+ },
1009
+ async (params) => {
1010
+ switch (params.action) {
1011
+ case "change-device-mode":
1012
+ return await handleChangeDeviceMode(params);
1013
+ case "toggle-favorite":
1014
+ return await handleToggleFavorite(params);
1015
+ case "ui-paste":
1016
+ return await handleUiPaste(params);
1017
+ default:
1018
+ throw new Error(`Unknown action: ${params.action}`);
1019
+ }
1020
+ }
1021
+ );
1022
+ }
1023
+ async function handleChangeDeviceMode(params) {
1024
+ if (!params.deviceMode) {
1025
+ throw new Error("deviceMode is required for change-device-mode action");
1026
+ }
1027
+ get$e()?.run("panel/change-device-mode", {
1028
+ device: params.deviceMode
1029
+ });
1030
+ return {
1031
+ content: [{ type: "text", text: `Device mode changed to ${params.deviceMode}.` }]
1032
+ };
1033
+ }
1034
+ async function handleToggleFavorite(params) {
1035
+ if (!params.widgetType) {
1036
+ throw new Error("widgetType is required for toggle-favorite action");
1037
+ }
1038
+ get$e()?.run("favorites/toggle", {
1039
+ name: params.widgetType
1040
+ });
1041
+ return {
1042
+ content: [{ type: "text", text: `Favorite status toggled for ${params.widgetType}.` }]
1043
+ };
1044
+ }
1045
+ async function handleUiPaste(params) {
1046
+ if (!params.elementId) {
1047
+ throw new Error("elementId is required for ui-paste action");
1048
+ }
1049
+ const container = getElementor()?.getContainer(params.elementId);
1050
+ if (!container) {
1051
+ throw new Error(`Element with ID ${params.elementId} not found.`);
1052
+ }
1053
+ get$e()?.run("document/ui/paste", {
1054
+ container
1055
+ });
1056
+ return {
1057
+ content: [{ type: "text", text: `UI paste performed on element ${params.elementId}.` }]
1058
+ };
1059
+ }
1060
+
1061
+ // src/elementor-mcp-server.ts
1062
+ var SERVER_INSTRUCTIONS = `## Elementor Page Builder
1063
+
1064
+ ### Capabilities:
1065
+ **Page Management:**
1066
+ - Manage page settings, saving and routing pages
1067
+ - Control the editor UI, including switching between desktop, tablet, and mobile views
1068
+
1069
+ **Global Styles:**
1070
+ - Work with global styles, helping manage shared design settings like colors and fonts across the site
1071
+
1072
+ **AI-Powered Content Creation:**
1073
+ - Generate and edit text and insert it into the page
1074
+ - Generate images and place them on the canvas
1075
+
1076
+ **Custom Styling & Code:**
1077
+ - Apply custom CSS to elements
1078
+ - Generate supported code snippets
1079
+
1080
+ ### Limitations:
1081
+ **Element Management (Not Supported):**
1082
+ - Cannot create or edit individual Elementor elements such as widgets or containers
1083
+ - Cannot build page layouts or create containers
1084
+ - Cannot modify widget-level settings
1085
+ - Cannot apply motion effects
1086
+ - Cannot reorder sections or perform detailed canvas-level edits
1087
+ - Cannot create fully designed or polished pages
1088
+ - Cannot fully resolve responsiveness issues
1089
+ - Support for these editor-level capabilities is planned for Editor V4
1090
+
1091
+ **Theme Builder:**
1092
+ - Cannot create or manage Theme Builder templates, including headers, footers, single posts, archives, products, loop items, or 404 pages
1093
+ - Cannot set display conditions for templates
1094
+ - Cannot configure popup triggers and advanced rules
1095
+
1096
+ **System Settings:**
1097
+ - Cannot change Elementor system-level settings
1098
+ - Cannot activate or work with Editor V4
1099
+ - Cannot manage form submissions
1100
+ - Cannot add custom fonts or icons
1101
+ - Cannot manage user roles
1102
+ - Cannot roll back Elementor versions
1103
+ - Cannot place the site in maintenance mode
1104
+ - Cannot export the website
1105
+ - Cannot apply full website templates
1106
+
1107
+ **Code & Widgets:**
1108
+ - Cannot register PHP code or create new custom widgets, though Angie may provide guidance, code snippets, or plugin suggestions where helpful
1109
+
1110
+ **Note**: While page names can include terms like "header" or "footer", these won't function as actual theme parts without Theme Builder access.
1111
+
1112
+ **Important**: When users ask "What can Angie do?" or similar questions about Angie's general capabilities, use the \`what-can-angie-do\` tool from the knowledge MCP server instead of generating your own response.`;
1113
+ var VERSION = "2.0.0";
1114
+ async function createElementorServer() {
1115
+ await (0, import_elementor_mcp_common.waitForElementorEditor)();
1116
+ const server = new import_mcp2.McpServer(
1117
+ {
1118
+ name: "elementor-server",
1119
+ version: VERSION,
1120
+ title: "Elementor"
1121
+ },
1122
+ {
1123
+ instructions: SERVER_INSTRUCTIONS,
1124
+ capabilities: {
1125
+ resources: {
1126
+ subscribe: true
1127
+ }
1128
+ }
1129
+ }
1130
+ );
1131
+ addElementorResources(server);
1132
+ addPageTool(server);
1133
+ addUiTool(server);
1134
+ addDynamicTool(server);
1135
+ addRoutesTool(server);
1136
+ addAiTool(server);
1137
+ addStylingTool(server);
1138
+ const sdk = (0, import_editor_mcp.getAngieSdk)();
1139
+ await sdk.waitForReady();
1140
+ sdk.registerLocalServer({ server, version: VERSION, description: SERVER_INSTRUCTIONS, name: "Elementor" });
1141
+ return server;
1142
+ }
1143
+
1144
+ // src/init.ts
1145
+ function init() {
1146
+ createElementorServer();
1147
+ }
1148
+ // Annotate the CommonJS export names for ESM import in node:
1149
+ 0 && (module.exports = {
1150
+ RESOURCE_NAME_ELEMENT_SETTINGS,
1151
+ RESOURCE_NAME_PAGE_OVERVIEW,
1152
+ RESOURCE_NAME_PAGE_SETTINGS,
1153
+ RESOURCE_NAME_WIDGET_CONFIG,
1154
+ RESOURCE_URI_ELEMENT_SETTINGS_TEMPLATE,
1155
+ RESOURCE_URI_PAGE_OVERVIEW,
1156
+ RESOURCE_URI_PAGE_SETTINGS,
1157
+ RESOURCE_URI_WIDGET_CONFIG_TEMPLATE,
1158
+ createElementorServer,
1159
+ init
1160
+ });
1161
+ //# sourceMappingURL=index.js.map