@sweepypanda/wp-elementor-mcp 1.7.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.
- package/LICENSE +21 -0
- package/README.md +381 -0
- package/client-config.json +13 -0
- package/dist/elementor-handler.d.ts +51 -0
- package/dist/elementor-handler.d.ts.map +1 -0
- package/dist/elementor-handler.js +358 -0
- package/dist/elementor-handler.js.map +1 -0
- package/dist/helpers.d.ts +24 -0
- package/dist/helpers.d.ts.map +1 -0
- package/dist/helpers.js +107 -0
- package/dist/helpers.js.map +1 -0
- package/dist/index-backup.d.ts +3 -0
- package/dist/index-backup.d.ts.map +1 -0
- package/dist/index-backup.js +3752 -0
- package/dist/index-backup.js.map +1 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +187 -0
- package/dist/index.js.map +1 -0
- package/dist/server-config.d.ts +80 -0
- package/dist/server-config.d.ts.map +1 -0
- package/dist/server-config.js +137 -0
- package/dist/server-config.js.map +1 -0
- package/dist/tool-handlers.d.ts +44 -0
- package/dist/tool-handlers.d.ts.map +1 -0
- package/dist/tool-handlers.js +1682 -0
- package/dist/tool-handlers.js.map +1 -0
- package/dist/tool-schemas.d.ts +859 -0
- package/dist/tool-schemas.d.ts.map +1 -0
- package/dist/tool-schemas.js +870 -0
- package/dist/tool-schemas.js.map +1 -0
- package/dist/types.d.ts +19 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +2 -0
- package/dist/types.js.map +1 -0
- package/dist/utils.d.ts +10 -0
- package/dist/utils.d.ts.map +1 -0
- package/dist/utils.js +142 -0
- package/dist/utils.js.map +1 -0
- package/dist/wordpress-client.d.ts +21 -0
- package/dist/wordpress-client.d.ts.map +1 -0
- package/dist/wordpress-client.js +151 -0
- package/dist/wordpress-client.js.map +1 -0
- package/package.json +74 -0
|
@@ -0,0 +1,3752 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// Load environment variables from .env file
|
|
3
|
+
import { config } from 'dotenv';
|
|
4
|
+
import { dirname, join } from 'path';
|
|
5
|
+
import { fileURLToPath } from 'url';
|
|
6
|
+
// Get the directory of this script for .env file path
|
|
7
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
8
|
+
const __dirname = dirname(__filename);
|
|
9
|
+
const envPath = join(__dirname, '..', '.env');
|
|
10
|
+
config({ path: envPath });
|
|
11
|
+
import { Server } from '@modelcontextprotocol/sdk/server/index.js';
|
|
12
|
+
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
|
|
13
|
+
import { CallToolRequestSchema, ListResourcesRequestSchema, ListToolsRequestSchema, McpError, ReadResourceRequestSchema, InitializeRequestSchema, } from '@modelcontextprotocol/sdk/types.js';
|
|
14
|
+
import axios from 'axios';
|
|
15
|
+
import FormData from 'form-data';
|
|
16
|
+
import https from 'https';
|
|
17
|
+
import { getServerConfig } from './server-config.js';
|
|
18
|
+
class ElementorWordPressMCP {
|
|
19
|
+
server;
|
|
20
|
+
axiosInstance = null;
|
|
21
|
+
config = null;
|
|
22
|
+
serverConfig;
|
|
23
|
+
constructor() {
|
|
24
|
+
this.serverConfig = getServerConfig();
|
|
25
|
+
this.server = new Server({
|
|
26
|
+
name: 'elementor-wordpress-mcp',
|
|
27
|
+
version: '1.7.1',
|
|
28
|
+
});
|
|
29
|
+
// Initialize WordPress configuration from environment variables
|
|
30
|
+
this.initializeFromEnvironment();
|
|
31
|
+
this.setupToolHandlers();
|
|
32
|
+
this.setupResourceHandlers();
|
|
33
|
+
}
|
|
34
|
+
// Robust JSON parsing utility for Elementor data
|
|
35
|
+
parseElementorResponse(responseText) {
|
|
36
|
+
try {
|
|
37
|
+
// Check if this is a direct API response
|
|
38
|
+
if (responseText.trim().startsWith('[') || responseText.trim().startsWith('{')) {
|
|
39
|
+
try {
|
|
40
|
+
const data = JSON.parse(responseText);
|
|
41
|
+
return {
|
|
42
|
+
success: true,
|
|
43
|
+
data: Array.isArray(data) ? data : [data]
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
catch (directParseError) {
|
|
47
|
+
return {
|
|
48
|
+
success: false,
|
|
49
|
+
error: `Direct JSON parse failed: ${directParseError}`,
|
|
50
|
+
rawData: responseText
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
// Extract debug info and JSON data from formatted response
|
|
55
|
+
let debugInfo = '';
|
|
56
|
+
let jsonData = '';
|
|
57
|
+
if (responseText.includes('--- Elementor Data ---')) {
|
|
58
|
+
const parts = responseText.split('--- Elementor Data ---');
|
|
59
|
+
debugInfo = parts[0]?.trim() || '';
|
|
60
|
+
jsonData = parts[1]?.trim() || '';
|
|
61
|
+
}
|
|
62
|
+
else if (responseText.includes('--- Raw Elementor Data ---')) {
|
|
63
|
+
const parts = responseText.split('--- Raw Elementor Data ---');
|
|
64
|
+
debugInfo = parts[0]?.trim() || '';
|
|
65
|
+
jsonData = parts[1]?.trim() || '';
|
|
66
|
+
}
|
|
67
|
+
else {
|
|
68
|
+
// Try to find JSON-like content
|
|
69
|
+
const jsonMatch = responseText.match(/(\[[\s\S]*\]|\{[\s\S]*\})/);
|
|
70
|
+
if (jsonMatch) {
|
|
71
|
+
jsonData = jsonMatch[1];
|
|
72
|
+
debugInfo = responseText.replace(jsonMatch[1], '').trim();
|
|
73
|
+
}
|
|
74
|
+
else {
|
|
75
|
+
return {
|
|
76
|
+
success: false,
|
|
77
|
+
error: 'No JSON data found in response',
|
|
78
|
+
rawData: responseText,
|
|
79
|
+
debugInfo: responseText
|
|
80
|
+
};
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
// Validate that we have data to parse
|
|
84
|
+
if (!jsonData || jsonData.trim() === '') {
|
|
85
|
+
return {
|
|
86
|
+
success: false,
|
|
87
|
+
error: 'Empty JSON data in response',
|
|
88
|
+
rawData: responseText,
|
|
89
|
+
debugInfo
|
|
90
|
+
};
|
|
91
|
+
}
|
|
92
|
+
// Check for known error messages in debug info
|
|
93
|
+
if (debugInfo.includes('No Elementor data found') ||
|
|
94
|
+
debugInfo.includes('does not use Elementor builder') ||
|
|
95
|
+
debugInfo.includes('failed to parse JSON')) {
|
|
96
|
+
return {
|
|
97
|
+
success: false,
|
|
98
|
+
error: 'No valid Elementor data available',
|
|
99
|
+
debugInfo,
|
|
100
|
+
rawData: jsonData
|
|
101
|
+
};
|
|
102
|
+
}
|
|
103
|
+
// Attempt to parse the JSON data
|
|
104
|
+
try {
|
|
105
|
+
const parsedData = JSON.parse(jsonData);
|
|
106
|
+
// Ensure we have an array
|
|
107
|
+
const dataArray = Array.isArray(parsedData) ? parsedData : [parsedData];
|
|
108
|
+
return {
|
|
109
|
+
success: true,
|
|
110
|
+
data: dataArray,
|
|
111
|
+
debugInfo
|
|
112
|
+
};
|
|
113
|
+
}
|
|
114
|
+
catch (parseError) {
|
|
115
|
+
// Try to clean the JSON and parse again
|
|
116
|
+
try {
|
|
117
|
+
// Remove common JSON formatting issues
|
|
118
|
+
const cleanedJson = jsonData
|
|
119
|
+
.replace(/^```json\s*/, '') // Remove markdown code blocks
|
|
120
|
+
.replace(/\s*```$/, '')
|
|
121
|
+
.replace(/^```\s*/, '')
|
|
122
|
+
.replace(/\n\s*\n/g, '\n') // Remove extra newlines
|
|
123
|
+
.trim();
|
|
124
|
+
const parsedData = JSON.parse(cleanedJson);
|
|
125
|
+
const dataArray = Array.isArray(parsedData) ? parsedData : [parsedData];
|
|
126
|
+
return {
|
|
127
|
+
success: true,
|
|
128
|
+
data: dataArray,
|
|
129
|
+
debugInfo
|
|
130
|
+
};
|
|
131
|
+
}
|
|
132
|
+
catch (cleanedParseError) {
|
|
133
|
+
return {
|
|
134
|
+
success: false,
|
|
135
|
+
error: `JSON parse failed: ${parseError.message}. Cleaned parse also failed: ${cleanedParseError}`,
|
|
136
|
+
rawData: jsonData,
|
|
137
|
+
debugInfo
|
|
138
|
+
};
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
catch (error) {
|
|
143
|
+
return {
|
|
144
|
+
success: false,
|
|
145
|
+
error: `Parsing utility failed: ${error.message}`,
|
|
146
|
+
rawData: responseText
|
|
147
|
+
};
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
// Safe method to get parsed Elementor data with comprehensive error handling
|
|
151
|
+
async safeGetElementorData(postId) {
|
|
152
|
+
try {
|
|
153
|
+
const response = await this.getElementorData({ post_id: postId });
|
|
154
|
+
// Parse the JSON response from content[0].text
|
|
155
|
+
const responseText = response.content[0].text;
|
|
156
|
+
const parsedResponse = JSON.parse(responseText);
|
|
157
|
+
// Check if response indicates success and has data
|
|
158
|
+
if (parsedResponse.status === 'success' && parsedResponse.data?.elementor_data) {
|
|
159
|
+
return {
|
|
160
|
+
success: true,
|
|
161
|
+
data: parsedResponse.data.elementor_data,
|
|
162
|
+
debugInfo: `Successfully retrieved ${parsedResponse.data.elementor_data.length} elements for post ${postId}`
|
|
163
|
+
};
|
|
164
|
+
}
|
|
165
|
+
else if (parsedResponse.status === 'error') {
|
|
166
|
+
return {
|
|
167
|
+
success: false,
|
|
168
|
+
error: parsedResponse.data?.message || parsedResponse.message || 'Unknown error from getElementorData',
|
|
169
|
+
debugInfo: parsedResponse.data?.details || parsedResponse.details || ''
|
|
170
|
+
};
|
|
171
|
+
}
|
|
172
|
+
else {
|
|
173
|
+
return {
|
|
174
|
+
success: false,
|
|
175
|
+
error: 'Unexpected response format from getElementorData',
|
|
176
|
+
debugInfo: `Response status: ${parsedResponse.status}`
|
|
177
|
+
};
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
catch (error) {
|
|
181
|
+
return {
|
|
182
|
+
success: false,
|
|
183
|
+
error: `Failed to retrieve Elementor data: ${error.message}`
|
|
184
|
+
};
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
initializeFromEnvironment() {
|
|
188
|
+
const baseUrl = process.env.WORDPRESS_BASE_URL;
|
|
189
|
+
const username = process.env.WORDPRESS_USERNAME;
|
|
190
|
+
const applicationPassword = process.env.WORDPRESS_APPLICATION_PASSWORD;
|
|
191
|
+
if (baseUrl && username && applicationPassword) {
|
|
192
|
+
console.error('Initializing WordPress connection from environment variables...');
|
|
193
|
+
this.setupAxios({
|
|
194
|
+
baseUrl: baseUrl.replace(/\/$/, ''), // Remove trailing slash if present
|
|
195
|
+
username,
|
|
196
|
+
applicationPassword
|
|
197
|
+
});
|
|
198
|
+
console.error('WordPress connection configured successfully');
|
|
199
|
+
}
|
|
200
|
+
else {
|
|
201
|
+
console.error('WordPress environment variables not found. Manual configuration will be required.');
|
|
202
|
+
console.error('Required environment variables:');
|
|
203
|
+
console.error('- WORDPRESS_BASE_URL');
|
|
204
|
+
console.error('- WORDPRESS_USERNAME');
|
|
205
|
+
console.error('- WORDPRESS_APPLICATION_PASSWORD');
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
setupAxios(config) {
|
|
209
|
+
const baseURL = config.baseUrl.endsWith('/')
|
|
210
|
+
? `${config.baseUrl}wp-json/wp/v2/`
|
|
211
|
+
: `${config.baseUrl}/wp-json/wp/v2/`;
|
|
212
|
+
const auth = Buffer.from(`${config.username}:${config.applicationPassword}`).toString('base64');
|
|
213
|
+
const httpsAgent = new https.Agent({
|
|
214
|
+
rejectUnauthorized: this.shouldRejectUnauthorized(config.baseUrl)
|
|
215
|
+
});
|
|
216
|
+
this.axiosInstance = axios.create({
|
|
217
|
+
baseURL,
|
|
218
|
+
headers: {
|
|
219
|
+
'Authorization': `Basic ${auth}`,
|
|
220
|
+
'Content-Type': 'application/json',
|
|
221
|
+
},
|
|
222
|
+
httpsAgent: httpsAgent,
|
|
223
|
+
timeout: 60000, // Increased to 60 second timeout for large operations
|
|
224
|
+
maxContentLength: 50 * 1024 * 1024, // 50MB response limit
|
|
225
|
+
maxBodyLength: 10 * 1024 * 1024, // 10MB request limit
|
|
226
|
+
});
|
|
227
|
+
// Add request interceptor for debugging
|
|
228
|
+
this.axiosInstance.interceptors.request.use((config) => {
|
|
229
|
+
console.error(`Making request to: ${config.method?.toUpperCase()} ${config.url}`);
|
|
230
|
+
if (config.data && typeof config.data === 'string' && config.data.length > 1000) {
|
|
231
|
+
console.error(`Request data size: ${config.data.length} characters`);
|
|
232
|
+
}
|
|
233
|
+
return config;
|
|
234
|
+
}, (error) => {
|
|
235
|
+
console.error(`Request error: ${error.message}`);
|
|
236
|
+
return Promise.reject(error);
|
|
237
|
+
});
|
|
238
|
+
// Add response interceptor for debugging and error handling
|
|
239
|
+
this.axiosInstance.interceptors.response.use((response) => {
|
|
240
|
+
console.error(`Response received: ${response.status} ${response.statusText}`);
|
|
241
|
+
if (response.data && typeof response.data === 'string' && response.data.length > 10000) {
|
|
242
|
+
console.error(`Response data size: ${response.data.length} characters`);
|
|
243
|
+
}
|
|
244
|
+
else if (response.data && Array.isArray(response.data)) {
|
|
245
|
+
console.error(`Response array length: ${response.data.length} items`);
|
|
246
|
+
}
|
|
247
|
+
return response;
|
|
248
|
+
}, (error) => {
|
|
249
|
+
// Enhanced error logging
|
|
250
|
+
if (error.code === 'ECONNABORTED') {
|
|
251
|
+
console.error(`🕐 Request timeout: ${error.message}`);
|
|
252
|
+
}
|
|
253
|
+
else if (error.response?.status) {
|
|
254
|
+
console.error(`❌ HTTP Error: ${error.response.status} ${error.response.statusText}`);
|
|
255
|
+
console.error(`URL: ${error.config?.url}`);
|
|
256
|
+
if (error.response.data?.message) {
|
|
257
|
+
console.error(`WordPress Error: ${error.response.data.message}`);
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
else {
|
|
261
|
+
console.error(`🔥 Network/Connection Error: ${error.message}`);
|
|
262
|
+
}
|
|
263
|
+
return Promise.reject(error);
|
|
264
|
+
});
|
|
265
|
+
this.config = config;
|
|
266
|
+
}
|
|
267
|
+
shouldRejectUnauthorized(baseUrl) {
|
|
268
|
+
try {
|
|
269
|
+
const url = new URL(baseUrl);
|
|
270
|
+
// Allow self-signed certificates for local development
|
|
271
|
+
const isLocal = url.hostname === 'localhost' ||
|
|
272
|
+
url.hostname === '127.0.0.1' ||
|
|
273
|
+
url.hostname.endsWith('.local') ||
|
|
274
|
+
url.hostname.endsWith('.dev') ||
|
|
275
|
+
url.hostname.endsWith('.test');
|
|
276
|
+
if (isLocal) {
|
|
277
|
+
console.error(`🔓 Allowing self-signed certificates for local development site: ${url.hostname}`);
|
|
278
|
+
return false;
|
|
279
|
+
}
|
|
280
|
+
// For production sites, require valid certificates
|
|
281
|
+
console.error(`🔒 Requiring valid SSL certificates for production site: ${url.hostname}`);
|
|
282
|
+
return true;
|
|
283
|
+
}
|
|
284
|
+
catch (error) {
|
|
285
|
+
// If URL parsing fails, default to requiring valid certificates
|
|
286
|
+
console.error(`⚠️ Could not parse URL ${baseUrl}, defaulting to requiring valid SSL certificates`);
|
|
287
|
+
return true;
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
ensureAuthenticated() {
|
|
291
|
+
if (!this.axiosInstance || !this.config) {
|
|
292
|
+
return this.createErrorResponse('WordPress connection not configured. Please set environment variables: WORDPRESS_BASE_URL, WORDPRESS_USERNAME, WORDPRESS_APPLICATION_PASSWORD.', 'NOT_CONFIGURED', 'AUTHENTICATION_ERROR', 'Missing WordPress connection configuration');
|
|
293
|
+
}
|
|
294
|
+
return null; // null means authenticated successfully
|
|
295
|
+
}
|
|
296
|
+
// Utility method to handle large responses with better error reporting
|
|
297
|
+
async safeApiCall(operation, operationName, context = '') {
|
|
298
|
+
try {
|
|
299
|
+
const startTime = Date.now();
|
|
300
|
+
console.error(`🚀 Starting ${operationName}${context ? ` for ${context}` : ''}`);
|
|
301
|
+
const result = await operation();
|
|
302
|
+
const duration = Date.now() - startTime;
|
|
303
|
+
console.error(`✅ Completed ${operationName} in ${duration}ms`);
|
|
304
|
+
return result;
|
|
305
|
+
}
|
|
306
|
+
catch (error) {
|
|
307
|
+
console.error(`❌ Failed ${operationName}${context ? ` for ${context}` : ''}`);
|
|
308
|
+
// Note: This method now throws errors but they will be caught by calling methods
|
|
309
|
+
// that use status-based responses
|
|
310
|
+
if (error.code === 'ECONNABORTED') {
|
|
311
|
+
console.error(`🕐 Operation timed out after 60 seconds`);
|
|
312
|
+
throw new Error(`Request timeout: ${operationName} took longer than 60 seconds. This often indicates the data is too large or the server is overloaded.`);
|
|
313
|
+
}
|
|
314
|
+
else if (error.response?.status >= 500) {
|
|
315
|
+
console.error(`🔥 Server error: ${error.response.status} ${error.response.statusText}`);
|
|
316
|
+
throw new Error(`Server error during ${operationName}: ${error.response.status} ${error.response.statusText}. The WordPress server may be overloaded or misconfigured.`);
|
|
317
|
+
}
|
|
318
|
+
else if (error.response?.status === 413) {
|
|
319
|
+
console.error(`📦 Payload too large`);
|
|
320
|
+
throw new Error(`Request payload too large for ${operationName}. Try breaking the operation into smaller chunks.`);
|
|
321
|
+
}
|
|
322
|
+
else if (error.message?.includes('maxContentLength')) {
|
|
323
|
+
console.error(`📦 Response too large`);
|
|
324
|
+
throw new Error(`Response too large for ${operationName}. The data exceeds 50MB limit. Try using chunked operations or filtering the request.`);
|
|
325
|
+
}
|
|
326
|
+
else {
|
|
327
|
+
// Re-throw the original error
|
|
328
|
+
throw error;
|
|
329
|
+
}
|
|
330
|
+
}
|
|
331
|
+
}
|
|
332
|
+
setupToolHandlers() {
|
|
333
|
+
// Tool Consolidation (v1.6.9):
|
|
334
|
+
// - Removed: get_elementor_data_chunked (replaced by get_elementor_data_smart)
|
|
335
|
+
// - Removed: get_page_structure (redundant with get_elementor_structure_summary)
|
|
336
|
+
// - Removed: clear_elementor_cache_by_page (redundant with clear_elementor_cache)
|
|
337
|
+
// - Renamed: get_elementor_data_deep_chunked → get_elementor_data_smart
|
|
338
|
+
this.server.setRequestHandler(ListToolsRequestSchema, async () => {
|
|
339
|
+
const tools = [];
|
|
340
|
+
// Add tools based on configuration
|
|
341
|
+
if (this.serverConfig.basicWordPressOperations) {
|
|
342
|
+
tools.push({
|
|
343
|
+
name: 'get_posts',
|
|
344
|
+
description: 'Retrieve WordPress posts with optional filtering',
|
|
345
|
+
inputSchema: {
|
|
346
|
+
type: 'object',
|
|
347
|
+
properties: {
|
|
348
|
+
per_page: {
|
|
349
|
+
type: 'number',
|
|
350
|
+
description: 'Number of posts to retrieve (default: 10)',
|
|
351
|
+
default: 10,
|
|
352
|
+
},
|
|
353
|
+
status: {
|
|
354
|
+
type: 'string',
|
|
355
|
+
description: 'Post status (publish, draft, private, etc.)',
|
|
356
|
+
default: 'publish',
|
|
357
|
+
},
|
|
358
|
+
search: {
|
|
359
|
+
type: 'string',
|
|
360
|
+
description: 'Search term to filter posts',
|
|
361
|
+
},
|
|
362
|
+
},
|
|
363
|
+
},
|
|
364
|
+
}, {
|
|
365
|
+
name: 'get_post',
|
|
366
|
+
description: 'Get a specific WordPress post by ID',
|
|
367
|
+
inputSchema: {
|
|
368
|
+
type: 'object',
|
|
369
|
+
properties: {
|
|
370
|
+
id: {
|
|
371
|
+
type: 'number',
|
|
372
|
+
description: 'Post ID',
|
|
373
|
+
},
|
|
374
|
+
},
|
|
375
|
+
required: ['id'],
|
|
376
|
+
},
|
|
377
|
+
}, {
|
|
378
|
+
name: 'create_post',
|
|
379
|
+
description: 'Create a new WordPress post',
|
|
380
|
+
inputSchema: {
|
|
381
|
+
type: 'object',
|
|
382
|
+
properties: {
|
|
383
|
+
title: {
|
|
384
|
+
type: 'string',
|
|
385
|
+
description: 'Post title',
|
|
386
|
+
},
|
|
387
|
+
content: {
|
|
388
|
+
type: 'string',
|
|
389
|
+
description: 'Post content (HTML)',
|
|
390
|
+
},
|
|
391
|
+
status: {
|
|
392
|
+
type: 'string',
|
|
393
|
+
description: 'Post status (draft, publish, private)',
|
|
394
|
+
default: 'draft',
|
|
395
|
+
},
|
|
396
|
+
excerpt: {
|
|
397
|
+
type: 'string',
|
|
398
|
+
description: 'Post excerpt',
|
|
399
|
+
},
|
|
400
|
+
},
|
|
401
|
+
required: ['title', 'content'],
|
|
402
|
+
},
|
|
403
|
+
}, {
|
|
404
|
+
name: 'update_post',
|
|
405
|
+
description: 'Update an existing WordPress post',
|
|
406
|
+
inputSchema: {
|
|
407
|
+
type: 'object',
|
|
408
|
+
properties: {
|
|
409
|
+
id: {
|
|
410
|
+
type: 'number',
|
|
411
|
+
description: 'Post ID to update',
|
|
412
|
+
},
|
|
413
|
+
title: {
|
|
414
|
+
type: 'string',
|
|
415
|
+
description: 'Post title',
|
|
416
|
+
},
|
|
417
|
+
content: {
|
|
418
|
+
type: 'string',
|
|
419
|
+
description: 'Post content (HTML)',
|
|
420
|
+
},
|
|
421
|
+
status: {
|
|
422
|
+
type: 'string',
|
|
423
|
+
description: 'Post status (draft, publish, private)',
|
|
424
|
+
},
|
|
425
|
+
excerpt: {
|
|
426
|
+
type: 'string',
|
|
427
|
+
description: 'Post excerpt',
|
|
428
|
+
},
|
|
429
|
+
},
|
|
430
|
+
required: ['id'],
|
|
431
|
+
},
|
|
432
|
+
}, {
|
|
433
|
+
name: 'get_pages',
|
|
434
|
+
description: 'Retrieve WordPress pages',
|
|
435
|
+
inputSchema: {
|
|
436
|
+
type: 'object',
|
|
437
|
+
properties: {
|
|
438
|
+
per_page: {
|
|
439
|
+
type: 'number',
|
|
440
|
+
description: 'Number of pages to retrieve (default: 10)',
|
|
441
|
+
default: 10,
|
|
442
|
+
},
|
|
443
|
+
status: {
|
|
444
|
+
type: 'string',
|
|
445
|
+
description: 'Page status (publish, draft, private, etc.)',
|
|
446
|
+
default: 'publish',
|
|
447
|
+
},
|
|
448
|
+
},
|
|
449
|
+
},
|
|
450
|
+
}, {
|
|
451
|
+
name: 'get_page',
|
|
452
|
+
description: 'Get a specific WordPress page by ID',
|
|
453
|
+
inputSchema: {
|
|
454
|
+
type: 'object',
|
|
455
|
+
properties: {
|
|
456
|
+
id: {
|
|
457
|
+
type: 'number',
|
|
458
|
+
description: 'Page ID',
|
|
459
|
+
},
|
|
460
|
+
},
|
|
461
|
+
required: ['id'],
|
|
462
|
+
},
|
|
463
|
+
}, {
|
|
464
|
+
name: 'list_all_content',
|
|
465
|
+
description: 'List all posts and pages with their IDs and Elementor status for debugging',
|
|
466
|
+
inputSchema: {
|
|
467
|
+
type: 'object',
|
|
468
|
+
properties: {
|
|
469
|
+
per_page: {
|
|
470
|
+
type: 'number',
|
|
471
|
+
description: 'Number of items to retrieve per type (default: 50)',
|
|
472
|
+
default: 50,
|
|
473
|
+
},
|
|
474
|
+
include_all_statuses: {
|
|
475
|
+
type: 'boolean',
|
|
476
|
+
description: 'Include draft, private, and trashed content (default: false)',
|
|
477
|
+
default: false,
|
|
478
|
+
},
|
|
479
|
+
},
|
|
480
|
+
},
|
|
481
|
+
}, {
|
|
482
|
+
name: 'create_page',
|
|
483
|
+
description: 'Create a new WordPress page',
|
|
484
|
+
inputSchema: {
|
|
485
|
+
type: 'object',
|
|
486
|
+
properties: {
|
|
487
|
+
title: {
|
|
488
|
+
type: 'string',
|
|
489
|
+
description: 'Page title',
|
|
490
|
+
},
|
|
491
|
+
content: {
|
|
492
|
+
type: 'string',
|
|
493
|
+
description: 'Page content (HTML)',
|
|
494
|
+
},
|
|
495
|
+
status: {
|
|
496
|
+
type: 'string',
|
|
497
|
+
description: 'Page status (draft, publish, private)',
|
|
498
|
+
default: 'draft',
|
|
499
|
+
},
|
|
500
|
+
excerpt: {
|
|
501
|
+
type: 'string',
|
|
502
|
+
description: 'Page excerpt',
|
|
503
|
+
},
|
|
504
|
+
parent: {
|
|
505
|
+
type: 'number',
|
|
506
|
+
description: 'Parent page ID (for creating child pages)',
|
|
507
|
+
},
|
|
508
|
+
},
|
|
509
|
+
required: ['title', 'content'],
|
|
510
|
+
},
|
|
511
|
+
}, {
|
|
512
|
+
name: 'update_page',
|
|
513
|
+
description: 'Update an existing WordPress page',
|
|
514
|
+
inputSchema: {
|
|
515
|
+
type: 'object',
|
|
516
|
+
properties: {
|
|
517
|
+
id: {
|
|
518
|
+
type: 'number',
|
|
519
|
+
description: 'Page ID to update',
|
|
520
|
+
},
|
|
521
|
+
title: {
|
|
522
|
+
type: 'string',
|
|
523
|
+
description: 'Page title',
|
|
524
|
+
},
|
|
525
|
+
content: {
|
|
526
|
+
type: 'string',
|
|
527
|
+
description: 'Page content (HTML)',
|
|
528
|
+
},
|
|
529
|
+
status: {
|
|
530
|
+
type: 'string',
|
|
531
|
+
description: 'Page status (draft, publish, private)',
|
|
532
|
+
},
|
|
533
|
+
excerpt: {
|
|
534
|
+
type: 'string',
|
|
535
|
+
description: 'Page excerpt',
|
|
536
|
+
},
|
|
537
|
+
parent: {
|
|
538
|
+
type: 'number',
|
|
539
|
+
description: 'Parent page ID (for creating child pages)',
|
|
540
|
+
},
|
|
541
|
+
},
|
|
542
|
+
required: ['id'],
|
|
543
|
+
},
|
|
544
|
+
}, {
|
|
545
|
+
name: 'get_media',
|
|
546
|
+
description: 'Get WordPress media library items',
|
|
547
|
+
inputSchema: {
|
|
548
|
+
type: 'object',
|
|
549
|
+
properties: {
|
|
550
|
+
per_page: {
|
|
551
|
+
type: 'number',
|
|
552
|
+
description: 'Number of media items to retrieve (default: 10)',
|
|
553
|
+
default: 10,
|
|
554
|
+
},
|
|
555
|
+
media_type: {
|
|
556
|
+
type: 'string',
|
|
557
|
+
description: 'Media type (image, video, audio, etc.)',
|
|
558
|
+
},
|
|
559
|
+
},
|
|
560
|
+
},
|
|
561
|
+
}, {
|
|
562
|
+
name: 'upload_media',
|
|
563
|
+
description: 'Upload media file to WordPress',
|
|
564
|
+
inputSchema: {
|
|
565
|
+
type: 'object',
|
|
566
|
+
properties: {
|
|
567
|
+
file_path: {
|
|
568
|
+
type: 'string',
|
|
569
|
+
description: 'Local path to file to upload',
|
|
570
|
+
},
|
|
571
|
+
title: {
|
|
572
|
+
type: 'string',
|
|
573
|
+
description: 'Media title',
|
|
574
|
+
},
|
|
575
|
+
alt_text: {
|
|
576
|
+
type: 'string',
|
|
577
|
+
description: 'Alt text for images',
|
|
578
|
+
},
|
|
579
|
+
},
|
|
580
|
+
required: ['file_path'],
|
|
581
|
+
},
|
|
582
|
+
});
|
|
583
|
+
}
|
|
584
|
+
if (this.serverConfig.basicElementorOperations) {
|
|
585
|
+
tools.push({
|
|
586
|
+
name: 'get_elementor_templates',
|
|
587
|
+
description: 'Get Elementor templates',
|
|
588
|
+
inputSchema: {
|
|
589
|
+
type: 'object',
|
|
590
|
+
properties: {
|
|
591
|
+
per_page: {
|
|
592
|
+
type: 'number',
|
|
593
|
+
description: 'Number of templates to retrieve (default: 10)',
|
|
594
|
+
default: 10,
|
|
595
|
+
},
|
|
596
|
+
type: {
|
|
597
|
+
type: 'string',
|
|
598
|
+
description: 'Template type (page, section, widget, etc.)',
|
|
599
|
+
},
|
|
600
|
+
},
|
|
601
|
+
},
|
|
602
|
+
}, {
|
|
603
|
+
name: 'get_elementor_data',
|
|
604
|
+
description: 'Get complete Elementor data for a page',
|
|
605
|
+
inputSchema: {
|
|
606
|
+
type: 'object',
|
|
607
|
+
properties: {
|
|
608
|
+
post_id: {
|
|
609
|
+
type: 'number',
|
|
610
|
+
description: 'Post/Page ID',
|
|
611
|
+
},
|
|
612
|
+
},
|
|
613
|
+
required: ['post_id'],
|
|
614
|
+
},
|
|
615
|
+
}, {
|
|
616
|
+
name: 'update_elementor_data',
|
|
617
|
+
description: 'Update complete Elementor data for a page',
|
|
618
|
+
inputSchema: {
|
|
619
|
+
type: 'object',
|
|
620
|
+
properties: {
|
|
621
|
+
post_id: {
|
|
622
|
+
type: 'number',
|
|
623
|
+
description: 'Post/Page ID to update',
|
|
624
|
+
},
|
|
625
|
+
elementor_data: {
|
|
626
|
+
type: 'string',
|
|
627
|
+
description: 'Complete Elementor data as JSON string',
|
|
628
|
+
},
|
|
629
|
+
},
|
|
630
|
+
required: ['post_id', 'elementor_data'],
|
|
631
|
+
},
|
|
632
|
+
}, {
|
|
633
|
+
name: 'update_elementor_widget',
|
|
634
|
+
description: 'Update a specific widget within an Elementor page (incremental update)',
|
|
635
|
+
inputSchema: {
|
|
636
|
+
type: 'object',
|
|
637
|
+
properties: {
|
|
638
|
+
post_id: {
|
|
639
|
+
type: 'number',
|
|
640
|
+
description: 'Post/Page ID to update',
|
|
641
|
+
},
|
|
642
|
+
widget_id: {
|
|
643
|
+
type: 'string',
|
|
644
|
+
description: 'Elementor widget ID (e.g., "621ef73f")',
|
|
645
|
+
},
|
|
646
|
+
widget_settings: {
|
|
647
|
+
type: 'object',
|
|
648
|
+
description: 'Widget settings object to update',
|
|
649
|
+
},
|
|
650
|
+
widget_content: {
|
|
651
|
+
type: 'string',
|
|
652
|
+
description: 'Widget content (for widgets like HTML, text, etc.)',
|
|
653
|
+
},
|
|
654
|
+
},
|
|
655
|
+
required: ['post_id', 'widget_id'],
|
|
656
|
+
},
|
|
657
|
+
}, {
|
|
658
|
+
name: 'get_elementor_widget',
|
|
659
|
+
description: 'Get a specific widget from an Elementor page',
|
|
660
|
+
inputSchema: {
|
|
661
|
+
type: 'object',
|
|
662
|
+
properties: {
|
|
663
|
+
post_id: {
|
|
664
|
+
type: 'number',
|
|
665
|
+
description: 'Post/Page ID',
|
|
666
|
+
},
|
|
667
|
+
widget_id: {
|
|
668
|
+
type: 'string',
|
|
669
|
+
description: 'Elementor widget ID (e.g., "621ef73f")',
|
|
670
|
+
},
|
|
671
|
+
},
|
|
672
|
+
required: ['post_id', 'widget_id'],
|
|
673
|
+
},
|
|
674
|
+
}, {
|
|
675
|
+
name: 'get_elementor_elements',
|
|
676
|
+
description: 'Get a simplified list of all elements and their IDs from an Elementor page',
|
|
677
|
+
inputSchema: {
|
|
678
|
+
type: 'object',
|
|
679
|
+
properties: {
|
|
680
|
+
post_id: {
|
|
681
|
+
type: 'number',
|
|
682
|
+
description: 'Post/Page ID',
|
|
683
|
+
},
|
|
684
|
+
include_content: {
|
|
685
|
+
type: 'boolean',
|
|
686
|
+
description: 'Include element content/settings preview (default: false)',
|
|
687
|
+
default: false,
|
|
688
|
+
},
|
|
689
|
+
},
|
|
690
|
+
required: ['post_id'],
|
|
691
|
+
},
|
|
692
|
+
}, {
|
|
693
|
+
name: 'update_elementor_section',
|
|
694
|
+
description: 'Update multiple widgets within a specific Elementor section (batch update)',
|
|
695
|
+
inputSchema: {
|
|
696
|
+
type: 'object',
|
|
697
|
+
properties: {
|
|
698
|
+
post_id: {
|
|
699
|
+
type: 'number',
|
|
700
|
+
description: 'Post/Page ID to update',
|
|
701
|
+
},
|
|
702
|
+
section_id: {
|
|
703
|
+
type: 'string',
|
|
704
|
+
description: 'Elementor section ID',
|
|
705
|
+
},
|
|
706
|
+
widgets_updates: {
|
|
707
|
+
type: 'array',
|
|
708
|
+
description: 'Array of widget updates',
|
|
709
|
+
items: {
|
|
710
|
+
type: 'object',
|
|
711
|
+
properties: {
|
|
712
|
+
widget_id: {
|
|
713
|
+
type: 'string',
|
|
714
|
+
description: 'Widget ID to update',
|
|
715
|
+
},
|
|
716
|
+
widget_settings: {
|
|
717
|
+
type: 'object',
|
|
718
|
+
description: 'Widget settings to update',
|
|
719
|
+
},
|
|
720
|
+
widget_content: {
|
|
721
|
+
type: 'string',
|
|
722
|
+
description: 'Widget content to update',
|
|
723
|
+
},
|
|
724
|
+
},
|
|
725
|
+
required: ['widget_id'],
|
|
726
|
+
},
|
|
727
|
+
},
|
|
728
|
+
},
|
|
729
|
+
required: ['post_id', 'section_id', 'widgets_updates'],
|
|
730
|
+
},
|
|
731
|
+
}, {
|
|
732
|
+
name: 'get_elementor_data_smart',
|
|
733
|
+
description: 'Get Elementor data with intelligent chunking for large pages - automatically handles nested structures and token limits',
|
|
734
|
+
inputSchema: {
|
|
735
|
+
type: 'object',
|
|
736
|
+
properties: {
|
|
737
|
+
post_id: {
|
|
738
|
+
type: 'number',
|
|
739
|
+
description: 'Post/Page ID',
|
|
740
|
+
},
|
|
741
|
+
max_depth: {
|
|
742
|
+
type: 'number',
|
|
743
|
+
description: 'Maximum nesting depth to include (default: 2 - sections and columns only)',
|
|
744
|
+
default: 2,
|
|
745
|
+
},
|
|
746
|
+
element_index: {
|
|
747
|
+
type: 'number',
|
|
748
|
+
description: 'Zero-based index of top-level element to retrieve (default: 0)',
|
|
749
|
+
default: 0,
|
|
750
|
+
},
|
|
751
|
+
include_widget_previews: {
|
|
752
|
+
type: 'boolean',
|
|
753
|
+
description: 'Include widget content previews (default: false)',
|
|
754
|
+
default: false,
|
|
755
|
+
},
|
|
756
|
+
},
|
|
757
|
+
required: ['post_id'],
|
|
758
|
+
},
|
|
759
|
+
}, {
|
|
760
|
+
name: 'get_elementor_structure_summary',
|
|
761
|
+
description: 'Get a compact summary of the page structure without heavy content to understand layout quickly',
|
|
762
|
+
inputSchema: {
|
|
763
|
+
type: 'object',
|
|
764
|
+
properties: {
|
|
765
|
+
post_id: {
|
|
766
|
+
type: 'number',
|
|
767
|
+
description: 'Post/Page ID',
|
|
768
|
+
},
|
|
769
|
+
max_depth: {
|
|
770
|
+
type: 'number',
|
|
771
|
+
description: 'Maximum depth to analyze (default: 4)',
|
|
772
|
+
default: 4,
|
|
773
|
+
},
|
|
774
|
+
},
|
|
775
|
+
required: ['post_id'],
|
|
776
|
+
},
|
|
777
|
+
}, {
|
|
778
|
+
name: 'backup_elementor_data',
|
|
779
|
+
description: 'Create a backup of Elementor page data',
|
|
780
|
+
inputSchema: {
|
|
781
|
+
type: 'object',
|
|
782
|
+
properties: {
|
|
783
|
+
post_id: {
|
|
784
|
+
type: 'number',
|
|
785
|
+
description: 'Post/Page ID to backup',
|
|
786
|
+
},
|
|
787
|
+
backup_name: {
|
|
788
|
+
type: 'string',
|
|
789
|
+
description: 'Optional name for the backup',
|
|
790
|
+
},
|
|
791
|
+
},
|
|
792
|
+
required: ['post_id'],
|
|
793
|
+
},
|
|
794
|
+
}, {
|
|
795
|
+
name: 'clear_elementor_cache',
|
|
796
|
+
description: 'Clear Elementor cache for better performance',
|
|
797
|
+
inputSchema: {
|
|
798
|
+
type: 'object',
|
|
799
|
+
properties: {
|
|
800
|
+
post_id: {
|
|
801
|
+
type: 'number',
|
|
802
|
+
description: 'Optional: specific post ID to clear cache for',
|
|
803
|
+
},
|
|
804
|
+
},
|
|
805
|
+
},
|
|
806
|
+
});
|
|
807
|
+
}
|
|
808
|
+
if (this.serverConfig.sectionContainerCreation) {
|
|
809
|
+
tools.push({
|
|
810
|
+
name: 'create_elementor_section',
|
|
811
|
+
description: 'Create a new Elementor section with specified columns',
|
|
812
|
+
inputSchema: {
|
|
813
|
+
type: 'object',
|
|
814
|
+
properties: {
|
|
815
|
+
post_id: {
|
|
816
|
+
type: 'number',
|
|
817
|
+
description: 'Post/Page ID to add section to',
|
|
818
|
+
},
|
|
819
|
+
position: {
|
|
820
|
+
type: 'number',
|
|
821
|
+
description: 'Position to insert the section (0-based, default: append to end)',
|
|
822
|
+
},
|
|
823
|
+
columns: {
|
|
824
|
+
type: 'number',
|
|
825
|
+
description: 'Number of columns to create (default: 1)',
|
|
826
|
+
default: 1,
|
|
827
|
+
},
|
|
828
|
+
section_settings: {
|
|
829
|
+
type: 'object',
|
|
830
|
+
description: 'Optional settings for the section',
|
|
831
|
+
},
|
|
832
|
+
},
|
|
833
|
+
required: ['post_id'],
|
|
834
|
+
},
|
|
835
|
+
}, {
|
|
836
|
+
name: 'create_elementor_container',
|
|
837
|
+
description: 'Create a new Elementor container (Flexbox)',
|
|
838
|
+
inputSchema: {
|
|
839
|
+
type: 'object',
|
|
840
|
+
properties: {
|
|
841
|
+
post_id: {
|
|
842
|
+
type: 'number',
|
|
843
|
+
description: 'Post/Page ID to add container to',
|
|
844
|
+
},
|
|
845
|
+
position: {
|
|
846
|
+
type: 'number',
|
|
847
|
+
description: 'Position to insert the container (0-based, default: append to end)',
|
|
848
|
+
},
|
|
849
|
+
container_settings: {
|
|
850
|
+
type: 'object',
|
|
851
|
+
description: 'Optional settings for the container',
|
|
852
|
+
},
|
|
853
|
+
},
|
|
854
|
+
required: ['post_id'],
|
|
855
|
+
},
|
|
856
|
+
}, {
|
|
857
|
+
name: 'add_column_to_section',
|
|
858
|
+
description: 'Add columns to an existing Elementor section',
|
|
859
|
+
inputSchema: {
|
|
860
|
+
type: 'object',
|
|
861
|
+
properties: {
|
|
862
|
+
post_id: {
|
|
863
|
+
type: 'number',
|
|
864
|
+
description: 'Post/Page ID',
|
|
865
|
+
},
|
|
866
|
+
section_id: {
|
|
867
|
+
type: 'string',
|
|
868
|
+
description: 'Section ID to add columns to',
|
|
869
|
+
},
|
|
870
|
+
columns_to_add: {
|
|
871
|
+
type: 'number',
|
|
872
|
+
description: 'Number of columns to add (default: 1)',
|
|
873
|
+
default: 1,
|
|
874
|
+
},
|
|
875
|
+
},
|
|
876
|
+
required: ['post_id', 'section_id'],
|
|
877
|
+
},
|
|
878
|
+
}, {
|
|
879
|
+
name: 'duplicate_section',
|
|
880
|
+
description: 'Duplicate an existing Elementor section',
|
|
881
|
+
inputSchema: {
|
|
882
|
+
type: 'object',
|
|
883
|
+
properties: {
|
|
884
|
+
post_id: {
|
|
885
|
+
type: 'number',
|
|
886
|
+
description: 'Post/Page ID',
|
|
887
|
+
},
|
|
888
|
+
section_id: {
|
|
889
|
+
type: 'string',
|
|
890
|
+
description: 'Section ID to duplicate',
|
|
891
|
+
},
|
|
892
|
+
position: {
|
|
893
|
+
type: 'number',
|
|
894
|
+
description: 'Position to insert the duplicated section (0-based, default: after original)',
|
|
895
|
+
},
|
|
896
|
+
},
|
|
897
|
+
required: ['post_id', 'section_id'],
|
|
898
|
+
},
|
|
899
|
+
});
|
|
900
|
+
}
|
|
901
|
+
if (this.serverConfig.widgetAddition) {
|
|
902
|
+
tools.push({
|
|
903
|
+
name: 'add_widget_to_section',
|
|
904
|
+
description: 'Add a widget to a specific section/column',
|
|
905
|
+
inputSchema: {
|
|
906
|
+
type: 'object',
|
|
907
|
+
properties: {
|
|
908
|
+
post_id: {
|
|
909
|
+
type: 'number',
|
|
910
|
+
description: 'Post/Page ID',
|
|
911
|
+
},
|
|
912
|
+
section_id: {
|
|
913
|
+
type: 'string',
|
|
914
|
+
description: 'Target section ID (optional if column_id is provided)',
|
|
915
|
+
},
|
|
916
|
+
column_id: {
|
|
917
|
+
type: 'string',
|
|
918
|
+
description: 'Target column ID (optional if section_id is provided)',
|
|
919
|
+
},
|
|
920
|
+
widget_type: {
|
|
921
|
+
type: 'string',
|
|
922
|
+
description: 'Widget type (e.g., "heading", "text", "image", "button")',
|
|
923
|
+
},
|
|
924
|
+
widget_settings: {
|
|
925
|
+
type: 'object',
|
|
926
|
+
description: 'Widget settings and content',
|
|
927
|
+
},
|
|
928
|
+
position: {
|
|
929
|
+
type: 'number',
|
|
930
|
+
description: 'Position within the container (0-based, default: append)',
|
|
931
|
+
},
|
|
932
|
+
},
|
|
933
|
+
required: ['post_id', 'widget_type'],
|
|
934
|
+
},
|
|
935
|
+
}, {
|
|
936
|
+
name: 'insert_widget_at_position',
|
|
937
|
+
description: 'Insert a widget at a specific position relative to another element',
|
|
938
|
+
inputSchema: {
|
|
939
|
+
type: 'object',
|
|
940
|
+
properties: {
|
|
941
|
+
post_id: {
|
|
942
|
+
type: 'number',
|
|
943
|
+
description: 'Post/Page ID',
|
|
944
|
+
},
|
|
945
|
+
widget_type: {
|
|
946
|
+
type: 'string',
|
|
947
|
+
description: 'Widget type to insert',
|
|
948
|
+
},
|
|
949
|
+
widget_settings: {
|
|
950
|
+
type: 'object',
|
|
951
|
+
description: 'Widget settings and content',
|
|
952
|
+
},
|
|
953
|
+
target_element_id: {
|
|
954
|
+
type: 'string',
|
|
955
|
+
description: 'Element ID to insert relative to',
|
|
956
|
+
},
|
|
957
|
+
insert_position: {
|
|
958
|
+
type: 'string',
|
|
959
|
+
description: 'Position relative to target: "before", "after", "inside" (default: "after")',
|
|
960
|
+
default: 'after',
|
|
961
|
+
},
|
|
962
|
+
},
|
|
963
|
+
required: ['post_id', 'widget_type', 'target_element_id'],
|
|
964
|
+
},
|
|
965
|
+
}, {
|
|
966
|
+
name: 'clone_widget',
|
|
967
|
+
description: 'Clone an existing widget',
|
|
968
|
+
inputSchema: {
|
|
969
|
+
type: 'object',
|
|
970
|
+
properties: {
|
|
971
|
+
post_id: {
|
|
972
|
+
type: 'number',
|
|
973
|
+
description: 'Post/Page ID',
|
|
974
|
+
},
|
|
975
|
+
widget_id: {
|
|
976
|
+
type: 'string',
|
|
977
|
+
description: 'Widget ID to clone',
|
|
978
|
+
},
|
|
979
|
+
target_element_id: {
|
|
980
|
+
type: 'string',
|
|
981
|
+
description: 'Element ID to insert cloned widget relative to (optional)',
|
|
982
|
+
},
|
|
983
|
+
insert_position: {
|
|
984
|
+
type: 'string',
|
|
985
|
+
description: 'Position relative to target: "before", "after", "inside" (default: "after")',
|
|
986
|
+
default: 'after',
|
|
987
|
+
},
|
|
988
|
+
},
|
|
989
|
+
required: ['post_id', 'widget_id'],
|
|
990
|
+
},
|
|
991
|
+
}, {
|
|
992
|
+
name: 'move_widget',
|
|
993
|
+
description: 'Move a widget to a different section/column',
|
|
994
|
+
inputSchema: {
|
|
995
|
+
type: 'object',
|
|
996
|
+
properties: {
|
|
997
|
+
post_id: {
|
|
998
|
+
type: 'number',
|
|
999
|
+
description: 'Post/Page ID',
|
|
1000
|
+
},
|
|
1001
|
+
widget_id: {
|
|
1002
|
+
type: 'string',
|
|
1003
|
+
description: 'Widget ID to move',
|
|
1004
|
+
},
|
|
1005
|
+
target_section_id: {
|
|
1006
|
+
type: 'string',
|
|
1007
|
+
description: 'Target section ID (optional if target_column_id is provided)',
|
|
1008
|
+
},
|
|
1009
|
+
target_column_id: {
|
|
1010
|
+
type: 'string',
|
|
1011
|
+
description: 'Target column ID (optional if target_section_id is provided)',
|
|
1012
|
+
},
|
|
1013
|
+
position: {
|
|
1014
|
+
type: 'number',
|
|
1015
|
+
description: 'Position within target container (0-based, default: append)',
|
|
1016
|
+
},
|
|
1017
|
+
},
|
|
1018
|
+
required: ['post_id', 'widget_id'],
|
|
1019
|
+
},
|
|
1020
|
+
});
|
|
1021
|
+
}
|
|
1022
|
+
if (this.serverConfig.elementManagement) {
|
|
1023
|
+
tools.push({
|
|
1024
|
+
name: 'delete_elementor_element',
|
|
1025
|
+
description: 'Delete an Elementor element (section, column, or widget)',
|
|
1026
|
+
inputSchema: {
|
|
1027
|
+
type: 'object',
|
|
1028
|
+
properties: {
|
|
1029
|
+
post_id: {
|
|
1030
|
+
type: 'number',
|
|
1031
|
+
description: 'Post/Page ID',
|
|
1032
|
+
},
|
|
1033
|
+
element_id: {
|
|
1034
|
+
type: 'string',
|
|
1035
|
+
description: 'Element ID to delete',
|
|
1036
|
+
},
|
|
1037
|
+
},
|
|
1038
|
+
required: ['post_id', 'element_id'],
|
|
1039
|
+
},
|
|
1040
|
+
}, {
|
|
1041
|
+
name: 'reorder_elements',
|
|
1042
|
+
description: 'Reorder elements within a container',
|
|
1043
|
+
inputSchema: {
|
|
1044
|
+
type: 'object',
|
|
1045
|
+
properties: {
|
|
1046
|
+
post_id: {
|
|
1047
|
+
type: 'number',
|
|
1048
|
+
description: 'Post/Page ID',
|
|
1049
|
+
},
|
|
1050
|
+
container_id: {
|
|
1051
|
+
type: 'string',
|
|
1052
|
+
description: 'Container ID (section or column)',
|
|
1053
|
+
},
|
|
1054
|
+
element_ids: {
|
|
1055
|
+
type: 'array',
|
|
1056
|
+
description: 'Array of element IDs in desired order',
|
|
1057
|
+
items: {
|
|
1058
|
+
type: 'string',
|
|
1059
|
+
},
|
|
1060
|
+
},
|
|
1061
|
+
},
|
|
1062
|
+
required: ['post_id', 'container_id', 'element_ids'],
|
|
1063
|
+
},
|
|
1064
|
+
}, {
|
|
1065
|
+
name: 'copy_element_settings',
|
|
1066
|
+
description: 'Copy settings from one element to another',
|
|
1067
|
+
inputSchema: {
|
|
1068
|
+
type: 'object',
|
|
1069
|
+
properties: {
|
|
1070
|
+
post_id: {
|
|
1071
|
+
type: 'number',
|
|
1072
|
+
description: 'Post/Page ID',
|
|
1073
|
+
},
|
|
1074
|
+
source_element_id: {
|
|
1075
|
+
type: 'string',
|
|
1076
|
+
description: 'Source element ID to copy from',
|
|
1077
|
+
},
|
|
1078
|
+
target_element_id: {
|
|
1079
|
+
type: 'string',
|
|
1080
|
+
description: 'Target element ID to copy to',
|
|
1081
|
+
},
|
|
1082
|
+
settings_to_copy: {
|
|
1083
|
+
type: 'array',
|
|
1084
|
+
description: 'Specific setting keys to copy (optional, copies all if not specified)',
|
|
1085
|
+
items: {
|
|
1086
|
+
type: 'string',
|
|
1087
|
+
},
|
|
1088
|
+
},
|
|
1089
|
+
},
|
|
1090
|
+
required: ['post_id', 'source_element_id', 'target_element_id'],
|
|
1091
|
+
},
|
|
1092
|
+
});
|
|
1093
|
+
}
|
|
1094
|
+
if (this.serverConfig.advancedElementOperations) {
|
|
1095
|
+
tools.push({
|
|
1096
|
+
name: 'find_elements_by_type',
|
|
1097
|
+
description: 'Find all elements of a specific type on a page',
|
|
1098
|
+
inputSchema: {
|
|
1099
|
+
type: 'object',
|
|
1100
|
+
properties: {
|
|
1101
|
+
post_id: {
|
|
1102
|
+
type: 'number',
|
|
1103
|
+
description: 'Post/Page ID',
|
|
1104
|
+
},
|
|
1105
|
+
widget_type: {
|
|
1106
|
+
type: 'string',
|
|
1107
|
+
description: 'Widget type to search for (e.g., "heading", "text", "image")',
|
|
1108
|
+
},
|
|
1109
|
+
include_settings: {
|
|
1110
|
+
type: 'boolean',
|
|
1111
|
+
description: 'Include element settings in results (default: false)',
|
|
1112
|
+
default: false,
|
|
1113
|
+
},
|
|
1114
|
+
},
|
|
1115
|
+
required: ['post_id', 'widget_type'],
|
|
1116
|
+
},
|
|
1117
|
+
});
|
|
1118
|
+
}
|
|
1119
|
+
// Add tool count to help users understand which mode is active
|
|
1120
|
+
const totalEnabled = this.serverConfig.getTotalEnabledFeatures();
|
|
1121
|
+
if (process.env.NODE_ENV !== 'production') {
|
|
1122
|
+
console.error(`[Config] Loaded ${tools.length} tools (Mode: ${this.serverConfig.mode}, Features: ${totalEnabled})`);
|
|
1123
|
+
console.error(`[Config] Active features: ${Object.entries(this.serverConfig)
|
|
1124
|
+
.filter(([key, value]) => typeof value === 'boolean' && value && key !== 'mode')
|
|
1125
|
+
.map(([key]) => key)
|
|
1126
|
+
.join(', ')}`);
|
|
1127
|
+
}
|
|
1128
|
+
return { tools };
|
|
1129
|
+
});
|
|
1130
|
+
this.server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
1131
|
+
const { name, arguments: args } = request.params;
|
|
1132
|
+
try {
|
|
1133
|
+
switch (name) {
|
|
1134
|
+
case 'get_posts':
|
|
1135
|
+
return await this.getPosts(args);
|
|
1136
|
+
case 'get_post':
|
|
1137
|
+
return await this.getPost(args);
|
|
1138
|
+
case 'create_post':
|
|
1139
|
+
return await this.createPost(args);
|
|
1140
|
+
case 'update_post':
|
|
1141
|
+
return await this.updatePost(args);
|
|
1142
|
+
case 'get_pages':
|
|
1143
|
+
return await this.getPages(args);
|
|
1144
|
+
case 'get_page':
|
|
1145
|
+
return await this.getPage(args);
|
|
1146
|
+
case 'list_all_content':
|
|
1147
|
+
return await this.listAllContent(args);
|
|
1148
|
+
case 'create_page':
|
|
1149
|
+
return await this.createPage(args);
|
|
1150
|
+
case 'update_page':
|
|
1151
|
+
return await this.updatePage(args);
|
|
1152
|
+
case 'get_elementor_templates':
|
|
1153
|
+
return await this.getElementorTemplates(args);
|
|
1154
|
+
case 'get_elementor_data':
|
|
1155
|
+
return await this.getElementorData(args);
|
|
1156
|
+
case 'update_elementor_data':
|
|
1157
|
+
return await this.updateElementorData(args);
|
|
1158
|
+
case 'update_elementor_widget':
|
|
1159
|
+
return await this.updateElementorWidget(args);
|
|
1160
|
+
case 'get_elementor_widget':
|
|
1161
|
+
return await this.getElementorWidget(args);
|
|
1162
|
+
case 'get_elementor_elements':
|
|
1163
|
+
return await this.getElementorElements(args);
|
|
1164
|
+
case 'update_elementor_section':
|
|
1165
|
+
return await this.updateElementorSection(args);
|
|
1166
|
+
case 'get_elementor_data_smart':
|
|
1167
|
+
return await this.getElementorDataDeepChunked(args);
|
|
1168
|
+
case 'get_elementor_structure_summary':
|
|
1169
|
+
return await this.getElementorStructureSummary(args);
|
|
1170
|
+
case 'backup_elementor_data':
|
|
1171
|
+
return await this.backupElementorData(args);
|
|
1172
|
+
case 'get_media':
|
|
1173
|
+
return await this.getMedia(args);
|
|
1174
|
+
case 'upload_media':
|
|
1175
|
+
return await this.uploadMedia(args);
|
|
1176
|
+
// Section and Container Creation Tools
|
|
1177
|
+
case 'create_elementor_section':
|
|
1178
|
+
return await this.createElementorSection(args);
|
|
1179
|
+
case 'create_elementor_container':
|
|
1180
|
+
return await this.createElementorContainer(args);
|
|
1181
|
+
case 'add_column_to_section':
|
|
1182
|
+
return await this.addColumnToSection(args);
|
|
1183
|
+
case 'duplicate_section':
|
|
1184
|
+
return await this.duplicateSection(args);
|
|
1185
|
+
// Widget Addition Tools
|
|
1186
|
+
case 'add_widget_to_section':
|
|
1187
|
+
return await this.addWidgetToSection(args);
|
|
1188
|
+
case 'insert_widget_at_position':
|
|
1189
|
+
return await this.insertWidgetAtPosition(args);
|
|
1190
|
+
case 'clone_widget':
|
|
1191
|
+
return await this.cloneWidget(args);
|
|
1192
|
+
case 'move_widget':
|
|
1193
|
+
return await this.moveWidget(args);
|
|
1194
|
+
// Element Management Tools
|
|
1195
|
+
case 'delete_elementor_element':
|
|
1196
|
+
return await this.deleteElementorElement(args);
|
|
1197
|
+
case 'reorder_elements':
|
|
1198
|
+
return await this.reorderElements(args);
|
|
1199
|
+
case 'copy_element_settings':
|
|
1200
|
+
return await this.copyElementSettings(args);
|
|
1201
|
+
// Template Management
|
|
1202
|
+
case 'create_elementor_template':
|
|
1203
|
+
return await this.createElementorTemplate(args);
|
|
1204
|
+
case 'apply_template_to_page':
|
|
1205
|
+
return await this.applyTemplateToPage(args);
|
|
1206
|
+
case 'export_elementor_template':
|
|
1207
|
+
return await this.exportElementorTemplate(args);
|
|
1208
|
+
case 'import_elementor_template':
|
|
1209
|
+
return await this.importElementorTemplate(args);
|
|
1210
|
+
// Global Settings Management
|
|
1211
|
+
case 'get_elementor_global_colors':
|
|
1212
|
+
return await this.getElementorGlobalColors(args);
|
|
1213
|
+
case 'update_elementor_global_colors':
|
|
1214
|
+
return await this.updateElementorGlobalColors(args);
|
|
1215
|
+
case 'get_elementor_global_fonts':
|
|
1216
|
+
return await this.getElementorGlobalFonts(args);
|
|
1217
|
+
case 'update_elementor_global_fonts':
|
|
1218
|
+
return await this.updateElementorGlobalFonts(args);
|
|
1219
|
+
// Page Structure Tools
|
|
1220
|
+
case 'rebuild_page_structure':
|
|
1221
|
+
return await this.rebuildPageStructure(args);
|
|
1222
|
+
case 'validate_elementor_data':
|
|
1223
|
+
return await this.validateElementorData(args);
|
|
1224
|
+
// Performance & Optimization
|
|
1225
|
+
case 'regenerate_css':
|
|
1226
|
+
return await this.regenerateCSS(args);
|
|
1227
|
+
case 'optimize_elementor_assets':
|
|
1228
|
+
return await this.optimizeElementorAssets(args);
|
|
1229
|
+
case 'clear_elementor_cache':
|
|
1230
|
+
return await this.clearElementorCacheGeneral(args);
|
|
1231
|
+
// Advanced Element Operations
|
|
1232
|
+
case 'find_elements_by_type':
|
|
1233
|
+
return await this.findElementsByType(args);
|
|
1234
|
+
case 'bulk_update_widget_settings':
|
|
1235
|
+
return await this.bulkUpdateWidgetSettings(args);
|
|
1236
|
+
case 'replace_widget_content':
|
|
1237
|
+
return await this.replaceWidgetContent(args);
|
|
1238
|
+
// Custom Fields Integration
|
|
1239
|
+
case 'get_elementor_custom_fields':
|
|
1240
|
+
return await this.getElementorCustomFields(args);
|
|
1241
|
+
case 'update_dynamic_content_sources':
|
|
1242
|
+
return await this.updateDynamicContentSources(args);
|
|
1243
|
+
// Revision and History
|
|
1244
|
+
case 'get_elementor_revisions':
|
|
1245
|
+
return await this.getElementorRevisions(args);
|
|
1246
|
+
case 'restore_elementor_revision':
|
|
1247
|
+
return await this.restoreElementorRevision(args);
|
|
1248
|
+
case 'compare_elementor_revisions':
|
|
1249
|
+
return await this.compareElementorRevisions(args);
|
|
1250
|
+
default:
|
|
1251
|
+
return this.createErrorResponse(`Unknown tool: ${name}`, 'METHOD_NOT_FOUND', 'TOOL_ERROR', `Tool "${name}" is not implemented or available`);
|
|
1252
|
+
}
|
|
1253
|
+
}
|
|
1254
|
+
catch (error) {
|
|
1255
|
+
if (error instanceof McpError) {
|
|
1256
|
+
// Convert MCP errors to structured format
|
|
1257
|
+
return this.createErrorResponse(error.message || 'MCP operation failed', 'MCP_ERROR', 'PROTOCOL_ERROR', `MCP Error Code: ${error.code || 'Unknown'}`);
|
|
1258
|
+
}
|
|
1259
|
+
return this.createErrorResponse(`Tool execution failed: ${error instanceof Error ? error.message : String(error)}`, 'EXECUTION_ERROR', 'INTERNAL_ERROR', 'Unexpected error during tool execution');
|
|
1260
|
+
}
|
|
1261
|
+
});
|
|
1262
|
+
this.server.setRequestHandler(InitializeRequestSchema, async (request) => {
|
|
1263
|
+
return {
|
|
1264
|
+
protocolVersion: request.params.protocolVersion,
|
|
1265
|
+
capabilities: {
|
|
1266
|
+
tools: {},
|
|
1267
|
+
},
|
|
1268
|
+
serverInfo: {
|
|
1269
|
+
name: 'elementor-wordpress-mcp',
|
|
1270
|
+
version: '1.7.1',
|
|
1271
|
+
},
|
|
1272
|
+
};
|
|
1273
|
+
});
|
|
1274
|
+
}
|
|
1275
|
+
setupResourceHandlers() {
|
|
1276
|
+
this.server.setRequestHandler(ListResourcesRequestSchema, async () => {
|
|
1277
|
+
return {
|
|
1278
|
+
resources: [
|
|
1279
|
+
{
|
|
1280
|
+
uri: 'elementor://config',
|
|
1281
|
+
mimeType: 'application/json',
|
|
1282
|
+
name: 'WordPress Configuration',
|
|
1283
|
+
description: 'Current WordPress connection configuration',
|
|
1284
|
+
},
|
|
1285
|
+
],
|
|
1286
|
+
};
|
|
1287
|
+
});
|
|
1288
|
+
this.server.setRequestHandler(ReadResourceRequestSchema, async (request) => {
|
|
1289
|
+
const { uri } = request.params;
|
|
1290
|
+
if (uri === 'elementor://config') {
|
|
1291
|
+
return {
|
|
1292
|
+
contents: [
|
|
1293
|
+
{
|
|
1294
|
+
uri,
|
|
1295
|
+
mimeType: 'application/json',
|
|
1296
|
+
text: JSON.stringify(this.config
|
|
1297
|
+
? {
|
|
1298
|
+
baseUrl: this.config.baseUrl,
|
|
1299
|
+
username: this.config.username,
|
|
1300
|
+
connected: true,
|
|
1301
|
+
}
|
|
1302
|
+
: { connected: false }, null, 2),
|
|
1303
|
+
},
|
|
1304
|
+
],
|
|
1305
|
+
};
|
|
1306
|
+
}
|
|
1307
|
+
return this.createErrorResponse(`Unknown resource: ${uri}`, "OPERATION_ERROR", "API_ERROR", "Operation failed");
|
|
1308
|
+
});
|
|
1309
|
+
}
|
|
1310
|
+
// Tool implementations
|
|
1311
|
+
async getPosts(args) {
|
|
1312
|
+
const authCheck = this.ensureAuthenticated();
|
|
1313
|
+
if (authCheck)
|
|
1314
|
+
return authCheck; // Return error response if not authenticated
|
|
1315
|
+
const params = {
|
|
1316
|
+
per_page: args.per_page || 10,
|
|
1317
|
+
status: args.status || 'publish',
|
|
1318
|
+
context: 'view' // Use 'view' instead of 'edit' for lighter responses
|
|
1319
|
+
};
|
|
1320
|
+
if (args.search) {
|
|
1321
|
+
params.search = args.search;
|
|
1322
|
+
}
|
|
1323
|
+
try {
|
|
1324
|
+
console.error(`Fetching posts with params: ${JSON.stringify(params)}`);
|
|
1325
|
+
const response = await this.axiosInstance.get('posts', { params });
|
|
1326
|
+
// Create optimized summary objects instead of returning full content
|
|
1327
|
+
const posts = response.data;
|
|
1328
|
+
const postSummaries = posts.map((post) => ({
|
|
1329
|
+
id: post.id,
|
|
1330
|
+
title: post.title?.rendered || '(No title)',
|
|
1331
|
+
status: post.status,
|
|
1332
|
+
date_created: post.date,
|
|
1333
|
+
date_modified: post.modified,
|
|
1334
|
+
url: post.link,
|
|
1335
|
+
excerpt: post.excerpt?.rendered ? post.excerpt.rendered.replace(/<[^>]*>/g, '').substring(0, 100) + '...' : '',
|
|
1336
|
+
author: post.author,
|
|
1337
|
+
featured_media: post.featured_media,
|
|
1338
|
+
comment_count: post.comment_count || 0,
|
|
1339
|
+
// Include Elementor status for quick reference
|
|
1340
|
+
elementor_status: post.meta?._elementor_data ? 'full' :
|
|
1341
|
+
post.meta?._elementor_edit_mode === 'builder' ? 'partial' : 'none'
|
|
1342
|
+
}));
|
|
1343
|
+
let debugInfo = `Found ${posts.length} posts\n`;
|
|
1344
|
+
// Add summary with Elementor status counts
|
|
1345
|
+
const elementorStats = {
|
|
1346
|
+
full: postSummaries.filter((p) => p.elementor_status === 'full').length,
|
|
1347
|
+
partial: postSummaries.filter((p) => p.elementor_status === 'partial').length,
|
|
1348
|
+
none: postSummaries.filter((p) => p.elementor_status === 'none').length
|
|
1349
|
+
};
|
|
1350
|
+
debugInfo += `Elementor Status: ✅ Full (${elementorStats.full}), ⚠️ Partial (${elementorStats.partial}), ❌ None (${elementorStats.none})\n`;
|
|
1351
|
+
debugInfo += `\n💡 Use get_post tool with specific ID for full content details\n`;
|
|
1352
|
+
return this.createSuccessResponse({
|
|
1353
|
+
posts: postSummaries, // Return summaries instead of full objects
|
|
1354
|
+
summary: debugInfo,
|
|
1355
|
+
total_found: posts.length,
|
|
1356
|
+
performance_note: "Optimized response - use get_post(id) for full content details"
|
|
1357
|
+
}, `Successfully retrieved ${posts.length} post summaries`);
|
|
1358
|
+
}
|
|
1359
|
+
catch (error) {
|
|
1360
|
+
console.error(`Error fetching posts: ${error.response?.status} - ${error.response?.statusText}`);
|
|
1361
|
+
console.error(`URL: ${error.config?.url}`);
|
|
1362
|
+
console.error(`Headers: ${JSON.stringify(error.config?.headers)}`);
|
|
1363
|
+
return this.createErrorResponse(`Failed to fetch posts: ${error.response?.status} ${error.response?.statusText} - ${error.response?.data?.message || error.message}`, 'FETCH_POSTS_ERROR', 'API_ERROR', `HTTP ${error.response?.status}: ${error.response?.statusText}`);
|
|
1364
|
+
}
|
|
1365
|
+
}
|
|
1366
|
+
async getPost(args) {
|
|
1367
|
+
const authCheck = this.ensureAuthenticated();
|
|
1368
|
+
if (authCheck)
|
|
1369
|
+
return authCheck;
|
|
1370
|
+
try {
|
|
1371
|
+
console.error(`Fetching post with ID: ${args.id}`);
|
|
1372
|
+
const response = await this.axiosInstance.get(`posts/${args.id}`, {
|
|
1373
|
+
params: { context: 'edit' }
|
|
1374
|
+
});
|
|
1375
|
+
return this.createSuccessResponse(response.data, `Successfully retrieved post ${args.id}`);
|
|
1376
|
+
}
|
|
1377
|
+
catch (error) {
|
|
1378
|
+
console.error(`Error fetching post ${args.id}: ${error.response?.status} - ${error.response?.statusText}`);
|
|
1379
|
+
console.error(`URL: ${error.config?.url}`);
|
|
1380
|
+
console.error(`Headers: ${JSON.stringify(error.config?.headers)}`);
|
|
1381
|
+
if (error.response?.status === 404) {
|
|
1382
|
+
return this.createErrorResponse(`Post with ID ${args.id} not found. The post may have been deleted, be in trash, or may not exist.`, 'POST_NOT_FOUND', 'NOT_FOUND_ERROR', `HTTP 404: Post ${args.id} does not exist`);
|
|
1383
|
+
}
|
|
1384
|
+
return this.createErrorResponse(`Failed to fetch post ${args.id}: ${error.response?.status} ${error.response?.statusText} - ${error.response?.data?.message || error.message}`, 'FETCH_POST_ERROR', 'API_ERROR', `HTTP ${error.response?.status}: ${error.response?.statusText}`);
|
|
1385
|
+
}
|
|
1386
|
+
}
|
|
1387
|
+
async getPage(args) {
|
|
1388
|
+
const authCheck = this.ensureAuthenticated();
|
|
1389
|
+
if (authCheck)
|
|
1390
|
+
return authCheck;
|
|
1391
|
+
try {
|
|
1392
|
+
console.error(`Fetching page with ID: ${args.id}`);
|
|
1393
|
+
const response = await this.axiosInstance.get(`pages/${args.id}`, {
|
|
1394
|
+
params: { context: 'edit' }
|
|
1395
|
+
});
|
|
1396
|
+
return this.createSuccessResponse(response.data, `Successfully retrieved page ${args.id}`);
|
|
1397
|
+
}
|
|
1398
|
+
catch (error) {
|
|
1399
|
+
console.error(`Error fetching page ${args.id}: ${error.response?.status} - ${error.response?.statusText}`);
|
|
1400
|
+
console.error(`URL: ${error.config?.url}`);
|
|
1401
|
+
console.error(`Headers: ${JSON.stringify(error.config?.headers)}`);
|
|
1402
|
+
if (error.response?.status === 404) {
|
|
1403
|
+
return this.createErrorResponse(`Page with ID ${args.id} not found. The page may have been deleted, be in trash, or may not exist.`, 'PAGE_NOT_FOUND', 'NOT_FOUND_ERROR', `HTTP 404: Page ${args.id} does not exist`);
|
|
1404
|
+
}
|
|
1405
|
+
return this.createErrorResponse(`Failed to fetch page ${args.id}: ${error.response?.status} ${error.response?.statusText} - ${error.response?.data?.message || error.message}`, 'FETCH_PAGE_ERROR', 'API_ERROR', `HTTP ${error.response?.status}: ${error.response?.statusText}`);
|
|
1406
|
+
}
|
|
1407
|
+
}
|
|
1408
|
+
async createPost(args) {
|
|
1409
|
+
const authCheck = this.ensureAuthenticated();
|
|
1410
|
+
if (authCheck)
|
|
1411
|
+
return authCheck;
|
|
1412
|
+
const postData = {
|
|
1413
|
+
title: args.title,
|
|
1414
|
+
content: args.content,
|
|
1415
|
+
status: args.status || 'draft',
|
|
1416
|
+
...(args.excerpt && { excerpt: args.excerpt }),
|
|
1417
|
+
};
|
|
1418
|
+
try {
|
|
1419
|
+
console.error(`Creating post with title: "${args.title}"`);
|
|
1420
|
+
const response = await this.axiosInstance.post('posts', postData);
|
|
1421
|
+
// Clear Elementor cache after creating post
|
|
1422
|
+
await this.clearElementorCache(response.data.id);
|
|
1423
|
+
return this.createSuccessResponse({
|
|
1424
|
+
post: response.data,
|
|
1425
|
+
cache_cleared: true
|
|
1426
|
+
}, `Post created successfully! ID: ${response.data.id}, Status: ${response.data.status}`);
|
|
1427
|
+
}
|
|
1428
|
+
catch (error) {
|
|
1429
|
+
console.error(`Error creating post: ${error.response?.status} - ${error.response?.statusText}`);
|
|
1430
|
+
console.error(`URL: ${error.config?.url}`);
|
|
1431
|
+
console.error(`Headers: ${JSON.stringify(error.config?.headers)}`);
|
|
1432
|
+
return this.createErrorResponse(`Failed to create post: ${error.response?.status} ${error.response?.statusText} - ${error.response?.data?.message || error.message}`, 'CREATE_POST_ERROR', 'API_ERROR', `HTTP ${error.response?.status}: ${error.response?.statusText}`);
|
|
1433
|
+
}
|
|
1434
|
+
}
|
|
1435
|
+
async updatePost(args) {
|
|
1436
|
+
const authCheck = this.ensureAuthenticated();
|
|
1437
|
+
if (authCheck)
|
|
1438
|
+
return authCheck;
|
|
1439
|
+
const updateData = {};
|
|
1440
|
+
if (args.title)
|
|
1441
|
+
updateData.title = args.title;
|
|
1442
|
+
if (args.content)
|
|
1443
|
+
updateData.content = args.content;
|
|
1444
|
+
if (args.status)
|
|
1445
|
+
updateData.status = args.status;
|
|
1446
|
+
if (args.excerpt)
|
|
1447
|
+
updateData.excerpt = args.excerpt;
|
|
1448
|
+
try {
|
|
1449
|
+
console.error(`Updating post ID: ${args.id}`);
|
|
1450
|
+
const response = await this.axiosInstance.post(`posts/${args.id}`, updateData);
|
|
1451
|
+
// Clear Elementor cache after updating post
|
|
1452
|
+
await this.clearElementorCache(args.id);
|
|
1453
|
+
return this.createSuccessResponse({
|
|
1454
|
+
post: response.data,
|
|
1455
|
+
cache_cleared: true
|
|
1456
|
+
}, `Post updated successfully! ID: ${response.data.id}, Status: ${response.data.status}`);
|
|
1457
|
+
}
|
|
1458
|
+
catch (error) {
|
|
1459
|
+
console.error(`Error updating post ${args.id}: ${error.response?.status} - ${error.response?.statusText}`);
|
|
1460
|
+
console.error(`URL: ${error.config?.url}`);
|
|
1461
|
+
console.error(`Headers: ${JSON.stringify(error.config?.headers)}`);
|
|
1462
|
+
if (error.response?.status === 404) {
|
|
1463
|
+
return this.createErrorResponse(`Post with ID ${args.id} not found. The post may have been deleted, be in trash, or may not exist.`, 'POST_NOT_FOUND', 'NOT_FOUND_ERROR', `HTTP 404: Post ${args.id} does not exist`);
|
|
1464
|
+
}
|
|
1465
|
+
return this.createErrorResponse(`Failed to update post ${args.id}: ${error.response?.status} ${error.response?.statusText} - ${error.response?.data?.message || error.message}`, 'UPDATE_POST_ERROR', 'API_ERROR', `HTTP ${error.response?.status}: ${error.response?.statusText}`);
|
|
1466
|
+
}
|
|
1467
|
+
}
|
|
1468
|
+
async getPages(args) {
|
|
1469
|
+
const authCheck = this.ensureAuthenticated();
|
|
1470
|
+
if (authCheck)
|
|
1471
|
+
return authCheck;
|
|
1472
|
+
const params = {
|
|
1473
|
+
per_page: args.per_page || 10,
|
|
1474
|
+
status: args.status || 'publish',
|
|
1475
|
+
context: 'view' // Use 'view' instead of 'edit' for lighter responses
|
|
1476
|
+
};
|
|
1477
|
+
try {
|
|
1478
|
+
console.error(`Fetching pages with params: ${JSON.stringify(params)}`);
|
|
1479
|
+
const response = await this.axiosInstance.get('pages', { params });
|
|
1480
|
+
// Create optimized summary objects instead of returning full content
|
|
1481
|
+
const pages = response.data;
|
|
1482
|
+
const pageSummaries = pages.map((page) => ({
|
|
1483
|
+
id: page.id,
|
|
1484
|
+
title: page.title?.rendered || '(No title)',
|
|
1485
|
+
status: page.status,
|
|
1486
|
+
date_created: page.date,
|
|
1487
|
+
date_modified: page.modified,
|
|
1488
|
+
url: page.link,
|
|
1489
|
+
excerpt: page.excerpt?.rendered ? page.excerpt.rendered.replace(/<[^>]*>/g, '').substring(0, 100) + '...' : '',
|
|
1490
|
+
author: page.author,
|
|
1491
|
+
parent: page.parent,
|
|
1492
|
+
menu_order: page.menu_order,
|
|
1493
|
+
featured_media: page.featured_media,
|
|
1494
|
+
comment_count: page.comment_count || 0,
|
|
1495
|
+
// Include Elementor status for quick reference
|
|
1496
|
+
elementor_status: page.meta?._elementor_data ? 'full' :
|
|
1497
|
+
page.meta?._elementor_edit_mode === 'builder' ? 'partial' : 'none'
|
|
1498
|
+
}));
|
|
1499
|
+
let debugInfo = `Found ${pages.length} pages\n`;
|
|
1500
|
+
// Add summary with Elementor status counts
|
|
1501
|
+
const elementorStats = {
|
|
1502
|
+
full: pageSummaries.filter((p) => p.elementor_status === 'full').length,
|
|
1503
|
+
partial: pageSummaries.filter((p) => p.elementor_status === 'partial').length,
|
|
1504
|
+
none: pageSummaries.filter((p) => p.elementor_status === 'none').length
|
|
1505
|
+
};
|
|
1506
|
+
debugInfo += `Elementor Status: ✅ Full (${elementorStats.full}), ⚠️ Partial (${elementorStats.partial}), ❌ None (${elementorStats.none})\n`;
|
|
1507
|
+
debugInfo += `\n💡 Use get_page tool with specific ID for full content details\n`;
|
|
1508
|
+
return this.createSuccessResponse({
|
|
1509
|
+
pages: pageSummaries, // Return summaries instead of full objects
|
|
1510
|
+
summary: debugInfo,
|
|
1511
|
+
total_found: pages.length,
|
|
1512
|
+
performance_note: "Optimized response - use get_page(id) for full content details"
|
|
1513
|
+
}, `Successfully retrieved ${pages.length} page summaries`);
|
|
1514
|
+
}
|
|
1515
|
+
catch (error) {
|
|
1516
|
+
console.error(`Error fetching pages: ${error.response?.status} - ${error.response?.statusText}`);
|
|
1517
|
+
console.error(`URL: ${error.config?.url}`);
|
|
1518
|
+
console.error(`Headers: ${JSON.stringify(error.config?.headers)}`);
|
|
1519
|
+
return this.createErrorResponse(`Failed to fetch pages: ${error.response?.status} ${error.response?.statusText} - ${error.response?.data?.message || error.message}`, 'FETCH_PAGES_ERROR', 'API_ERROR', `HTTP ${error.response?.status}: ${error.response?.statusText}`);
|
|
1520
|
+
}
|
|
1521
|
+
}
|
|
1522
|
+
async listAllContent(args) {
|
|
1523
|
+
const authCheck = this.ensureAuthenticated();
|
|
1524
|
+
if (authCheck)
|
|
1525
|
+
return authCheck;
|
|
1526
|
+
try {
|
|
1527
|
+
const perPage = args.per_page || 50;
|
|
1528
|
+
const statuses = args.include_all_statuses ? ['publish', 'draft', 'private', 'trash'] : ['publish'];
|
|
1529
|
+
console.error(`Listing all content with per_page: ${perPage}, statuses: ${statuses.join(', ')}`);
|
|
1530
|
+
let allContent = [];
|
|
1531
|
+
// Fetch posts for each status
|
|
1532
|
+
for (const status of statuses) {
|
|
1533
|
+
try {
|
|
1534
|
+
const postsResponse = await this.axiosInstance.get('posts', {
|
|
1535
|
+
params: {
|
|
1536
|
+
per_page: perPage,
|
|
1537
|
+
status,
|
|
1538
|
+
context: 'edit'
|
|
1539
|
+
}
|
|
1540
|
+
});
|
|
1541
|
+
postsResponse.data.forEach((post) => {
|
|
1542
|
+
const hasElementorData = post.meta?._elementor_data;
|
|
1543
|
+
const hasElementorEditMode = post.meta?._elementor_edit_mode;
|
|
1544
|
+
let elementorStatus = 'none';
|
|
1545
|
+
if (hasElementorData) {
|
|
1546
|
+
elementorStatus = 'full';
|
|
1547
|
+
}
|
|
1548
|
+
else if (hasElementorEditMode === 'builder') {
|
|
1549
|
+
elementorStatus = 'partial';
|
|
1550
|
+
}
|
|
1551
|
+
allContent.push({
|
|
1552
|
+
id: post.id,
|
|
1553
|
+
title: post.title.rendered || '(No title)',
|
|
1554
|
+
type: 'post',
|
|
1555
|
+
status: post.status,
|
|
1556
|
+
elementor_status: elementorStatus,
|
|
1557
|
+
url: post.link
|
|
1558
|
+
});
|
|
1559
|
+
});
|
|
1560
|
+
}
|
|
1561
|
+
catch (error) {
|
|
1562
|
+
console.error(`Failed to fetch posts with status ${status}: ${error.message}`);
|
|
1563
|
+
}
|
|
1564
|
+
}
|
|
1565
|
+
// Fetch pages for each status
|
|
1566
|
+
for (const status of statuses) {
|
|
1567
|
+
try {
|
|
1568
|
+
const pagesResponse = await this.axiosInstance.get('pages', {
|
|
1569
|
+
params: {
|
|
1570
|
+
per_page: perPage,
|
|
1571
|
+
status,
|
|
1572
|
+
context: 'edit'
|
|
1573
|
+
}
|
|
1574
|
+
});
|
|
1575
|
+
pagesResponse.data.forEach((page) => {
|
|
1576
|
+
const hasElementorData = page.meta?._elementor_data;
|
|
1577
|
+
const hasElementorEditMode = page.meta?._elementor_edit_mode;
|
|
1578
|
+
let elementorStatus = 'none';
|
|
1579
|
+
if (hasElementorData) {
|
|
1580
|
+
elementorStatus = 'full';
|
|
1581
|
+
}
|
|
1582
|
+
else if (hasElementorEditMode === 'builder') {
|
|
1583
|
+
elementorStatus = 'partial';
|
|
1584
|
+
}
|
|
1585
|
+
allContent.push({
|
|
1586
|
+
id: page.id,
|
|
1587
|
+
title: page.title.rendered || '(No title)',
|
|
1588
|
+
type: 'page',
|
|
1589
|
+
status: page.status,
|
|
1590
|
+
elementor_status: elementorStatus,
|
|
1591
|
+
url: page.link
|
|
1592
|
+
});
|
|
1593
|
+
});
|
|
1594
|
+
}
|
|
1595
|
+
catch (error) {
|
|
1596
|
+
console.error(`Failed to fetch pages with status ${status}: ${error.message}`);
|
|
1597
|
+
}
|
|
1598
|
+
}
|
|
1599
|
+
// Sort by ID
|
|
1600
|
+
allContent.sort((a, b) => a.id - b.id);
|
|
1601
|
+
// Generate summary
|
|
1602
|
+
const summary = {
|
|
1603
|
+
total: allContent.length,
|
|
1604
|
+
by_type: {
|
|
1605
|
+
posts: allContent.filter(item => item.type === 'post').length,
|
|
1606
|
+
pages: allContent.filter(item => item.type === 'page').length
|
|
1607
|
+
},
|
|
1608
|
+
by_elementor_status: {
|
|
1609
|
+
full: allContent.filter(item => item.elementor_status === 'full').length,
|
|
1610
|
+
partial: allContent.filter(item => item.elementor_status === 'partial').length,
|
|
1611
|
+
none: allContent.filter(item => item.elementor_status === 'none').length
|
|
1612
|
+
},
|
|
1613
|
+
by_status: {}
|
|
1614
|
+
};
|
|
1615
|
+
// Count by status
|
|
1616
|
+
allContent.forEach(item => {
|
|
1617
|
+
summary.by_status[item.status] = (summary.by_status[item.status] || 0) + 1;
|
|
1618
|
+
});
|
|
1619
|
+
// Format table output
|
|
1620
|
+
let formattedTable = `📊 Content Summary\n`;
|
|
1621
|
+
formattedTable += `Total items: ${summary.total}\n`;
|
|
1622
|
+
formattedTable += `Posts: ${summary.by_type.posts}, Pages: ${summary.by_type.pages}\n`;
|
|
1623
|
+
formattedTable += `Elementor: Full (${summary.by_elementor_status.full}), Partial (${summary.by_elementor_status.partial}), None (${summary.by_elementor_status.none})\n`;
|
|
1624
|
+
formattedTable += `Statuses: ${Object.entries(summary.by_status).map(([status, count]) => `${status} (${count})`).join(', ')}\n\n`;
|
|
1625
|
+
formattedTable += `📋 Content List\n`;
|
|
1626
|
+
formattedTable += `${'ID'.padEnd(6)} ${'Type'.padEnd(5)} ${'Status'.padEnd(8)} ${'Elementor'.padEnd(9)} Title\n`;
|
|
1627
|
+
formattedTable += `${'─'.repeat(6)} ${'─'.repeat(5)} ${'─'.repeat(8)} ${'─'.repeat(9)} ${'─'.repeat(50)}\n`;
|
|
1628
|
+
allContent.forEach(item => {
|
|
1629
|
+
const elementorIcon = item.elementor_status === 'full' ? '✅' : item.elementor_status === 'partial' ? '⚠️' : '❌';
|
|
1630
|
+
const title = item.title.length > 45 ? item.title.substring(0, 42) + '...' : item.title;
|
|
1631
|
+
formattedTable += `${item.id.toString().padEnd(6)} ${item.type.padEnd(5)} ${item.status.padEnd(8)} ${(elementorIcon + ' ' + item.elementor_status).padEnd(9)} ${title}\n`;
|
|
1632
|
+
});
|
|
1633
|
+
return this.createSuccessResponse({
|
|
1634
|
+
summary,
|
|
1635
|
+
content: allContent,
|
|
1636
|
+
formatted_table: formattedTable
|
|
1637
|
+
}, `Successfully retrieved ${allContent.length} content items`);
|
|
1638
|
+
}
|
|
1639
|
+
catch (error) {
|
|
1640
|
+
console.error(`Error listing all content: ${error.message}`);
|
|
1641
|
+
return this.createErrorResponse(`Failed to list content: ${error.response?.status} ${error.response?.statusText} - ${error.response?.data?.message || error.message}`, "LIST_CONTENT_ERROR", "API_ERROR", `HTTP ${error.response?.status}: ${error.response?.statusText}`);
|
|
1642
|
+
}
|
|
1643
|
+
}
|
|
1644
|
+
async createPage(args) {
|
|
1645
|
+
const authCheck = this.ensureAuthenticated();
|
|
1646
|
+
if (authCheck)
|
|
1647
|
+
return authCheck;
|
|
1648
|
+
const pageData = {
|
|
1649
|
+
title: args.title,
|
|
1650
|
+
content: args.content,
|
|
1651
|
+
status: args.status || 'draft',
|
|
1652
|
+
...(args.excerpt && { excerpt: args.excerpt }),
|
|
1653
|
+
...(args.parent && { parent: args.parent }),
|
|
1654
|
+
};
|
|
1655
|
+
try {
|
|
1656
|
+
console.error(`Creating page with title: "${args.title}"`);
|
|
1657
|
+
const response = await this.axiosInstance.post('pages', pageData);
|
|
1658
|
+
// Clear Elementor cache after creating page
|
|
1659
|
+
await this.clearElementorCache(response.data.id);
|
|
1660
|
+
return this.createSuccessResponse({
|
|
1661
|
+
page: response.data,
|
|
1662
|
+
cache_cleared: true
|
|
1663
|
+
}, `Page created successfully! ID: ${response.data.id}, Status: ${response.data.status}`);
|
|
1664
|
+
}
|
|
1665
|
+
catch (error) {
|
|
1666
|
+
console.error(`Error creating page: ${error.response?.status} - ${error.response?.statusText}`);
|
|
1667
|
+
console.error(`URL: ${error.config?.url}`);
|
|
1668
|
+
console.error(`Headers: ${JSON.stringify(error.config?.headers)}`);
|
|
1669
|
+
return this.createErrorResponse(`Failed to create page: ${error.response?.status} ${error.response?.statusText} - ${error.response?.data?.message || error.message}`, 'CREATE_PAGE_ERROR', 'API_ERROR', `HTTP ${error.response?.status}: ${error.response?.statusText}`);
|
|
1670
|
+
}
|
|
1671
|
+
}
|
|
1672
|
+
async updatePage(args) {
|
|
1673
|
+
const authCheck = this.ensureAuthenticated();
|
|
1674
|
+
if (authCheck)
|
|
1675
|
+
return authCheck;
|
|
1676
|
+
const updateData = {};
|
|
1677
|
+
if (args.title)
|
|
1678
|
+
updateData.title = args.title;
|
|
1679
|
+
if (args.content)
|
|
1680
|
+
updateData.content = args.content;
|
|
1681
|
+
if (args.status)
|
|
1682
|
+
updateData.status = args.status;
|
|
1683
|
+
if (args.excerpt)
|
|
1684
|
+
updateData.excerpt = args.excerpt;
|
|
1685
|
+
if (args.parent !== undefined)
|
|
1686
|
+
updateData.parent = args.parent;
|
|
1687
|
+
try {
|
|
1688
|
+
console.error(`Updating page ID: ${args.id}`);
|
|
1689
|
+
const response = await this.axiosInstance.post(`pages/${args.id}`, updateData);
|
|
1690
|
+
// Clear Elementor cache after updating page
|
|
1691
|
+
await this.clearElementorCache(args.id);
|
|
1692
|
+
return this.createSuccessResponse({
|
|
1693
|
+
page: response.data,
|
|
1694
|
+
cache_cleared: true
|
|
1695
|
+
}, `Page updated successfully! ID: ${response.data.id}, Status: ${response.data.status}`);
|
|
1696
|
+
}
|
|
1697
|
+
catch (error) {
|
|
1698
|
+
console.error(`Error updating page ${args.id}: ${error.response?.status} - ${error.response?.statusText}`);
|
|
1699
|
+
console.error(`URL: ${error.config?.url}`);
|
|
1700
|
+
console.error(`Headers: ${JSON.stringify(error.config?.headers)}`);
|
|
1701
|
+
if (error.response?.status === 404) {
|
|
1702
|
+
return this.createErrorResponse(`Page with ID ${args.id} not found. The page may have been deleted, be in trash, or may not exist.`, 'PAGE_NOT_FOUND', 'NOT_FOUND', `HTTP 404: Page ID ${args.id} not accessible`);
|
|
1703
|
+
}
|
|
1704
|
+
return this.createErrorResponse(`Failed to update page ${args.id}: ${error.response?.status} ${error.response?.statusText} - ${error.response?.data?.message || error.message}`, 'UPDATE_PAGE_ERROR', 'API_ERROR', `HTTP ${error.response?.status}: ${error.response?.statusText}`);
|
|
1705
|
+
}
|
|
1706
|
+
}
|
|
1707
|
+
async getElementorTemplates(args) {
|
|
1708
|
+
const authCheck = this.ensureAuthenticated();
|
|
1709
|
+
if (authCheck)
|
|
1710
|
+
return authCheck;
|
|
1711
|
+
const params = {
|
|
1712
|
+
per_page: args.per_page || 10,
|
|
1713
|
+
meta_key: '_elementor_template_type',
|
|
1714
|
+
};
|
|
1715
|
+
if (args.type) {
|
|
1716
|
+
params.meta_value = args.type;
|
|
1717
|
+
}
|
|
1718
|
+
try {
|
|
1719
|
+
const response = await this.axiosInstance.get('elementor_library', { params });
|
|
1720
|
+
// Create optimized summary objects instead of returning full content
|
|
1721
|
+
const templates = response.data;
|
|
1722
|
+
const templateSummaries = templates.map((template) => ({
|
|
1723
|
+
id: template.id,
|
|
1724
|
+
title: template.title?.rendered || '(No title)',
|
|
1725
|
+
template_type: template.meta?._elementor_template_type || 'unknown',
|
|
1726
|
+
date_created: template.date,
|
|
1727
|
+
date_modified: template.modified,
|
|
1728
|
+
status: template.status,
|
|
1729
|
+
author: template.author,
|
|
1730
|
+
// Basic template info without heavy Elementor data
|
|
1731
|
+
has_elementor_data: !!template.meta?._elementor_data,
|
|
1732
|
+
element_count: template.meta?._elementor_data ?
|
|
1733
|
+
(JSON.parse(template.meta._elementor_data)?.length || 0) : 0
|
|
1734
|
+
}));
|
|
1735
|
+
return this.createSuccessResponse({
|
|
1736
|
+
templates: templateSummaries, // Return summaries instead of full objects
|
|
1737
|
+
count: templateSummaries.length,
|
|
1738
|
+
filter_type: args.type || 'all',
|
|
1739
|
+
performance_note: "Optimized response - use get_elementor_data(template_id) for full template content"
|
|
1740
|
+
}, `Retrieved ${templateSummaries.length} Elementor template summaries`);
|
|
1741
|
+
}
|
|
1742
|
+
catch (error) {
|
|
1743
|
+
if (error.response?.status === 404) {
|
|
1744
|
+
return this.createErrorResponse('Elementor templates endpoint not found. Make sure Elementor Pro is installed and activated.', 'TEMPLATES_NOT_FOUND', 'NOT_FOUND', 'Elementor Pro required for template access');
|
|
1745
|
+
}
|
|
1746
|
+
return this.createErrorResponse(`Failed to get Elementor templates: ${error.message}`, 'GET_TEMPLATES_ERROR', 'API_ERROR', `HTTP ${error.response?.status}: ${error.response?.statusText}`);
|
|
1747
|
+
}
|
|
1748
|
+
}
|
|
1749
|
+
async getElementorData(args) {
|
|
1750
|
+
const authCheck = this.ensureAuthenticated();
|
|
1751
|
+
if (authCheck)
|
|
1752
|
+
return authCheck;
|
|
1753
|
+
return this.safeApiCall(async () => {
|
|
1754
|
+
console.error(`Getting Elementor data for ID: ${args.post_id}`);
|
|
1755
|
+
// Try to get as post first, then as page if that fails
|
|
1756
|
+
let response;
|
|
1757
|
+
let postType = 'post';
|
|
1758
|
+
let debugInfo = '';
|
|
1759
|
+
try {
|
|
1760
|
+
console.error(`Trying to fetch as post: posts/${args.post_id}`);
|
|
1761
|
+
response = await this.axiosInstance.get(`posts/${args.post_id}`, {
|
|
1762
|
+
params: { context: 'edit' }
|
|
1763
|
+
});
|
|
1764
|
+
debugInfo += `Found as post (ID: ${args.post_id})\n`;
|
|
1765
|
+
}
|
|
1766
|
+
catch (postError) {
|
|
1767
|
+
console.error(`Post fetch failed: ${postError.response?.status} - ${postError.response?.statusText}`);
|
|
1768
|
+
if (postError.response?.status === 404) {
|
|
1769
|
+
// Try as page
|
|
1770
|
+
try {
|
|
1771
|
+
console.error(`Trying to fetch as page: pages/${args.post_id}`);
|
|
1772
|
+
response = await this.axiosInstance.get(`pages/${args.post_id}`, {
|
|
1773
|
+
params: { context: 'edit' }
|
|
1774
|
+
});
|
|
1775
|
+
postType = 'page';
|
|
1776
|
+
debugInfo += `Found as page (ID: ${args.post_id})\n`;
|
|
1777
|
+
}
|
|
1778
|
+
catch (pageError) {
|
|
1779
|
+
console.error(`Page fetch failed: ${pageError.response?.status} - ${pageError.response?.statusText}`);
|
|
1780
|
+
// Provide comprehensive error message
|
|
1781
|
+
const errorDetails = `
|
|
1782
|
+
❌ Post/Page ID ${args.post_id} not found
|
|
1783
|
+
|
|
1784
|
+
Debug Information:
|
|
1785
|
+
- Tried as post: ${postError.response?.status} ${postError.response?.statusText}
|
|
1786
|
+
- Tried as page: ${pageError.response?.status} ${pageError.response?.statusText}
|
|
1787
|
+
|
|
1788
|
+
Suggestions:
|
|
1789
|
+
1. Verify the ID exists by running get_posts or get_pages
|
|
1790
|
+
2. Check if the ID might be a custom post type
|
|
1791
|
+
3. Ensure the post/page is not trashed
|
|
1792
|
+
4. Verify your user permissions include access to this content
|
|
1793
|
+
`;
|
|
1794
|
+
return this.createErrorResponse(errorDetails.trim(), "POST_PAGE_NOT_FOUND", "NOT_FOUND", "Post/Page ID not found in either posts or pages endpoints");
|
|
1795
|
+
}
|
|
1796
|
+
}
|
|
1797
|
+
else {
|
|
1798
|
+
throw postError;
|
|
1799
|
+
}
|
|
1800
|
+
}
|
|
1801
|
+
// Analyze the response for Elementor data
|
|
1802
|
+
const data = response.data;
|
|
1803
|
+
console.error(`Response received for ${postType} ${args.post_id}`);
|
|
1804
|
+
console.error(`Meta keys available: ${data.meta ? Object.keys(data.meta).join(', ') : 'None'}`);
|
|
1805
|
+
const elementorData = data.meta?._elementor_data;
|
|
1806
|
+
const elementorEditMode = data.meta?._elementor_edit_mode;
|
|
1807
|
+
const elementorVersion = data.meta?._elementor_version;
|
|
1808
|
+
const elementorPageSettings = data.meta?._elementor_page_settings;
|
|
1809
|
+
debugInfo += `Title: "${data.title.rendered}"\n`;
|
|
1810
|
+
debugInfo += `Status: ${data.status}\n`;
|
|
1811
|
+
debugInfo += `Type: ${postType}\n`;
|
|
1812
|
+
debugInfo += `Edit Mode: ${elementorEditMode || 'None'}\n`;
|
|
1813
|
+
debugInfo += `Version: ${elementorVersion || 'None'}\n`;
|
|
1814
|
+
debugInfo += `Has Page Settings: ${elementorPageSettings ? 'Yes' : 'No'}\n`;
|
|
1815
|
+
debugInfo += `Has Elementor Data: ${elementorData ? 'Yes' : 'No'}\n`;
|
|
1816
|
+
if (elementorData) {
|
|
1817
|
+
try {
|
|
1818
|
+
const parsedData = JSON.parse(elementorData);
|
|
1819
|
+
debugInfo += `Elementor Elements Count: ${Array.isArray(parsedData) ? parsedData.length : 'Not an array'}\n`;
|
|
1820
|
+
return this.createSuccessResponse({
|
|
1821
|
+
post_id: args.post_id,
|
|
1822
|
+
post_type: postType,
|
|
1823
|
+
title: data.title?.rendered || data.title?.raw || 'Unknown',
|
|
1824
|
+
status: data.status,
|
|
1825
|
+
edit_mode: elementorEditMode,
|
|
1826
|
+
elementor_data: parsedData,
|
|
1827
|
+
metadata: {
|
|
1828
|
+
has_page_settings: !!data.meta?._elementor_page_settings,
|
|
1829
|
+
has_elementor_data: true,
|
|
1830
|
+
elements_count: Array.isArray(parsedData) ? parsedData.length : 0,
|
|
1831
|
+
version: elementorVersion
|
|
1832
|
+
}
|
|
1833
|
+
}, `Successfully retrieved Elementor data for ${postType} ID ${args.post_id} with ${Array.isArray(parsedData) ? parsedData.length : 0} elements`);
|
|
1834
|
+
}
|
|
1835
|
+
catch (parseError) {
|
|
1836
|
+
debugInfo += `⚠️ Elementor data found but failed to parse JSON\n`;
|
|
1837
|
+
return this.createErrorResponse(`Failed to parse Elementor data for ${postType} ID ${args.post_id}: JSON parsing error`, 'PARSE_ERROR', 'DATA_FORMAT_ERROR', `${debugInfo}\nRaw data: ${elementorData?.substring(0, 200)}...`);
|
|
1838
|
+
}
|
|
1839
|
+
}
|
|
1840
|
+
else {
|
|
1841
|
+
// Check if this is an Elementor page without data
|
|
1842
|
+
if (elementorEditMode === 'builder') {
|
|
1843
|
+
debugInfo += `\n⚠️ This appears to be an Elementor page but has no data.\n`;
|
|
1844
|
+
debugInfo += `Possible reasons:\n`;
|
|
1845
|
+
debugInfo += `- Empty Elementor page\n`;
|
|
1846
|
+
debugInfo += `- Cache/synchronization issue\n`;
|
|
1847
|
+
debugInfo += `- Elementor data stored differently\n`;
|
|
1848
|
+
}
|
|
1849
|
+
else {
|
|
1850
|
+
debugInfo += `\n❌ This ${postType} does not use Elementor builder.\n`;
|
|
1851
|
+
}
|
|
1852
|
+
return this.createErrorResponse(`No Elementor data found for ${postType} ID ${args.post_id}`, 'NO_ELEMENTOR_DATA', 'DATA_NOT_FOUND', `${debugInfo}\nAvailable meta keys: ${data.meta ? Object.keys(data.meta).join(', ') : 'None'}`);
|
|
1853
|
+
}
|
|
1854
|
+
}, 'getElementorData', `post/page ID ${args.post_id}`);
|
|
1855
|
+
}
|
|
1856
|
+
async clearElementorCache(postId) {
|
|
1857
|
+
try {
|
|
1858
|
+
console.error('Attempting to clear Elementor cache...');
|
|
1859
|
+
// Method 1: Clear specific Elementor meta that forces regeneration
|
|
1860
|
+
if (postId) {
|
|
1861
|
+
try {
|
|
1862
|
+
console.error(`Clearing Elementor cache meta for post ${postId}...`);
|
|
1863
|
+
// Try to get current page/post
|
|
1864
|
+
let currentData;
|
|
1865
|
+
let isPage = false;
|
|
1866
|
+
try {
|
|
1867
|
+
currentData = await this.axiosInstance.get(`pages/${postId}`);
|
|
1868
|
+
isPage = true;
|
|
1869
|
+
}
|
|
1870
|
+
catch {
|
|
1871
|
+
currentData = await this.axiosInstance.get(`posts/${postId}`);
|
|
1872
|
+
}
|
|
1873
|
+
// Update meta to force Elementor cache regeneration
|
|
1874
|
+
const cacheBreakMeta = {
|
|
1875
|
+
meta: {
|
|
1876
|
+
_elementor_css: '', // Clear generated CSS cache
|
|
1877
|
+
_elementor_page_settings: currentData.data.meta._elementor_page_settings || '',
|
|
1878
|
+
_elementor_edit_mode: 'builder',
|
|
1879
|
+
_elementor_version: '3.0.0', // Force version update
|
|
1880
|
+
_elementor_cache_bust: Date.now().toString()
|
|
1881
|
+
}
|
|
1882
|
+
};
|
|
1883
|
+
if (isPage) {
|
|
1884
|
+
await this.axiosInstance.post(`pages/${postId}`, cacheBreakMeta);
|
|
1885
|
+
}
|
|
1886
|
+
else {
|
|
1887
|
+
await this.axiosInstance.post(`posts/${postId}`, cacheBreakMeta);
|
|
1888
|
+
}
|
|
1889
|
+
console.error(`Elementor cache meta cleared for post ${postId}`);
|
|
1890
|
+
}
|
|
1891
|
+
catch (error) {
|
|
1892
|
+
console.error(`Failed to clear post-specific cache: ${error.message}`);
|
|
1893
|
+
}
|
|
1894
|
+
}
|
|
1895
|
+
// Method 2: Try WordPress cache clearing endpoints
|
|
1896
|
+
const cacheEndpoints = [
|
|
1897
|
+
'wp/v2/elementor/flush',
|
|
1898
|
+
'wp/v2/settings', // Sometimes triggers cache clear
|
|
1899
|
+
'elementor/v1/flush-css',
|
|
1900
|
+
'elementor/v1/clear-cache'
|
|
1901
|
+
];
|
|
1902
|
+
for (const endpoint of cacheEndpoints) {
|
|
1903
|
+
try {
|
|
1904
|
+
console.error(`Trying cache clear endpoint: ${endpoint}`);
|
|
1905
|
+
if (endpoint === 'wp/v2/settings') {
|
|
1906
|
+
// Force settings update to trigger cache clear
|
|
1907
|
+
await this.axiosInstance.post(endpoint, {
|
|
1908
|
+
elementor_cache_time: Date.now().toString()
|
|
1909
|
+
});
|
|
1910
|
+
}
|
|
1911
|
+
else {
|
|
1912
|
+
await this.axiosInstance.post(endpoint, {});
|
|
1913
|
+
}
|
|
1914
|
+
console.error(`Cache clear attempted via ${endpoint}`);
|
|
1915
|
+
}
|
|
1916
|
+
catch (error) {
|
|
1917
|
+
console.error(`Cache clear failed via ${endpoint}: ${error.response?.status || error.message}`);
|
|
1918
|
+
}
|
|
1919
|
+
}
|
|
1920
|
+
// Method 3: Update Elementor global settings to force regeneration
|
|
1921
|
+
try {
|
|
1922
|
+
console.error('Forcing Elementor regeneration via options...');
|
|
1923
|
+
// Update multiple Elementor options that can trigger cache clear
|
|
1924
|
+
const optionUpdates = [
|
|
1925
|
+
{ elementor_css_print_method: 'internal' },
|
|
1926
|
+
{ elementor_cpt_support: ['page', 'post'] },
|
|
1927
|
+
{ elementor_disable_color_schemes: '' },
|
|
1928
|
+
{ elementor_disable_typography_schemes: '' },
|
|
1929
|
+
{ elementor_cache_files_time: Date.now().toString() }
|
|
1930
|
+
];
|
|
1931
|
+
for (const option of optionUpdates) {
|
|
1932
|
+
try {
|
|
1933
|
+
await this.axiosInstance.post('wp/v2/options', option);
|
|
1934
|
+
}
|
|
1935
|
+
catch (error) {
|
|
1936
|
+
console.error(`Option update failed: ${error.message}`);
|
|
1937
|
+
}
|
|
1938
|
+
}
|
|
1939
|
+
console.error('Elementor options updated to force regeneration');
|
|
1940
|
+
}
|
|
1941
|
+
catch (error) {
|
|
1942
|
+
console.error(`Failed to update Elementor options: ${error.message}`);
|
|
1943
|
+
}
|
|
1944
|
+
// Method 4: Force Elementor to rebuild by clearing ALL cache-related meta
|
|
1945
|
+
if (postId) {
|
|
1946
|
+
try {
|
|
1947
|
+
console.error('Forcing complete Elementor rebuild...');
|
|
1948
|
+
// Get current data again
|
|
1949
|
+
let currentData;
|
|
1950
|
+
let isPage = false;
|
|
1951
|
+
try {
|
|
1952
|
+
currentData = await this.axiosInstance.get(`pages/${postId}`);
|
|
1953
|
+
isPage = true;
|
|
1954
|
+
}
|
|
1955
|
+
catch {
|
|
1956
|
+
currentData = await this.axiosInstance.get(`posts/${postId}`);
|
|
1957
|
+
}
|
|
1958
|
+
// Force complete rebuild by clearing all Elementor cache meta
|
|
1959
|
+
const forceRebuildMeta = {
|
|
1960
|
+
meta: {
|
|
1961
|
+
_elementor_css: '',
|
|
1962
|
+
_elementor_page_assets: '',
|
|
1963
|
+
_elementor_controls_usage: '',
|
|
1964
|
+
_elementor_css_file: '',
|
|
1965
|
+
_elementor_inline_css: '',
|
|
1966
|
+
_elementor_template_type: '',
|
|
1967
|
+
_elementor_edit_mode: 'builder',
|
|
1968
|
+
_elementor_version: '3.20.0',
|
|
1969
|
+
_elementor_pro_version: '3.20.0',
|
|
1970
|
+
_elementor_data: currentData.data.meta._elementor_data, // Keep the data but force rebuild
|
|
1971
|
+
_elementor_cache_bust: Date.now().toString(),
|
|
1972
|
+
_elementor_force_rebuild: 'yes'
|
|
1973
|
+
}
|
|
1974
|
+
};
|
|
1975
|
+
if (isPage) {
|
|
1976
|
+
await this.axiosInstance.post(`pages/${postId}`, forceRebuildMeta);
|
|
1977
|
+
}
|
|
1978
|
+
else {
|
|
1979
|
+
await this.axiosInstance.post(`posts/${postId}`, forceRebuildMeta);
|
|
1980
|
+
}
|
|
1981
|
+
console.error('Complete Elementor rebuild forced');
|
|
1982
|
+
}
|
|
1983
|
+
catch (error) {
|
|
1984
|
+
console.error(`Force rebuild failed: ${error.message}`);
|
|
1985
|
+
}
|
|
1986
|
+
}
|
|
1987
|
+
// Method 5: Try to trigger WordPress object cache flush
|
|
1988
|
+
try {
|
|
1989
|
+
console.error('Attempting WordPress object cache flush...');
|
|
1990
|
+
await this.axiosInstance.post('wp/v2/posts', {
|
|
1991
|
+
title: `Cache Flush Trigger ${Date.now()}`,
|
|
1992
|
+
content: 'Cache flush trigger',
|
|
1993
|
+
status: 'draft'
|
|
1994
|
+
});
|
|
1995
|
+
console.error('Cache flush trigger post created');
|
|
1996
|
+
}
|
|
1997
|
+
catch (error) {
|
|
1998
|
+
console.error(`Cache flush trigger failed: ${error.message}`);
|
|
1999
|
+
}
|
|
2000
|
+
// Method 6: Try to force WordPress transient cache clear
|
|
2001
|
+
try {
|
|
2002
|
+
console.error('Attempting transient cache clear...');
|
|
2003
|
+
await this.axiosInstance.post('wp/v2/options', {
|
|
2004
|
+
elementor_transient_clear: Date.now().toString()
|
|
2005
|
+
});
|
|
2006
|
+
console.error('Transient cache clear attempted');
|
|
2007
|
+
}
|
|
2008
|
+
catch (error) {
|
|
2009
|
+
console.error(`Transient cache clear failed: ${error.message}`);
|
|
2010
|
+
}
|
|
2011
|
+
console.error('Aggressive Elementor cache clearing sequence completed');
|
|
2012
|
+
}
|
|
2013
|
+
catch (error) {
|
|
2014
|
+
console.error('Cache clearing error:', error.message);
|
|
2015
|
+
}
|
|
2016
|
+
}
|
|
2017
|
+
async updateElementorData(args) {
|
|
2018
|
+
const authCheck = this.ensureAuthenticated();
|
|
2019
|
+
if (authCheck)
|
|
2020
|
+
return authCheck;
|
|
2021
|
+
try {
|
|
2022
|
+
// Update the post meta with Elementor data
|
|
2023
|
+
const updateData = {
|
|
2024
|
+
meta: {
|
|
2025
|
+
_elementor_data: args.elementor_data,
|
|
2026
|
+
_elementor_edit_mode: 'builder',
|
|
2027
|
+
},
|
|
2028
|
+
};
|
|
2029
|
+
// Try to update as post first, then as page if that fails
|
|
2030
|
+
let response;
|
|
2031
|
+
let postType = 'post';
|
|
2032
|
+
try {
|
|
2033
|
+
response = await this.axiosInstance.post(`posts/${args.post_id}`, updateData);
|
|
2034
|
+
}
|
|
2035
|
+
catch (postError) {
|
|
2036
|
+
if (postError.response?.status === 404) {
|
|
2037
|
+
// Try as page
|
|
2038
|
+
try {
|
|
2039
|
+
response = await this.axiosInstance.post(`pages/${args.post_id}`, updateData);
|
|
2040
|
+
postType = 'page';
|
|
2041
|
+
}
|
|
2042
|
+
catch (pageError) {
|
|
2043
|
+
return this.createErrorResponse(`Post/Page ID ${args.post_id} not found in posts or pages`, 'POST_PAGE_NOT_FOUND', 'NOT_FOUND', 'Failed to update both post and page endpoints');
|
|
2044
|
+
}
|
|
2045
|
+
}
|
|
2046
|
+
else {
|
|
2047
|
+
return this.createErrorResponse(`Failed to update post: ${postError.response?.data?.message || postError.message}`, 'UPDATE_POST_ERROR', 'API_ERROR', `HTTP ${postError.response?.status}: ${postError.response?.statusText}`);
|
|
2048
|
+
}
|
|
2049
|
+
}
|
|
2050
|
+
// Clear Elementor cache after updating data
|
|
2051
|
+
await this.clearElementorCache(args.post_id);
|
|
2052
|
+
return this.createSuccessResponse({
|
|
2053
|
+
post_type: postType,
|
|
2054
|
+
post_id: args.post_id,
|
|
2055
|
+
cache_cleared: true,
|
|
2056
|
+
updated_data: true
|
|
2057
|
+
}, `Elementor data updated successfully for ${postType} ID: ${args.post_id}`);
|
|
2058
|
+
}
|
|
2059
|
+
catch (error) {
|
|
2060
|
+
return this.createErrorResponse(`Failed to update Elementor data: ${error.response?.data?.message || error.message}`, 'UPDATE_ELEMENTOR_DATA_ERROR', 'API_ERROR', `HTTP ${error.response?.status}: ${error.response?.statusText}`);
|
|
2061
|
+
}
|
|
2062
|
+
}
|
|
2063
|
+
async updateElementorWidget(args) {
|
|
2064
|
+
const authCheck = this.ensureAuthenticated();
|
|
2065
|
+
if (authCheck)
|
|
2066
|
+
return authCheck;
|
|
2067
|
+
try {
|
|
2068
|
+
// Get current Elementor data using safe parsing utility
|
|
2069
|
+
const parsedResult = await this.safeGetElementorData(args.post_id);
|
|
2070
|
+
if (!parsedResult.success || !parsedResult.data) {
|
|
2071
|
+
return this.createErrorResponse(`Failed to get Elementor data for post/page ID ${args.post_id}: ${parsedResult.error || 'Unknown error'}`, 'ELEMENTOR_DATA_ERROR', 'DATA_ERROR', 'Could not retrieve or parse Elementor data for widget update');
|
|
2072
|
+
}
|
|
2073
|
+
const elementorData = parsedResult.data;
|
|
2074
|
+
// Function to recursively find and update widget
|
|
2075
|
+
const updateWidgetRecursive = (elements) => {
|
|
2076
|
+
for (let i = 0; i < elements.length; i++) {
|
|
2077
|
+
const element = elements[i];
|
|
2078
|
+
if (element.id === args.widget_id) {
|
|
2079
|
+
// Found the widget, update it
|
|
2080
|
+
if (args.widget_settings) {
|
|
2081
|
+
element.settings = { ...element.settings, ...args.widget_settings };
|
|
2082
|
+
}
|
|
2083
|
+
// Special handling for HTML widget content
|
|
2084
|
+
if (args.widget_content && element.widgetType === 'html') {
|
|
2085
|
+
element.settings.html = args.widget_content;
|
|
2086
|
+
}
|
|
2087
|
+
// Special handling for text widget content
|
|
2088
|
+
if (args.widget_content && element.widgetType === 'text-editor') {
|
|
2089
|
+
element.settings.editor = args.widget_content;
|
|
2090
|
+
}
|
|
2091
|
+
// Special handling for heading widget content
|
|
2092
|
+
if (args.widget_content && element.widgetType === 'heading') {
|
|
2093
|
+
element.settings.title = args.widget_content;
|
|
2094
|
+
}
|
|
2095
|
+
return true;
|
|
2096
|
+
}
|
|
2097
|
+
// Recursively search in nested elements
|
|
2098
|
+
if (element.elements && element.elements.length > 0) {
|
|
2099
|
+
if (updateWidgetRecursive(element.elements)) {
|
|
2100
|
+
return true;
|
|
2101
|
+
}
|
|
2102
|
+
}
|
|
2103
|
+
}
|
|
2104
|
+
return false;
|
|
2105
|
+
};
|
|
2106
|
+
// Find and update the widget
|
|
2107
|
+
const widgetFound = updateWidgetRecursive(elementorData);
|
|
2108
|
+
if (!widgetFound) {
|
|
2109
|
+
return this.createErrorResponse(`Widget ID ${args.widget_id} not found in Elementor data`, 'WIDGET_NOT_FOUND', 'NOT_FOUND', `Could not locate widget with ID ${args.widget_id} in the page structure`);
|
|
2110
|
+
}
|
|
2111
|
+
// Update the page with modified data
|
|
2112
|
+
const updateData = {
|
|
2113
|
+
meta: {
|
|
2114
|
+
_elementor_data: JSON.stringify(elementorData),
|
|
2115
|
+
_elementor_edit_mode: 'builder',
|
|
2116
|
+
},
|
|
2117
|
+
};
|
|
2118
|
+
// Try to update as post first, then as page if that fails
|
|
2119
|
+
let response;
|
|
2120
|
+
let postType = 'post';
|
|
2121
|
+
try {
|
|
2122
|
+
response = await this.axiosInstance.post(`posts/${args.post_id}`, updateData);
|
|
2123
|
+
}
|
|
2124
|
+
catch (postError) {
|
|
2125
|
+
if (postError.response?.status === 404) {
|
|
2126
|
+
// Try as page
|
|
2127
|
+
try {
|
|
2128
|
+
response = await this.axiosInstance.post(`pages/${args.post_id}`, updateData);
|
|
2129
|
+
postType = 'page';
|
|
2130
|
+
}
|
|
2131
|
+
catch (pageError) {
|
|
2132
|
+
return this.createErrorResponse(`Post/Page ID ${args.post_id} not found in posts or pages`, 'POST_PAGE_NOT_FOUND', 'NOT_FOUND', 'Failed to update both post and page endpoints for widget update');
|
|
2133
|
+
}
|
|
2134
|
+
}
|
|
2135
|
+
else {
|
|
2136
|
+
return this.createErrorResponse(`Failed to update post: ${postError.response?.data?.message || postError.message}`, 'UPDATE_POST_ERROR', 'API_ERROR', `HTTP ${postError.response?.status}: ${postError.response?.statusText}`);
|
|
2137
|
+
}
|
|
2138
|
+
}
|
|
2139
|
+
// Clear Elementor cache after updating data
|
|
2140
|
+
await this.clearElementorCache(args.post_id);
|
|
2141
|
+
return this.createSuccessResponse({
|
|
2142
|
+
widget_id: args.widget_id,
|
|
2143
|
+
post_type: postType,
|
|
2144
|
+
post_id: args.post_id,
|
|
2145
|
+
cache_cleared: true,
|
|
2146
|
+
updated_settings: !!args.widget_settings,
|
|
2147
|
+
updated_content: !!args.widget_content
|
|
2148
|
+
}, `Elementor widget ${args.widget_id} updated successfully for ${postType} ID: ${args.post_id}`);
|
|
2149
|
+
}
|
|
2150
|
+
catch (error) {
|
|
2151
|
+
return this.createErrorResponse(`Failed to update Elementor widget: ${error.response?.data?.message || error.message}`, 'UPDATE_WIDGET_ERROR', 'API_ERROR', `HTTP ${error.response?.status}: ${error.response?.statusText}`);
|
|
2152
|
+
}
|
|
2153
|
+
}
|
|
2154
|
+
async getElementorWidget(args) {
|
|
2155
|
+
const authCheck = this.ensureAuthenticated();
|
|
2156
|
+
if (authCheck)
|
|
2157
|
+
return authCheck;
|
|
2158
|
+
try {
|
|
2159
|
+
console.error(`🔍 Getting widget ${args.widget_id} from post ID: ${args.post_id}`);
|
|
2160
|
+
// Get current Elementor data using safe parsing utility
|
|
2161
|
+
console.error(`🔍 Getting Elementor data for widget search in post ID: ${args.post_id}`);
|
|
2162
|
+
const parsedResult = await this.safeGetElementorData(args.post_id);
|
|
2163
|
+
if (!parsedResult.success || !parsedResult.data) {
|
|
2164
|
+
console.error(`❌ Failed to get Elementor data: ${parsedResult.error}`);
|
|
2165
|
+
return this.createErrorResponse(`Failed to retrieve Elementor data for post/page ID ${args.post_id}: ${parsedResult.error || 'Unknown error'}`, 'ELEMENTOR_DATA_ERROR', 'DATA_ERROR', 'Could not retrieve or parse Elementor data for widget retrieval');
|
|
2166
|
+
}
|
|
2167
|
+
const elementorData = parsedResult.data;
|
|
2168
|
+
console.error(`✅ Successfully parsed data, searching through ${elementorData.length} top-level elements for widget ${args.widget_id}`);
|
|
2169
|
+
// Function to recursively find widget
|
|
2170
|
+
const findWidgetRecursive = (elements) => {
|
|
2171
|
+
for (const element of elements) {
|
|
2172
|
+
if (element.id === args.widget_id) {
|
|
2173
|
+
return element;
|
|
2174
|
+
}
|
|
2175
|
+
// Recursively search in nested elements
|
|
2176
|
+
if (element.elements && element.elements.length > 0) {
|
|
2177
|
+
const found = findWidgetRecursive(element.elements);
|
|
2178
|
+
if (found)
|
|
2179
|
+
return found;
|
|
2180
|
+
}
|
|
2181
|
+
}
|
|
2182
|
+
return null;
|
|
2183
|
+
};
|
|
2184
|
+
// Find the widget
|
|
2185
|
+
const widget = findWidgetRecursive(elementorData);
|
|
2186
|
+
if (!widget) {
|
|
2187
|
+
return this.createErrorResponse(`Widget ID ${args.widget_id} not found in Elementor data`, 'WIDGET_NOT_FOUND', 'NOT_FOUND', `Could not locate widget with ID ${args.widget_id} in the page structure`);
|
|
2188
|
+
}
|
|
2189
|
+
return this.createSuccessResponse({
|
|
2190
|
+
widget: widget,
|
|
2191
|
+
widget_id: args.widget_id,
|
|
2192
|
+
post_id: args.post_id
|
|
2193
|
+
}, `Widget ${args.widget_id} retrieved successfully`);
|
|
2194
|
+
}
|
|
2195
|
+
catch (error) {
|
|
2196
|
+
return this.createErrorResponse(`Failed to get Elementor widget: ${error.response?.data?.message || error.message}`, 'GET_WIDGET_ERROR', 'API_ERROR', `HTTP ${error.response?.status}: ${error.response?.statusText}`);
|
|
2197
|
+
}
|
|
2198
|
+
}
|
|
2199
|
+
async getElementorElements(args) {
|
|
2200
|
+
const authCheck = this.ensureAuthenticated();
|
|
2201
|
+
if (authCheck)
|
|
2202
|
+
return authCheck;
|
|
2203
|
+
try {
|
|
2204
|
+
console.error(`🔍 Getting Elementor elements for ID: ${args.post_id}`);
|
|
2205
|
+
// Get current Elementor data using safe parsing utility
|
|
2206
|
+
const parsedResult = await this.safeGetElementorData(args.post_id);
|
|
2207
|
+
if (!parsedResult.success || !parsedResult.data) {
|
|
2208
|
+
return this.createErrorResponse(`No Elementor data found for post/page ID ${args.post_id}`, 'NO_ELEMENTOR_DATA', 'DATA_NOT_FOUND', `Post/page does not contain Elementor data or is not built with Elementor: ${parsedResult.error}`);
|
|
2209
|
+
}
|
|
2210
|
+
const elementorData = parsedResult.data;
|
|
2211
|
+
console.error(`✅ Successfully parsed ${elementorData.length} top-level elements`);
|
|
2212
|
+
// Function to recursively extract elements
|
|
2213
|
+
const extractElementsRecursive = (elements, level = 0) => {
|
|
2214
|
+
const result = [];
|
|
2215
|
+
for (const element of elements) {
|
|
2216
|
+
const elementInfo = {
|
|
2217
|
+
id: element.id,
|
|
2218
|
+
type: element.elType,
|
|
2219
|
+
level: level,
|
|
2220
|
+
};
|
|
2221
|
+
if (element.widgetType) {
|
|
2222
|
+
elementInfo.widgetType = element.widgetType;
|
|
2223
|
+
}
|
|
2224
|
+
if (args.include_content && element.settings) {
|
|
2225
|
+
// Include preview of content for common widget types
|
|
2226
|
+
if (element.widgetType === 'html' && element.settings.html) {
|
|
2227
|
+
elementInfo.contentPreview = element.settings.html.substring(0, 100) + (element.settings.html.length > 100 ? '...' : '');
|
|
2228
|
+
}
|
|
2229
|
+
else if (element.widgetType === 'text-editor' && element.settings.editor) {
|
|
2230
|
+
elementInfo.contentPreview = element.settings.editor.substring(0, 100) + (element.settings.editor.length > 100 ? '...' : '');
|
|
2231
|
+
}
|
|
2232
|
+
else if (element.widgetType === 'heading' && element.settings.title) {
|
|
2233
|
+
elementInfo.contentPreview = element.settings.title.substring(0, 100) + (element.settings.title.length > 100 ? '...' : '');
|
|
2234
|
+
}
|
|
2235
|
+
}
|
|
2236
|
+
result.push(elementInfo);
|
|
2237
|
+
// Recursively process nested elements
|
|
2238
|
+
if (element.elements && element.elements.length > 0) {
|
|
2239
|
+
result.push(...extractElementsRecursive(element.elements, level + 1));
|
|
2240
|
+
}
|
|
2241
|
+
}
|
|
2242
|
+
return result;
|
|
2243
|
+
};
|
|
2244
|
+
const elements = extractElementsRecursive(elementorData);
|
|
2245
|
+
return this.createSuccessResponse({
|
|
2246
|
+
post_id: args.post_id,
|
|
2247
|
+
total_elements: elements.length,
|
|
2248
|
+
elements: elements
|
|
2249
|
+
}, `Retrieved ${elements.length} Elementor elements from post/page ${args.post_id}`);
|
|
2250
|
+
}
|
|
2251
|
+
catch (error) {
|
|
2252
|
+
return this.createErrorResponse(`Failed to get Elementor elements: ${error.response?.data?.message || error.message}`, 'GET_ELEMENTS_ERROR', 'API_ERROR', `HTTP ${error.response?.status}: ${error.response?.statusText}`);
|
|
2253
|
+
}
|
|
2254
|
+
}
|
|
2255
|
+
async updateElementorSection(args) {
|
|
2256
|
+
const authCheck = this.ensureAuthenticated();
|
|
2257
|
+
if (authCheck)
|
|
2258
|
+
return authCheck;
|
|
2259
|
+
try {
|
|
2260
|
+
// Get current Elementor data using safe parsing utility
|
|
2261
|
+
const parsedResult = await this.safeGetElementorData(args.post_id);
|
|
2262
|
+
if (!parsedResult.success || !parsedResult.data) {
|
|
2263
|
+
return this.createErrorResponse(`Failed to get Elementor data for post/page ID ${args.post_id}: ${parsedResult.error || 'Unknown error'}`, 'ELEMENTOR_DATA_ERROR', 'DATA_ERROR', 'Could not retrieve or parse Elementor data for section update');
|
|
2264
|
+
}
|
|
2265
|
+
const elementorData = parsedResult.data;
|
|
2266
|
+
let sectionFound = false;
|
|
2267
|
+
let updatedWidgets = [];
|
|
2268
|
+
// Function to recursively find section and update widgets
|
|
2269
|
+
const updateSectionWidgets = (elements) => {
|
|
2270
|
+
for (let i = 0; i < elements.length; i++) {
|
|
2271
|
+
const element = elements[i];
|
|
2272
|
+
if (element.id === args.section_id) {
|
|
2273
|
+
sectionFound = true;
|
|
2274
|
+
// Found the section, now update widgets within it
|
|
2275
|
+
for (const widgetUpdate of args.widgets_updates) {
|
|
2276
|
+
const updated = updateWidgetInElements(element.elements || [], widgetUpdate);
|
|
2277
|
+
if (updated) {
|
|
2278
|
+
updatedWidgets.push(widgetUpdate.widget_id);
|
|
2279
|
+
}
|
|
2280
|
+
}
|
|
2281
|
+
return true;
|
|
2282
|
+
}
|
|
2283
|
+
// Recursively search in nested elements
|
|
2284
|
+
if (element.elements && element.elements.length > 0) {
|
|
2285
|
+
if (updateSectionWidgets(element.elements)) {
|
|
2286
|
+
return true;
|
|
2287
|
+
}
|
|
2288
|
+
}
|
|
2289
|
+
}
|
|
2290
|
+
return false;
|
|
2291
|
+
};
|
|
2292
|
+
// Helper function to update widget in elements array
|
|
2293
|
+
const updateWidgetInElements = (elements, widgetUpdate) => {
|
|
2294
|
+
for (const element of elements) {
|
|
2295
|
+
if (element.id === widgetUpdate.widget_id) {
|
|
2296
|
+
// Found the widget, update it
|
|
2297
|
+
if (widgetUpdate.widget_settings) {
|
|
2298
|
+
element.settings = { ...element.settings, ...widgetUpdate.widget_settings };
|
|
2299
|
+
}
|
|
2300
|
+
// Special handling for HTML widget content
|
|
2301
|
+
if (widgetUpdate.widget_content && element.widgetType === 'html') {
|
|
2302
|
+
element.settings.html = widgetUpdate.widget_content;
|
|
2303
|
+
}
|
|
2304
|
+
// Special handling for text widget content
|
|
2305
|
+
if (widgetUpdate.widget_content && element.widgetType === 'text-editor') {
|
|
2306
|
+
element.settings.editor = widgetUpdate.widget_content;
|
|
2307
|
+
}
|
|
2308
|
+
// Special handling for heading widget content
|
|
2309
|
+
if (widgetUpdate.widget_content && element.widgetType === 'heading') {
|
|
2310
|
+
element.settings.title = widgetUpdate.widget_content;
|
|
2311
|
+
}
|
|
2312
|
+
return true;
|
|
2313
|
+
}
|
|
2314
|
+
// Recursively search in nested elements
|
|
2315
|
+
if (element.elements && element.elements.length > 0) {
|
|
2316
|
+
if (updateWidgetInElements(element.elements, widgetUpdate)) {
|
|
2317
|
+
return true;
|
|
2318
|
+
}
|
|
2319
|
+
}
|
|
2320
|
+
}
|
|
2321
|
+
return false;
|
|
2322
|
+
};
|
|
2323
|
+
// Find section and update widgets
|
|
2324
|
+
updateSectionWidgets(elementorData);
|
|
2325
|
+
if (!sectionFound) {
|
|
2326
|
+
return this.createErrorResponse(`Section ID ${args.section_id} not found in Elementor data`, "OPERATION_ERROR", "API_ERROR", "Operation failed");
|
|
2327
|
+
}
|
|
2328
|
+
// Update the page with modified data
|
|
2329
|
+
const updateData = {
|
|
2330
|
+
meta: {
|
|
2331
|
+
_elementor_data: JSON.stringify(elementorData),
|
|
2332
|
+
_elementor_edit_mode: 'builder',
|
|
2333
|
+
},
|
|
2334
|
+
};
|
|
2335
|
+
// Try to update as post first, then as page if that fails
|
|
2336
|
+
let response;
|
|
2337
|
+
let postType = 'post';
|
|
2338
|
+
try {
|
|
2339
|
+
response = await this.axiosInstance.post(`posts/${args.post_id}`, updateData);
|
|
2340
|
+
}
|
|
2341
|
+
catch (postError) {
|
|
2342
|
+
if (postError.response?.status === 404) {
|
|
2343
|
+
// Try as page
|
|
2344
|
+
try {
|
|
2345
|
+
response = await this.axiosInstance.post(`pages/${args.post_id}`, updateData);
|
|
2346
|
+
postType = 'page';
|
|
2347
|
+
}
|
|
2348
|
+
catch (pageError) {
|
|
2349
|
+
return this.createErrorResponse(`Post/Page ID ${args.post_id} not found in posts or pages`, "OPERATION_ERROR", "API_ERROR", "Operation failed");
|
|
2350
|
+
}
|
|
2351
|
+
}
|
|
2352
|
+
else {
|
|
2353
|
+
throw postError;
|
|
2354
|
+
}
|
|
2355
|
+
}
|
|
2356
|
+
// Clear Elementor cache after updating data
|
|
2357
|
+
await this.clearElementorCache(args.post_id);
|
|
2358
|
+
return this.createSuccessResponse({
|
|
2359
|
+
operation_type: "update_section_widgets",
|
|
2360
|
+
section_id: args.section_id,
|
|
2361
|
+
post_id: args.post_id,
|
|
2362
|
+
post_type: postType,
|
|
2363
|
+
updated_widgets: updatedWidgets,
|
|
2364
|
+
widgets_not_found: args.widgets_updates.filter(w => !updatedWidgets.includes(w.widget_id)).map(w => w.widget_id),
|
|
2365
|
+
total_updates_requested: args.widgets_updates.length,
|
|
2366
|
+
successful_updates: updatedWidgets.length,
|
|
2367
|
+
cache_cleared: true,
|
|
2368
|
+
manual_cache_clearing_required: true
|
|
2369
|
+
}, `Elementor section ${args.section_id} updated successfully with ${updatedWidgets.length}/${args.widgets_updates.length} widget updates`);
|
|
2370
|
+
}
|
|
2371
|
+
catch (error) {
|
|
2372
|
+
if (error instanceof McpError) {
|
|
2373
|
+
throw error;
|
|
2374
|
+
}
|
|
2375
|
+
return this.createErrorResponse(`Failed to update Elementor section: ${error.response?.data?.message || error.message}`, "OPERATION_ERROR", "API_ERROR", "Operation failed");
|
|
2376
|
+
}
|
|
2377
|
+
}
|
|
2378
|
+
async getElementorDataDeepChunked(args) {
|
|
2379
|
+
const authCheck = this.ensureAuthenticated();
|
|
2380
|
+
if (authCheck)
|
|
2381
|
+
return authCheck;
|
|
2382
|
+
try {
|
|
2383
|
+
console.error(`🔍 Getting deep chunked Elementor data for ID: ${args.post_id}, element index: ${args.element_index || 0}`);
|
|
2384
|
+
// Get current Elementor data using safe parsing utility
|
|
2385
|
+
const parsedResult = await this.safeGetElementorData(args.post_id);
|
|
2386
|
+
if (!parsedResult.success || !parsedResult.data) {
|
|
2387
|
+
return this.createErrorResponse(`Failed to get Elementor data for post/page ID ${args.post_id}: ${parsedResult.error || 'Unknown error'}`, 'ELEMENTOR_DATA_ERROR', 'DATA_ERROR', 'Could not retrieve or parse Elementor data for deep chunking');
|
|
2388
|
+
}
|
|
2389
|
+
const elementorData = parsedResult.data;
|
|
2390
|
+
const maxDepth = args.max_depth || 2;
|
|
2391
|
+
const elementIndex = args.element_index || 0;
|
|
2392
|
+
const includeWidgetPreviews = args.include_widget_previews || false;
|
|
2393
|
+
if (elementIndex >= elementorData.length) {
|
|
2394
|
+
return this.createErrorResponse(`Element index ${elementIndex} is out of range. Total top-level elements: ${elementorData.length}`, 'INDEX_OUT_OF_RANGE', 'VALIDATION_ERROR', 'Requested element index exceeds available elements');
|
|
2395
|
+
}
|
|
2396
|
+
// Function to limit depth and content of elements
|
|
2397
|
+
const limitElementDepth = (element, currentDepth) => {
|
|
2398
|
+
const limited = {
|
|
2399
|
+
id: element.id,
|
|
2400
|
+
elType: element.elType,
|
|
2401
|
+
widgetType: element.widgetType || null,
|
|
2402
|
+
isInner: element.isInner || false,
|
|
2403
|
+
depth: currentDepth
|
|
2404
|
+
};
|
|
2405
|
+
// Add limited settings (exclude heavy content)
|
|
2406
|
+
if (element.settings) {
|
|
2407
|
+
limited.settings = {};
|
|
2408
|
+
// Include only lightweight settings, exclude heavy content
|
|
2409
|
+
const lightweightKeys = ['_column_size', '_inline_size', 'background_background', 'content_width', 'flex_direction', 'gap'];
|
|
2410
|
+
lightweightKeys.forEach(key => {
|
|
2411
|
+
if (element.settings[key] !== undefined) {
|
|
2412
|
+
limited.settings[key] = element.settings[key];
|
|
2413
|
+
}
|
|
2414
|
+
});
|
|
2415
|
+
// Add widget content preview if requested and if it's a widget
|
|
2416
|
+
if (includeWidgetPreviews && element.widgetType && element.settings) {
|
|
2417
|
+
if (element.settings.title) {
|
|
2418
|
+
limited.content_preview = String(element.settings.title).substring(0, 100);
|
|
2419
|
+
}
|
|
2420
|
+
else if (element.settings.editor) {
|
|
2421
|
+
limited.content_preview = String(element.settings.editor).replace(/<[^>]*>/g, '').substring(0, 100);
|
|
2422
|
+
}
|
|
2423
|
+
else if (element.settings.html) {
|
|
2424
|
+
limited.content_preview = String(element.settings.html).replace(/<[^>]*>/g, '').substring(0, 100);
|
|
2425
|
+
}
|
|
2426
|
+
}
|
|
2427
|
+
}
|
|
2428
|
+
// Recursively process children up to max depth
|
|
2429
|
+
if (element.elements && element.elements.length > 0 && currentDepth < maxDepth) {
|
|
2430
|
+
limited.elements = element.elements.map((child) => limitElementDepth(child, currentDepth + 1));
|
|
2431
|
+
limited.child_count = element.elements.length;
|
|
2432
|
+
}
|
|
2433
|
+
else if (element.elements && element.elements.length > 0) {
|
|
2434
|
+
// If we've reached max depth, just show summary
|
|
2435
|
+
limited.child_count = element.elements.length;
|
|
2436
|
+
limited.child_types = [...new Set(element.elements.map((child) => child.elType || child.widgetType))];
|
|
2437
|
+
}
|
|
2438
|
+
return limited;
|
|
2439
|
+
};
|
|
2440
|
+
const targetElement = elementorData[elementIndex];
|
|
2441
|
+
const limitedElement = limitElementDepth(targetElement, 0);
|
|
2442
|
+
// Calculate approximate token size (rough estimate: 4 chars per token)
|
|
2443
|
+
const estimatedTokens = Math.ceil(JSON.stringify(limitedElement).length / 4);
|
|
2444
|
+
return this.createSuccessResponse({
|
|
2445
|
+
post_id: args.post_id,
|
|
2446
|
+
element_index: elementIndex,
|
|
2447
|
+
max_depth: maxDepth,
|
|
2448
|
+
include_widget_previews: includeWidgetPreviews,
|
|
2449
|
+
total_top_level_elements: elementorData.length,
|
|
2450
|
+
element_data: limitedElement,
|
|
2451
|
+
navigation: {
|
|
2452
|
+
has_previous: elementIndex > 0,
|
|
2453
|
+
has_next: elementIndex < elementorData.length - 1,
|
|
2454
|
+
previous_index: elementIndex > 0 ? elementIndex - 1 : null,
|
|
2455
|
+
next_index: elementIndex < elementorData.length - 1 ? elementIndex + 1 : null
|
|
2456
|
+
},
|
|
2457
|
+
estimated_tokens: estimatedTokens
|
|
2458
|
+
}, `Retrieved deep chunked element ${elementIndex + 1} of ${elementorData.length} with max depth ${maxDepth} (estimated ${estimatedTokens} tokens)`);
|
|
2459
|
+
}
|
|
2460
|
+
catch (error) {
|
|
2461
|
+
return this.createErrorResponse(`Failed to get deep chunked Elementor data: ${error.message}`, 'GET_DEEP_CHUNKED_DATA_ERROR', 'API_ERROR', 'Operation failed');
|
|
2462
|
+
}
|
|
2463
|
+
}
|
|
2464
|
+
async getElementorStructureSummary(args) {
|
|
2465
|
+
const authCheck = this.ensureAuthenticated();
|
|
2466
|
+
if (authCheck)
|
|
2467
|
+
return authCheck;
|
|
2468
|
+
try {
|
|
2469
|
+
console.error(`📊 Getting structure summary for ID: ${args.post_id}`);
|
|
2470
|
+
// Get current Elementor data using safe parsing utility
|
|
2471
|
+
const parsedResult = await this.safeGetElementorData(args.post_id);
|
|
2472
|
+
if (!parsedResult.success || !parsedResult.data) {
|
|
2473
|
+
return this.createErrorResponse(`Failed to get Elementor data for post/page ID ${args.post_id}: ${parsedResult.error || 'Unknown error'}`, 'ELEMENTOR_DATA_ERROR', 'DATA_ERROR', 'Could not retrieve or parse Elementor data for structure summary');
|
|
2474
|
+
}
|
|
2475
|
+
const elementorData = parsedResult.data;
|
|
2476
|
+
const maxDepth = args.max_depth || 4;
|
|
2477
|
+
// Function to create a minimal structure summary
|
|
2478
|
+
const createStructureSummary = (elements, currentDepth = 0) => {
|
|
2479
|
+
return elements.map((element, index) => {
|
|
2480
|
+
const summary = {
|
|
2481
|
+
index: index,
|
|
2482
|
+
id: element.id,
|
|
2483
|
+
type: element.elType,
|
|
2484
|
+
depth: currentDepth
|
|
2485
|
+
};
|
|
2486
|
+
if (element.widgetType) {
|
|
2487
|
+
summary.widget_type = element.widgetType;
|
|
2488
|
+
}
|
|
2489
|
+
// Add some key properties for structure understanding
|
|
2490
|
+
if (element.settings) {
|
|
2491
|
+
if (element.settings._column_size) {
|
|
2492
|
+
summary.column_size = element.settings._column_size;
|
|
2493
|
+
}
|
|
2494
|
+
if (element.settings.content_width) {
|
|
2495
|
+
summary.content_width = element.settings.content_width;
|
|
2496
|
+
}
|
|
2497
|
+
if (element.settings.flex_direction) {
|
|
2498
|
+
summary.flex_direction = element.settings.flex_direction;
|
|
2499
|
+
}
|
|
2500
|
+
}
|
|
2501
|
+
// Count children and their types
|
|
2502
|
+
if (element.elements && element.elements.length > 0) {
|
|
2503
|
+
summary.child_count = element.elements.length;
|
|
2504
|
+
summary.child_types = [...new Set(element.elements.map((child) => child.elType || child.widgetType))];
|
|
2505
|
+
// Recursively process children up to max depth
|
|
2506
|
+
if (currentDepth < maxDepth) {
|
|
2507
|
+
summary.children = createStructureSummary(element.elements, currentDepth + 1);
|
|
2508
|
+
}
|
|
2509
|
+
}
|
|
2510
|
+
return summary;
|
|
2511
|
+
});
|
|
2512
|
+
};
|
|
2513
|
+
const structureSummary = createStructureSummary(elementorData);
|
|
2514
|
+
// Calculate total elements at each level
|
|
2515
|
+
const levelCounts = {};
|
|
2516
|
+
const widgetCounts = {};
|
|
2517
|
+
const countElements = (elements, depth = 0) => {
|
|
2518
|
+
levelCounts[depth] = (levelCounts[depth] || 0) + elements.length;
|
|
2519
|
+
elements.forEach(element => {
|
|
2520
|
+
if (element.widget_type) {
|
|
2521
|
+
widgetCounts[element.widget_type] = (widgetCounts[element.widget_type] || 0) + 1;
|
|
2522
|
+
}
|
|
2523
|
+
if (element.children) {
|
|
2524
|
+
countElements(element.children, depth + 1);
|
|
2525
|
+
}
|
|
2526
|
+
});
|
|
2527
|
+
};
|
|
2528
|
+
countElements(structureSummary);
|
|
2529
|
+
// Create human-readable summary
|
|
2530
|
+
let textSummary = `📄 Page Structure Summary (Post ID: ${args.post_id})\n\n`;
|
|
2531
|
+
textSummary += `🏗️ Top-level elements: ${elementorData.length}\n`;
|
|
2532
|
+
Object.entries(levelCounts).forEach(([depth, count]) => {
|
|
2533
|
+
textSummary += ` Level ${depth}: ${count} elements\n`;
|
|
2534
|
+
});
|
|
2535
|
+
if (Object.keys(widgetCounts).length > 0) {
|
|
2536
|
+
textSummary += `\n🧩 Widget types found:\n`;
|
|
2537
|
+
Object.entries(widgetCounts)
|
|
2538
|
+
.sort(([, a], [, b]) => b - a)
|
|
2539
|
+
.forEach(([widget, count]) => {
|
|
2540
|
+
textSummary += ` ${widget}: ${count}\n`;
|
|
2541
|
+
});
|
|
2542
|
+
}
|
|
2543
|
+
return this.createSuccessResponse({
|
|
2544
|
+
post_id: args.post_id,
|
|
2545
|
+
max_depth: maxDepth,
|
|
2546
|
+
structure: structureSummary,
|
|
2547
|
+
statistics: {
|
|
2548
|
+
total_top_level_elements: elementorData.length,
|
|
2549
|
+
level_counts: levelCounts,
|
|
2550
|
+
widget_counts: widgetCounts,
|
|
2551
|
+
total_widgets: Object.values(widgetCounts).reduce((sum, count) => sum + count, 0)
|
|
2552
|
+
},
|
|
2553
|
+
text_summary: textSummary
|
|
2554
|
+
}, `Structure summary generated for ${elementorData.length} top-level elements with ${Object.values(levelCounts).reduce((sum, count) => sum + count, 0)} total elements`);
|
|
2555
|
+
}
|
|
2556
|
+
catch (error) {
|
|
2557
|
+
return this.createErrorResponse(`Failed to get structure summary: ${error.message}`, 'GET_STRUCTURE_SUMMARY_ERROR', 'API_ERROR', 'Operation failed');
|
|
2558
|
+
}
|
|
2559
|
+
}
|
|
2560
|
+
async backupElementorData(args) {
|
|
2561
|
+
this.ensureAuthenticated();
|
|
2562
|
+
try {
|
|
2563
|
+
// Get current Elementor data using safe parsing utility
|
|
2564
|
+
const parsedResult = await this.safeGetElementorData(args.post_id);
|
|
2565
|
+
if (!parsedResult.success || !parsedResult.data) {
|
|
2566
|
+
return this.createErrorResponse(`Failed to get Elementor data for post/page ID ${args.post_id}: ${parsedResult.error || 'Unknown error'}`, "OPERATION_ERROR", "API_ERROR", "Operation failed");
|
|
2567
|
+
}
|
|
2568
|
+
// Create backup metadata
|
|
2569
|
+
const timestamp = new Date().toISOString();
|
|
2570
|
+
const backupName = args.backup_name || `backup_${timestamp}`;
|
|
2571
|
+
// Try to get the post/page to determine type
|
|
2572
|
+
let postInfo;
|
|
2573
|
+
let postType = 'post';
|
|
2574
|
+
try {
|
|
2575
|
+
postInfo = await this.axiosInstance.get(`posts/${args.post_id}`, {
|
|
2576
|
+
params: { context: 'edit' }
|
|
2577
|
+
});
|
|
2578
|
+
}
|
|
2579
|
+
catch (postError) {
|
|
2580
|
+
if (postError.response?.status === 404) {
|
|
2581
|
+
try {
|
|
2582
|
+
postInfo = await this.axiosInstance.get(`pages/${args.post_id}`, {
|
|
2583
|
+
params: { context: 'edit' }
|
|
2584
|
+
});
|
|
2585
|
+
postType = 'page';
|
|
2586
|
+
}
|
|
2587
|
+
catch (pageError) {
|
|
2588
|
+
return this.createErrorResponse(`Post/Page ID ${args.post_id} not found in posts or pages`, "OPERATION_ERROR", "API_ERROR", "Operation failed");
|
|
2589
|
+
}
|
|
2590
|
+
}
|
|
2591
|
+
else {
|
|
2592
|
+
throw postError;
|
|
2593
|
+
}
|
|
2594
|
+
}
|
|
2595
|
+
// Store backup in post meta with unique key
|
|
2596
|
+
const backupKey = `_elementor_data_backup_${Date.now()}`;
|
|
2597
|
+
const backupMeta = {
|
|
2598
|
+
meta: {
|
|
2599
|
+
[backupKey]: JSON.stringify({
|
|
2600
|
+
backup_name: backupName,
|
|
2601
|
+
timestamp: timestamp,
|
|
2602
|
+
post_id: args.post_id,
|
|
2603
|
+
post_type: postType,
|
|
2604
|
+
post_title: postInfo.data.title.rendered || postInfo.data.title.raw,
|
|
2605
|
+
elementor_data: JSON.stringify(parsedResult.data)
|
|
2606
|
+
})
|
|
2607
|
+
}
|
|
2608
|
+
};
|
|
2609
|
+
// Save backup
|
|
2610
|
+
let response;
|
|
2611
|
+
if (postType === 'page') {
|
|
2612
|
+
response = await this.axiosInstance.post(`pages/${args.post_id}`, backupMeta);
|
|
2613
|
+
}
|
|
2614
|
+
else {
|
|
2615
|
+
response = await this.axiosInstance.post(`posts/${args.post_id}`, backupMeta);
|
|
2616
|
+
}
|
|
2617
|
+
return this.createSuccessResponse({
|
|
2618
|
+
operation_type: "backup_elementor_data",
|
|
2619
|
+
post_id: args.post_id,
|
|
2620
|
+
post_type: postType,
|
|
2621
|
+
post_title: postInfo.data.title.rendered || postInfo.data.title.raw,
|
|
2622
|
+
backup_name: backupName,
|
|
2623
|
+
backup_key: backupKey,
|
|
2624
|
+
timestamp: timestamp,
|
|
2625
|
+
backup_storage: "wordpress_meta",
|
|
2626
|
+
data_size: JSON.stringify(parsedResult.data).length
|
|
2627
|
+
}, `Elementor data backup created successfully! Backup key: ${backupKey}`);
|
|
2628
|
+
}
|
|
2629
|
+
catch (error) {
|
|
2630
|
+
if (error instanceof McpError) {
|
|
2631
|
+
throw error;
|
|
2632
|
+
}
|
|
2633
|
+
return this.createErrorResponse(`Failed to backup Elementor data: ${error.response?.data?.message || error.message}`, "OPERATION_ERROR", "API_ERROR", "Operation failed");
|
|
2634
|
+
}
|
|
2635
|
+
}
|
|
2636
|
+
async getMedia(args) {
|
|
2637
|
+
const authCheck = this.ensureAuthenticated();
|
|
2638
|
+
if (authCheck)
|
|
2639
|
+
return authCheck;
|
|
2640
|
+
const params = {
|
|
2641
|
+
per_page: args.per_page || 10,
|
|
2642
|
+
};
|
|
2643
|
+
if (args.media_type) {
|
|
2644
|
+
params.media_type = args.media_type;
|
|
2645
|
+
}
|
|
2646
|
+
try {
|
|
2647
|
+
console.error(`Fetching media with params: ${JSON.stringify(params)}`);
|
|
2648
|
+
const response = await this.axiosInstance.get('media', { params });
|
|
2649
|
+
// Create optimized summary objects instead of returning full content
|
|
2650
|
+
const media = response.data;
|
|
2651
|
+
const mediaSummaries = media.map((item) => ({
|
|
2652
|
+
id: item.id,
|
|
2653
|
+
title: item.title?.rendered || '(No title)',
|
|
2654
|
+
filename: item.media_details?.file || 'Unknown',
|
|
2655
|
+
mime_type: item.mime_type,
|
|
2656
|
+
file_size: item.media_details?.filesize || 0,
|
|
2657
|
+
width: item.media_details?.width || null,
|
|
2658
|
+
height: item.media_details?.height || null,
|
|
2659
|
+
url: item.source_url,
|
|
2660
|
+
date_uploaded: item.date,
|
|
2661
|
+
alt_text: item.alt_text || '',
|
|
2662
|
+
author: item.author,
|
|
2663
|
+
status: item.status
|
|
2664
|
+
}));
|
|
2665
|
+
return this.createSuccessResponse({
|
|
2666
|
+
media: mediaSummaries, // Return summaries instead of full objects
|
|
2667
|
+
count: mediaSummaries.length,
|
|
2668
|
+
filter: args.media_type || 'all',
|
|
2669
|
+
performance_note: "Optimized response - use get_media_item(id) for full details"
|
|
2670
|
+
}, `Retrieved ${mediaSummaries.length} media item summaries`);
|
|
2671
|
+
}
|
|
2672
|
+
catch (error) {
|
|
2673
|
+
console.error(`Error fetching media: ${error.response?.status} - ${error.response?.statusText}`);
|
|
2674
|
+
console.error(`URL: ${error.config?.url}`);
|
|
2675
|
+
console.error(`Headers: ${JSON.stringify(error.config?.headers)}`);
|
|
2676
|
+
return this.createErrorResponse(`Failed to fetch media: ${error.response?.status} ${error.response?.statusText} - ${error.response?.data?.message || error.message}`, 'GET_MEDIA_ERROR', 'API_ERROR', `HTTP ${error.response?.status}: ${error.response?.statusText}`);
|
|
2677
|
+
}
|
|
2678
|
+
}
|
|
2679
|
+
async uploadMedia(args) {
|
|
2680
|
+
this.ensureAuthenticated();
|
|
2681
|
+
try {
|
|
2682
|
+
const fs = await import('fs');
|
|
2683
|
+
const path = await import('path');
|
|
2684
|
+
if (!fs.existsSync(args.file_path)) {
|
|
2685
|
+
return this.createErrorResponse(`Failed to upload media: File not found: ${args.file_path}`, 'FILE_NOT_FOUND', 'VALIDATION_ERROR', 'The specified file path does not exist or is not accessible');
|
|
2686
|
+
}
|
|
2687
|
+
const formData = new FormData();
|
|
2688
|
+
const fileStream = fs.createReadStream(args.file_path);
|
|
2689
|
+
const fileName = path.basename(args.file_path);
|
|
2690
|
+
formData.append('file', fileStream, fileName);
|
|
2691
|
+
if (args.title) {
|
|
2692
|
+
formData.append('title', args.title);
|
|
2693
|
+
}
|
|
2694
|
+
if (args.alt_text) {
|
|
2695
|
+
formData.append('alt_text', args.alt_text);
|
|
2696
|
+
}
|
|
2697
|
+
const response = await this.axiosInstance.post('media', formData, {
|
|
2698
|
+
headers: {
|
|
2699
|
+
...formData.getHeaders(),
|
|
2700
|
+
},
|
|
2701
|
+
});
|
|
2702
|
+
return this.createSuccessResponse({
|
|
2703
|
+
operation_type: "upload_media",
|
|
2704
|
+
media_id: response.data.id,
|
|
2705
|
+
url: response.data.source_url,
|
|
2706
|
+
title: response.data.title.rendered,
|
|
2707
|
+
file_path: args.file_path,
|
|
2708
|
+
file_name: fileName,
|
|
2709
|
+
mime_type: response.data.mime_type,
|
|
2710
|
+
file_size: response.data.media_details?.filesize || null,
|
|
2711
|
+
alt_text: args.alt_text || null,
|
|
2712
|
+
upload_date: response.data.date
|
|
2713
|
+
}, `Media uploaded successfully! Media ID: ${response.data.id} - ${response.data.title.rendered}`);
|
|
2714
|
+
}
|
|
2715
|
+
catch (error) {
|
|
2716
|
+
return this.createErrorResponse(`Failed to upload media: ${error.response?.data?.message || error.message}`, 'UPLOAD_FAILED', 'API_ERROR', `HTTP ${error.response?.status}: ${error.response?.statusText} - ${error.response?.data || error.message}`);
|
|
2717
|
+
}
|
|
2718
|
+
}
|
|
2719
|
+
// Helper methods for status-based responses
|
|
2720
|
+
createSuccessResponse(data, message) {
|
|
2721
|
+
return {
|
|
2722
|
+
content: [
|
|
2723
|
+
{
|
|
2724
|
+
type: 'text',
|
|
2725
|
+
text: JSON.stringify({
|
|
2726
|
+
status: "success",
|
|
2727
|
+
data: data,
|
|
2728
|
+
message: message || "Operation completed successfully"
|
|
2729
|
+
}, null, 2),
|
|
2730
|
+
},
|
|
2731
|
+
],
|
|
2732
|
+
};
|
|
2733
|
+
}
|
|
2734
|
+
createErrorResponse(message, code, errorType, details) {
|
|
2735
|
+
return {
|
|
2736
|
+
content: [
|
|
2737
|
+
{
|
|
2738
|
+
type: 'text',
|
|
2739
|
+
text: JSON.stringify({
|
|
2740
|
+
status: "error",
|
|
2741
|
+
data: {
|
|
2742
|
+
message: message,
|
|
2743
|
+
code: code || "GENERAL_ERROR",
|
|
2744
|
+
error_type: errorType || "OPERATION_ERROR",
|
|
2745
|
+
details: details || message
|
|
2746
|
+
}
|
|
2747
|
+
}, null, 2),
|
|
2748
|
+
},
|
|
2749
|
+
],
|
|
2750
|
+
};
|
|
2751
|
+
}
|
|
2752
|
+
// Section and Container Creation Tools
|
|
2753
|
+
async createElementorSection(args) {
|
|
2754
|
+
this.ensureAuthenticated();
|
|
2755
|
+
try {
|
|
2756
|
+
// Get current Elementor data using safe parsing utility
|
|
2757
|
+
const parsedResult = await this.safeGetElementorData(args.post_id);
|
|
2758
|
+
let elementorData = [];
|
|
2759
|
+
if (parsedResult.success && parsedResult.data) {
|
|
2760
|
+
elementorData = parsedResult.data;
|
|
2761
|
+
}
|
|
2762
|
+
// Generate unique ID for the section
|
|
2763
|
+
const sectionId = Math.random().toString(36).substr(2, 8);
|
|
2764
|
+
const columns = args.columns || 1;
|
|
2765
|
+
// Create column elements
|
|
2766
|
+
const columnElements = [];
|
|
2767
|
+
for (let i = 0; i < columns; i++) {
|
|
2768
|
+
const columnId = Math.random().toString(36).substr(2, 8);
|
|
2769
|
+
columnElements.push({
|
|
2770
|
+
id: columnId,
|
|
2771
|
+
elType: 'column',
|
|
2772
|
+
isInner: false,
|
|
2773
|
+
settings: {
|
|
2774
|
+
_column_size: Math.floor(100 / columns),
|
|
2775
|
+
_inline_size: null
|
|
2776
|
+
},
|
|
2777
|
+
elements: [],
|
|
2778
|
+
widgetType: null
|
|
2779
|
+
});
|
|
2780
|
+
}
|
|
2781
|
+
// Create new section
|
|
2782
|
+
const newSection = {
|
|
2783
|
+
id: sectionId,
|
|
2784
|
+
elType: 'section',
|
|
2785
|
+
isInner: false,
|
|
2786
|
+
settings: args.section_settings || {},
|
|
2787
|
+
elements: columnElements,
|
|
2788
|
+
widgetType: null
|
|
2789
|
+
};
|
|
2790
|
+
// Insert section at specified position or at the end
|
|
2791
|
+
if (args.position !== undefined && args.position >= 0 && args.position < elementorData.length) {
|
|
2792
|
+
elementorData.splice(args.position, 0, newSection);
|
|
2793
|
+
}
|
|
2794
|
+
else {
|
|
2795
|
+
elementorData.push(newSection);
|
|
2796
|
+
}
|
|
2797
|
+
// Update the page
|
|
2798
|
+
await this.updateElementorData({
|
|
2799
|
+
post_id: args.post_id,
|
|
2800
|
+
elementor_data: JSON.stringify(elementorData)
|
|
2801
|
+
});
|
|
2802
|
+
return this.createSuccessResponse({
|
|
2803
|
+
operation_type: "create_section",
|
|
2804
|
+
section_id: sectionId,
|
|
2805
|
+
columns: columns,
|
|
2806
|
+
position: args.position || elementorData.length - 1,
|
|
2807
|
+
post_id: args.post_id,
|
|
2808
|
+
section_settings: args.section_settings || {},
|
|
2809
|
+
column_ids: columnElements.map(col => col.id)
|
|
2810
|
+
}, `Section created successfully! Section ID: ${sectionId} with ${columns} columns at position ${args.position || 'end'}`);
|
|
2811
|
+
}
|
|
2812
|
+
catch (error) {
|
|
2813
|
+
if (error instanceof McpError) {
|
|
2814
|
+
throw error;
|
|
2815
|
+
}
|
|
2816
|
+
return this.createErrorResponse(`Failed to create section: ${error.message}`, "OPERATION_ERROR", "API_ERROR", "Operation failed");
|
|
2817
|
+
}
|
|
2818
|
+
}
|
|
2819
|
+
async createElementorContainer(args) {
|
|
2820
|
+
this.ensureAuthenticated();
|
|
2821
|
+
try {
|
|
2822
|
+
// Get current Elementor data using safe parsing utility
|
|
2823
|
+
const parsedResult = await this.safeGetElementorData(args.post_id);
|
|
2824
|
+
let elementorData = [];
|
|
2825
|
+
if (parsedResult.success && parsedResult.data) {
|
|
2826
|
+
elementorData = parsedResult.data;
|
|
2827
|
+
}
|
|
2828
|
+
// Generate unique ID for the container
|
|
2829
|
+
const containerId = Math.random().toString(36).substr(2, 8);
|
|
2830
|
+
// Create new container (Elementor v3.6+ flexbox container)
|
|
2831
|
+
const newContainer = {
|
|
2832
|
+
id: containerId,
|
|
2833
|
+
elType: 'container',
|
|
2834
|
+
isInner: false,
|
|
2835
|
+
settings: {
|
|
2836
|
+
content_width: 'boxed',
|
|
2837
|
+
flex_direction: 'column',
|
|
2838
|
+
...args.container_settings
|
|
2839
|
+
},
|
|
2840
|
+
elements: [],
|
|
2841
|
+
widgetType: null
|
|
2842
|
+
};
|
|
2843
|
+
// Insert container at specified position or at the end
|
|
2844
|
+
if (args.position !== undefined && args.position >= 0 && args.position < elementorData.length) {
|
|
2845
|
+
elementorData.splice(args.position, 0, newContainer);
|
|
2846
|
+
}
|
|
2847
|
+
else {
|
|
2848
|
+
elementorData.push(newContainer);
|
|
2849
|
+
}
|
|
2850
|
+
// Update the page
|
|
2851
|
+
await this.updateElementorData({
|
|
2852
|
+
post_id: args.post_id,
|
|
2853
|
+
elementor_data: JSON.stringify(elementorData)
|
|
2854
|
+
});
|
|
2855
|
+
return this.createSuccessResponse({
|
|
2856
|
+
operation_type: "create_container",
|
|
2857
|
+
container_id: containerId,
|
|
2858
|
+
position: args.position || elementorData.length - 1,
|
|
2859
|
+
post_id: args.post_id,
|
|
2860
|
+
container_settings: {
|
|
2861
|
+
content_width: 'boxed',
|
|
2862
|
+
flex_direction: 'column',
|
|
2863
|
+
...args.container_settings
|
|
2864
|
+
}
|
|
2865
|
+
}, `Container created successfully! Container ID: ${containerId} at position ${args.position || 'end'}`);
|
|
2866
|
+
}
|
|
2867
|
+
catch (error) {
|
|
2868
|
+
if (error instanceof McpError) {
|
|
2869
|
+
throw error;
|
|
2870
|
+
}
|
|
2871
|
+
return this.createErrorResponse(`Failed to create container: ${error.message}`, "OPERATION_ERROR", "API_ERROR", "Operation failed");
|
|
2872
|
+
}
|
|
2873
|
+
}
|
|
2874
|
+
async addColumnToSection(args) {
|
|
2875
|
+
this.ensureAuthenticated();
|
|
2876
|
+
try {
|
|
2877
|
+
// Get current Elementor data using safe parsing utility
|
|
2878
|
+
const parsedResult = await this.safeGetElementorData(args.post_id);
|
|
2879
|
+
if (!parsedResult.success || !parsedResult.data) {
|
|
2880
|
+
return this.createErrorResponse(`Failed to get Elementor data for post/page ID ${args.post_id}: ${parsedResult.error || 'Unknown error'}`, "OPERATION_ERROR", "API_ERROR", "Operation failed");
|
|
2881
|
+
}
|
|
2882
|
+
const elementorData = parsedResult.data;
|
|
2883
|
+
const columnsToAdd = args.columns_to_add || 1;
|
|
2884
|
+
let sectionFound = false;
|
|
2885
|
+
// Function to find and update section
|
|
2886
|
+
const findAndUpdateSection = (elements) => {
|
|
2887
|
+
for (let element of elements) {
|
|
2888
|
+
if (element.id === args.section_id && element.elType === 'section') {
|
|
2889
|
+
// Add new columns
|
|
2890
|
+
for (let i = 0; i < columnsToAdd; i++) {
|
|
2891
|
+
const columnId = Math.random().toString(36).substr(2, 8);
|
|
2892
|
+
element.elements.push({
|
|
2893
|
+
id: columnId,
|
|
2894
|
+
elType: 'column',
|
|
2895
|
+
isInner: false,
|
|
2896
|
+
settings: {},
|
|
2897
|
+
elements: [],
|
|
2898
|
+
widgetType: null
|
|
2899
|
+
});
|
|
2900
|
+
}
|
|
2901
|
+
return true;
|
|
2902
|
+
}
|
|
2903
|
+
if (element.elements && element.elements.length > 0) {
|
|
2904
|
+
if (findAndUpdateSection(element.elements)) {
|
|
2905
|
+
return true;
|
|
2906
|
+
}
|
|
2907
|
+
}
|
|
2908
|
+
}
|
|
2909
|
+
return false;
|
|
2910
|
+
};
|
|
2911
|
+
sectionFound = findAndUpdateSection(elementorData);
|
|
2912
|
+
if (!sectionFound) {
|
|
2913
|
+
return this.createErrorResponse(`Section ID ${args.section_id} not found`, "OPERATION_ERROR", "API_ERROR", "Operation failed");
|
|
2914
|
+
}
|
|
2915
|
+
// Update the page
|
|
2916
|
+
await this.updateElementorData({
|
|
2917
|
+
post_id: args.post_id,
|
|
2918
|
+
elementor_data: JSON.stringify(elementorData)
|
|
2919
|
+
});
|
|
2920
|
+
return this.createSuccessResponse({
|
|
2921
|
+
operation_type: "add_columns_to_section",
|
|
2922
|
+
section_id: args.section_id,
|
|
2923
|
+
columns_added: columnsToAdd,
|
|
2924
|
+
post_id: args.post_id,
|
|
2925
|
+
new_column_ids: Array.from({ length: columnsToAdd }, () => Math.random().toString(36).substr(2, 8))
|
|
2926
|
+
}, `Successfully added ${columnsToAdd} column(s) to section ${args.section_id}`);
|
|
2927
|
+
}
|
|
2928
|
+
catch (error) {
|
|
2929
|
+
if (error instanceof McpError) {
|
|
2930
|
+
throw error;
|
|
2931
|
+
}
|
|
2932
|
+
return this.createErrorResponse(`Failed to add columns to section: ${error.message}`, "OPERATION_ERROR", "API_ERROR", "Operation failed");
|
|
2933
|
+
}
|
|
2934
|
+
}
|
|
2935
|
+
async duplicateSection(args) {
|
|
2936
|
+
this.ensureAuthenticated();
|
|
2937
|
+
try {
|
|
2938
|
+
// Get current Elementor data using safe parsing utility
|
|
2939
|
+
const parsedResult = await this.safeGetElementorData(args.post_id);
|
|
2940
|
+
if (!parsedResult.success || !parsedResult.data) {
|
|
2941
|
+
return this.createErrorResponse(`Failed to get Elementor data for post/page ID ${args.post_id}: ${parsedResult.error || 'Unknown error'}`, "OPERATION_ERROR", "API_ERROR", "Operation failed");
|
|
2942
|
+
}
|
|
2943
|
+
const elementorData = parsedResult.data;
|
|
2944
|
+
let sectionToDuplicate = null;
|
|
2945
|
+
let insertIndex = elementorData.length;
|
|
2946
|
+
// Find the section to duplicate
|
|
2947
|
+
for (let i = 0; i < elementorData.length; i++) {
|
|
2948
|
+
if (elementorData[i].id === args.section_id && elementorData[i].elType === 'section') {
|
|
2949
|
+
sectionToDuplicate = JSON.parse(JSON.stringify(elementorData[i])); // Deep copy
|
|
2950
|
+
insertIndex = args.position !== undefined ? args.position : i + 1;
|
|
2951
|
+
break;
|
|
2952
|
+
}
|
|
2953
|
+
}
|
|
2954
|
+
if (!sectionToDuplicate) {
|
|
2955
|
+
return this.createErrorResponse(`Section ID ${args.section_id} not found`, "OPERATION_ERROR", "API_ERROR", "Operation failed");
|
|
2956
|
+
}
|
|
2957
|
+
// Generate new IDs for the duplicated section and all its children
|
|
2958
|
+
const generateNewIds = (element) => {
|
|
2959
|
+
element.id = Math.random().toString(36).substr(2, 8);
|
|
2960
|
+
if (element.elements) {
|
|
2961
|
+
element.elements.forEach(generateNewIds);
|
|
2962
|
+
}
|
|
2963
|
+
};
|
|
2964
|
+
generateNewIds(sectionToDuplicate);
|
|
2965
|
+
// Insert the duplicated section
|
|
2966
|
+
elementorData.splice(insertIndex, 0, sectionToDuplicate);
|
|
2967
|
+
// Update the page
|
|
2968
|
+
await this.updateElementorData({
|
|
2969
|
+
post_id: args.post_id,
|
|
2970
|
+
elementor_data: JSON.stringify(elementorData)
|
|
2971
|
+
});
|
|
2972
|
+
return this.createSuccessResponse({
|
|
2973
|
+
operation_type: "duplicate_section",
|
|
2974
|
+
original_section_id: args.section_id,
|
|
2975
|
+
new_section_id: sectionToDuplicate.id,
|
|
2976
|
+
position: insertIndex,
|
|
2977
|
+
post_id: args.post_id,
|
|
2978
|
+
duplicated_elements_count: 1 + (sectionToDuplicate.elements ? sectionToDuplicate.elements.length : 0)
|
|
2979
|
+
}, `Section duplicated successfully! New section ID: ${sectionToDuplicate.id} inserted at position: ${insertIndex}`);
|
|
2980
|
+
}
|
|
2981
|
+
catch (error) {
|
|
2982
|
+
if (error instanceof McpError) {
|
|
2983
|
+
throw error;
|
|
2984
|
+
}
|
|
2985
|
+
return this.createErrorResponse(`Failed to duplicate section: ${error.message}`, "OPERATION_ERROR", "API_ERROR", "Operation failed");
|
|
2986
|
+
}
|
|
2987
|
+
}
|
|
2988
|
+
// Widget Addition Tools
|
|
2989
|
+
async addWidgetToSection(args) {
|
|
2990
|
+
this.ensureAuthenticated();
|
|
2991
|
+
try {
|
|
2992
|
+
console.error(`➕ Adding widget ${args.widget_type} to post ID: ${args.post_id}`);
|
|
2993
|
+
if (args.section_id)
|
|
2994
|
+
console.error(`📍 Target section: ${args.section_id}`);
|
|
2995
|
+
if (args.column_id)
|
|
2996
|
+
console.error(`📍 Target column: ${args.column_id}`);
|
|
2997
|
+
// Get current Elementor data using safe parsing utility
|
|
2998
|
+
console.error(`🔄 Fetching Elementor data for widget addition...`);
|
|
2999
|
+
const parsedResult = await this.safeGetElementorData(args.post_id);
|
|
3000
|
+
if (!parsedResult.success || !parsedResult.data) {
|
|
3001
|
+
console.error(`❌ Failed to get Elementor data: ${parsedResult.error}`);
|
|
3002
|
+
return this.createErrorResponse(`Failed to get Elementor data for post/page ID ${args.post_id}: ${parsedResult.error || 'Unknown error'}. Cannot add widget.`, "OPERATION_ERROR", "API_ERROR", "Operation failed");
|
|
3003
|
+
}
|
|
3004
|
+
const elementorData = parsedResult.data;
|
|
3005
|
+
console.error(`✅ Successfully parsed data for widget addition: ${elementorData.length} top-level elements`);
|
|
3006
|
+
// Generate unique ID for the widget
|
|
3007
|
+
const widgetId = Math.random().toString(36).substr(2, 8);
|
|
3008
|
+
// Create new widget
|
|
3009
|
+
const newWidget = {
|
|
3010
|
+
id: widgetId,
|
|
3011
|
+
elType: 'widget',
|
|
3012
|
+
widgetType: args.widget_type,
|
|
3013
|
+
isInner: false,
|
|
3014
|
+
settings: args.widget_settings || {},
|
|
3015
|
+
elements: []
|
|
3016
|
+
};
|
|
3017
|
+
let targetFound = false;
|
|
3018
|
+
// Function to find target container and add widget
|
|
3019
|
+
const visited = new Set(); // Create visited set once, outside the function
|
|
3020
|
+
const findAndAddWidget = (elements) => {
|
|
3021
|
+
for (let element of elements) {
|
|
3022
|
+
// Prevent infinite recursion by tracking visited elements
|
|
3023
|
+
if (visited.has(element.id)) {
|
|
3024
|
+
continue;
|
|
3025
|
+
}
|
|
3026
|
+
visited.add(element.id);
|
|
3027
|
+
// If column_id is specified, look for that specific column
|
|
3028
|
+
if (args.column_id && element.id === args.column_id && element.elType === 'column') {
|
|
3029
|
+
if (args.position !== undefined && args.position >= 0 && args.position < element.elements.length) {
|
|
3030
|
+
element.elements.splice(args.position, 0, newWidget);
|
|
3031
|
+
}
|
|
3032
|
+
else {
|
|
3033
|
+
element.elements.push(newWidget);
|
|
3034
|
+
}
|
|
3035
|
+
return true;
|
|
3036
|
+
}
|
|
3037
|
+
// If section_id is specified, add to section or container
|
|
3038
|
+
if (args.section_id && element.id === args.section_id && (element.elType === 'section' || element.elType === 'container')) {
|
|
3039
|
+
if (element.elType === 'container') {
|
|
3040
|
+
// For containers, add widget directly
|
|
3041
|
+
if (args.position !== undefined && args.position >= 0 && args.position < element.elements.length) {
|
|
3042
|
+
element.elements.splice(args.position, 0, newWidget);
|
|
3043
|
+
}
|
|
3044
|
+
else {
|
|
3045
|
+
element.elements.push(newWidget);
|
|
3046
|
+
}
|
|
3047
|
+
return true;
|
|
3048
|
+
}
|
|
3049
|
+
else if (element.elType === 'section') {
|
|
3050
|
+
// For sections, add to first column
|
|
3051
|
+
if (element.elements && element.elements.length > 0) {
|
|
3052
|
+
const firstColumn = element.elements[0];
|
|
3053
|
+
if (args.position !== undefined && args.position >= 0 && args.position < firstColumn.elements.length) {
|
|
3054
|
+
firstColumn.elements.splice(args.position, 0, newWidget);
|
|
3055
|
+
}
|
|
3056
|
+
else {
|
|
3057
|
+
firstColumn.elements.push(newWidget);
|
|
3058
|
+
}
|
|
3059
|
+
return true;
|
|
3060
|
+
}
|
|
3061
|
+
}
|
|
3062
|
+
}
|
|
3063
|
+
// If no specific target, add to first available column or container
|
|
3064
|
+
if (!args.section_id && !args.column_id) {
|
|
3065
|
+
if (element.elType === 'column' || element.elType === 'container') {
|
|
3066
|
+
element.elements.push(newWidget);
|
|
3067
|
+
return true;
|
|
3068
|
+
}
|
|
3069
|
+
}
|
|
3070
|
+
// Recursively search
|
|
3071
|
+
if (element.elements && element.elements.length > 0) {
|
|
3072
|
+
if (findAndAddWidget(element.elements)) {
|
|
3073
|
+
return true;
|
|
3074
|
+
}
|
|
3075
|
+
}
|
|
3076
|
+
}
|
|
3077
|
+
return false;
|
|
3078
|
+
};
|
|
3079
|
+
targetFound = findAndAddWidget(elementorData);
|
|
3080
|
+
if (!targetFound) {
|
|
3081
|
+
return this.createErrorResponse(`Target container not found (section_id: ${args.section_id}, column_id: ${args.column_id})`, "OPERATION_ERROR", "API_ERROR", "Operation failed");
|
|
3082
|
+
}
|
|
3083
|
+
// Update the page
|
|
3084
|
+
await this.updateElementorData({
|
|
3085
|
+
post_id: args.post_id,
|
|
3086
|
+
elementor_data: JSON.stringify(elementorData)
|
|
3087
|
+
});
|
|
3088
|
+
return this.createSuccessResponse({
|
|
3089
|
+
widget_id: widgetId,
|
|
3090
|
+
widget_type: args.widget_type,
|
|
3091
|
+
post_id: args.post_id,
|
|
3092
|
+
target_section: args.section_id,
|
|
3093
|
+
target_column: args.column_id,
|
|
3094
|
+
position: args.position || 'end'
|
|
3095
|
+
}, `Widget added successfully! Widget ID: ${widgetId}, Type: ${args.widget_type}`);
|
|
3096
|
+
}
|
|
3097
|
+
catch (error) {
|
|
3098
|
+
if (error instanceof McpError) {
|
|
3099
|
+
throw error;
|
|
3100
|
+
}
|
|
3101
|
+
return this.createErrorResponse(`Failed to add widget: ${error.message}`, "OPERATION_ERROR", "API_ERROR", "Operation failed");
|
|
3102
|
+
}
|
|
3103
|
+
}
|
|
3104
|
+
async insertWidgetAtPosition(args) {
|
|
3105
|
+
this.ensureAuthenticated();
|
|
3106
|
+
try {
|
|
3107
|
+
// Get current Elementor data using safe parsing utility
|
|
3108
|
+
const parsedResult = await this.safeGetElementorData(args.post_id);
|
|
3109
|
+
if (!parsedResult.success || !parsedResult.data) {
|
|
3110
|
+
return this.createErrorResponse(`Failed to get Elementor data for post/page ID ${args.post_id}: ${parsedResult.error || 'Unknown error'}`, "OPERATION_ERROR", "API_ERROR", "Operation failed");
|
|
3111
|
+
}
|
|
3112
|
+
const elementorData = parsedResult.data;
|
|
3113
|
+
// Generate unique ID for the widget
|
|
3114
|
+
const widgetId = Math.random().toString(36).substr(2, 8);
|
|
3115
|
+
// Create new widget
|
|
3116
|
+
const newWidget = {
|
|
3117
|
+
id: widgetId,
|
|
3118
|
+
elType: 'widget',
|
|
3119
|
+
widgetType: args.widget_type,
|
|
3120
|
+
isInner: false,
|
|
3121
|
+
settings: args.widget_settings || {},
|
|
3122
|
+
elements: []
|
|
3123
|
+
};
|
|
3124
|
+
let targetFound = false;
|
|
3125
|
+
// Function to find target element and insert widget
|
|
3126
|
+
const findAndInsertWidget = (elements, parent) => {
|
|
3127
|
+
for (let i = 0; i < elements.length; i++) {
|
|
3128
|
+
const element = elements[i];
|
|
3129
|
+
if (element.id === args.target_element_id) {
|
|
3130
|
+
const insertIndex = args.insert_position === 'before' ? i : i + 1;
|
|
3131
|
+
parent.elements.splice(insertIndex, 0, newWidget);
|
|
3132
|
+
return true;
|
|
3133
|
+
}
|
|
3134
|
+
if (element.elements && element.elements.length > 0) {
|
|
3135
|
+
if (findAndInsertWidget(element.elements, element)) {
|
|
3136
|
+
return true;
|
|
3137
|
+
}
|
|
3138
|
+
}
|
|
3139
|
+
}
|
|
3140
|
+
return false;
|
|
3141
|
+
};
|
|
3142
|
+
// Create a mock parent for top-level elements
|
|
3143
|
+
const mockParent = { elements: elementorData };
|
|
3144
|
+
targetFound = findAndInsertWidget(elementorData, mockParent);
|
|
3145
|
+
if (!targetFound) {
|
|
3146
|
+
return this.createErrorResponse(`Target element ID ${args.target_element_id} not found`, "OPERATION_ERROR", "API_ERROR", "Operation failed");
|
|
3147
|
+
}
|
|
3148
|
+
// Update the page
|
|
3149
|
+
await this.updateElementorData({
|
|
3150
|
+
post_id: args.post_id,
|
|
3151
|
+
elementor_data: JSON.stringify(elementorData)
|
|
3152
|
+
});
|
|
3153
|
+
return this.createSuccessResponse({
|
|
3154
|
+
operation_type: "insert_widget_at_position",
|
|
3155
|
+
widget_id: widgetId,
|
|
3156
|
+
widget_type: args.widget_type,
|
|
3157
|
+
target_element_id: args.target_element_id,
|
|
3158
|
+
insert_position: args.insert_position || 'after',
|
|
3159
|
+
post_id: args.post_id,
|
|
3160
|
+
widget_settings: args.widget_settings || {}
|
|
3161
|
+
}, `Widget inserted successfully! Widget ID: ${widgetId} (${args.widget_type}) ${args.insert_position || 'after'} element: ${args.target_element_id}`);
|
|
3162
|
+
}
|
|
3163
|
+
catch (error) {
|
|
3164
|
+
if (error instanceof McpError) {
|
|
3165
|
+
throw error;
|
|
3166
|
+
}
|
|
3167
|
+
return this.createErrorResponse(`Failed to insert widget: ${error.message}`, "OPERATION_ERROR", "API_ERROR", "Operation failed");
|
|
3168
|
+
}
|
|
3169
|
+
}
|
|
3170
|
+
async cloneWidget(args) {
|
|
3171
|
+
this.ensureAuthenticated();
|
|
3172
|
+
try {
|
|
3173
|
+
// Get current Elementor data using safe parsing utility
|
|
3174
|
+
const parsedResult = await this.safeGetElementorData(args.post_id);
|
|
3175
|
+
if (!parsedResult.success || !parsedResult.data) {
|
|
3176
|
+
return this.createErrorResponse(`Failed to get Elementor data for post/page ID ${args.post_id}: ${parsedResult.error || 'Unknown error'}`, "OPERATION_ERROR", "API_ERROR", "Operation failed");
|
|
3177
|
+
}
|
|
3178
|
+
const elementorData = parsedResult.data;
|
|
3179
|
+
let widgetToClone = null;
|
|
3180
|
+
// Function to find widget to clone
|
|
3181
|
+
const findWidgetVisited = new Set(); // Create visited set once, outside the function
|
|
3182
|
+
const findWidget = (elements) => {
|
|
3183
|
+
for (let element of elements) {
|
|
3184
|
+
// Prevent infinite recursion by tracking visited elements
|
|
3185
|
+
if (findWidgetVisited.has(element.id)) {
|
|
3186
|
+
continue;
|
|
3187
|
+
}
|
|
3188
|
+
findWidgetVisited.add(element.id);
|
|
3189
|
+
if (element.id === args.widget_id) {
|
|
3190
|
+
return JSON.parse(JSON.stringify(element)); // Deep copy
|
|
3191
|
+
}
|
|
3192
|
+
if (element.elements && element.elements.length > 0) {
|
|
3193
|
+
const found = findWidget(element.elements);
|
|
3194
|
+
if (found)
|
|
3195
|
+
return found;
|
|
3196
|
+
}
|
|
3197
|
+
}
|
|
3198
|
+
return null;
|
|
3199
|
+
};
|
|
3200
|
+
widgetToClone = findWidget(elementorData);
|
|
3201
|
+
if (!widgetToClone) {
|
|
3202
|
+
return this.createErrorResponse(`Widget ID ${args.widget_id} not found`, "OPERATION_ERROR", "API_ERROR", "Operation failed");
|
|
3203
|
+
}
|
|
3204
|
+
// Generate new ID for cloned widget
|
|
3205
|
+
const generateNewIds = (element) => {
|
|
3206
|
+
element.id = Math.random().toString(36).substr(2, 8);
|
|
3207
|
+
if (element.elements) {
|
|
3208
|
+
element.elements.forEach(generateNewIds);
|
|
3209
|
+
}
|
|
3210
|
+
};
|
|
3211
|
+
generateNewIds(widgetToClone);
|
|
3212
|
+
// Insert cloned widget
|
|
3213
|
+
if (args.target_element_id) {
|
|
3214
|
+
// Insert at specific position
|
|
3215
|
+
let targetFound = false;
|
|
3216
|
+
const insertWidgetVisited = new Set(); // Create visited set once, outside the function
|
|
3217
|
+
const findAndInsertWidget = (elements, parent) => {
|
|
3218
|
+
for (let i = 0; i < elements.length; i++) {
|
|
3219
|
+
const element = elements[i];
|
|
3220
|
+
// Prevent infinite recursion by tracking visited elements
|
|
3221
|
+
if (insertWidgetVisited.has(element.id)) {
|
|
3222
|
+
continue;
|
|
3223
|
+
}
|
|
3224
|
+
insertWidgetVisited.add(element.id);
|
|
3225
|
+
if (element.id === args.target_element_id) {
|
|
3226
|
+
const insertIndex = args.insert_position === 'before' ? i : i + 1;
|
|
3227
|
+
parent.elements.splice(insertIndex, 0, widgetToClone);
|
|
3228
|
+
return true;
|
|
3229
|
+
}
|
|
3230
|
+
if (element.elements && element.elements.length > 0) {
|
|
3231
|
+
if (findAndInsertWidget(element.elements, element)) {
|
|
3232
|
+
return true;
|
|
3233
|
+
}
|
|
3234
|
+
}
|
|
3235
|
+
}
|
|
3236
|
+
return false;
|
|
3237
|
+
};
|
|
3238
|
+
const mockParent = { elements: elementorData };
|
|
3239
|
+
targetFound = findAndInsertWidget(elementorData, mockParent);
|
|
3240
|
+
if (!targetFound) {
|
|
3241
|
+
return this.createErrorResponse(`Target element ID ${args.target_element_id} not found`, "OPERATION_ERROR", "API_ERROR", "Operation failed");
|
|
3242
|
+
}
|
|
3243
|
+
}
|
|
3244
|
+
else {
|
|
3245
|
+
// Add to first available column or container
|
|
3246
|
+
const containerVisited = new Set(); // Create visited set once, outside the function
|
|
3247
|
+
const findFirstContainer = (elements) => {
|
|
3248
|
+
for (let element of elements) {
|
|
3249
|
+
// Prevent infinite recursion by tracking visited elements
|
|
3250
|
+
if (containerVisited.has(element.id)) {
|
|
3251
|
+
continue;
|
|
3252
|
+
}
|
|
3253
|
+
containerVisited.add(element.id);
|
|
3254
|
+
// Check for traditional column
|
|
3255
|
+
if (element.elType === 'column') {
|
|
3256
|
+
element.elements.push(widgetToClone);
|
|
3257
|
+
return true;
|
|
3258
|
+
}
|
|
3259
|
+
// Check for new container (Elementor v3.6+)
|
|
3260
|
+
if (element.elType === 'container') {
|
|
3261
|
+
element.elements.push(widgetToClone);
|
|
3262
|
+
return true;
|
|
3263
|
+
}
|
|
3264
|
+
if (element.elements && element.elements.length > 0) {
|
|
3265
|
+
if (findFirstContainer(element.elements)) {
|
|
3266
|
+
return true;
|
|
3267
|
+
}
|
|
3268
|
+
}
|
|
3269
|
+
}
|
|
3270
|
+
return false;
|
|
3271
|
+
};
|
|
3272
|
+
if (!findFirstContainer(elementorData)) {
|
|
3273
|
+
return this.createErrorResponse('No column or container found to place cloned widget', "OPERATION_ERROR", "API_ERROR", "Operation failed");
|
|
3274
|
+
}
|
|
3275
|
+
}
|
|
3276
|
+
// Update the page
|
|
3277
|
+
await this.updateElementorData({
|
|
3278
|
+
post_id: args.post_id,
|
|
3279
|
+
elementor_data: JSON.stringify(elementorData)
|
|
3280
|
+
});
|
|
3281
|
+
return this.createSuccessResponse({
|
|
3282
|
+
post_id: args.post_id,
|
|
3283
|
+
original_widget_id: args.widget_id,
|
|
3284
|
+
new_widget_id: widgetToClone.id,
|
|
3285
|
+
widget_type: widgetToClone.widgetType || null,
|
|
3286
|
+
target_element_id: args.target_element_id || null,
|
|
3287
|
+
insert_position: args.insert_position || "after",
|
|
3288
|
+
operation: "widget_cloning",
|
|
3289
|
+
cloned_settings: widgetToClone.settings || {}
|
|
3290
|
+
}, `Widget cloned successfully! New widget ID: ${widgetToClone.id}`);
|
|
3291
|
+
}
|
|
3292
|
+
catch (error) {
|
|
3293
|
+
if (error instanceof McpError) {
|
|
3294
|
+
throw error;
|
|
3295
|
+
}
|
|
3296
|
+
return this.createErrorResponse(`Failed to clone widget: ${error.message}`, "OPERATION_ERROR", "API_ERROR", "Operation failed");
|
|
3297
|
+
}
|
|
3298
|
+
}
|
|
3299
|
+
async moveWidget(args) {
|
|
3300
|
+
this.ensureAuthenticated();
|
|
3301
|
+
try {
|
|
3302
|
+
// Get current Elementor data using safe parsing utility
|
|
3303
|
+
const parsedResult = await this.safeGetElementorData(args.post_id);
|
|
3304
|
+
if (!parsedResult.success || !parsedResult.data) {
|
|
3305
|
+
return this.createErrorResponse(`Failed to get Elementor data for post/page ID ${args.post_id}: ${parsedResult.error || 'Unknown error'}`, "OPERATION_ERROR", "API_ERROR", "Operation failed");
|
|
3306
|
+
}
|
|
3307
|
+
const elementorData = parsedResult.data;
|
|
3308
|
+
let widgetToMove = null;
|
|
3309
|
+
let sourceLocation = { column_id: null, previous_position: -1 };
|
|
3310
|
+
let targetLocation = {
|
|
3311
|
+
column_id: args.target_column_id || null,
|
|
3312
|
+
new_position: args.position !== undefined ? args.position : "end"
|
|
3313
|
+
};
|
|
3314
|
+
// Function to find and remove widget
|
|
3315
|
+
const findAndRemoveWidget = (elements, parent = null) => {
|
|
3316
|
+
for (let i = 0; i < elements.length; i++) {
|
|
3317
|
+
const element = elements[i];
|
|
3318
|
+
if (element.id === args.widget_id) {
|
|
3319
|
+
widgetToMove = elements.splice(i, 1)[0];
|
|
3320
|
+
if (parent && parent.elType === 'column') {
|
|
3321
|
+
sourceLocation.column_id = parent.id;
|
|
3322
|
+
sourceLocation.previous_position = i;
|
|
3323
|
+
}
|
|
3324
|
+
return true;
|
|
3325
|
+
}
|
|
3326
|
+
if (element.elements && element.elements.length > 0) {
|
|
3327
|
+
if (findAndRemoveWidget(element.elements, element)) {
|
|
3328
|
+
return true;
|
|
3329
|
+
}
|
|
3330
|
+
}
|
|
3331
|
+
}
|
|
3332
|
+
return false;
|
|
3333
|
+
};
|
|
3334
|
+
if (!findAndRemoveWidget(elementorData)) {
|
|
3335
|
+
return this.createErrorResponse("Widget ID ${args.widget_id} not found", "OPERATION_ERROR", "API_ERROR", "Operation failed");
|
|
3336
|
+
}
|
|
3337
|
+
// Function to find target and add widget
|
|
3338
|
+
const findTargetAndAddWidget = (elements) => {
|
|
3339
|
+
for (let element of elements) {
|
|
3340
|
+
// If column_id is specified, look for that specific column
|
|
3341
|
+
if (args.target_column_id && element.id === args.target_column_id && element.elType === 'column') {
|
|
3342
|
+
targetLocation.column_id = element.id;
|
|
3343
|
+
if (args.position !== undefined && args.position >= 0 && args.position < element.elements.length) {
|
|
3344
|
+
element.elements.splice(args.position, 0, widgetToMove);
|
|
3345
|
+
targetLocation.new_position = args.position;
|
|
3346
|
+
}
|
|
3347
|
+
else {
|
|
3348
|
+
element.elements.push(widgetToMove);
|
|
3349
|
+
targetLocation.new_position = element.elements.length - 1;
|
|
3350
|
+
}
|
|
3351
|
+
return true;
|
|
3352
|
+
}
|
|
3353
|
+
// If section_id is specified, add to first column of that section
|
|
3354
|
+
if (args.target_section_id && element.id === args.target_section_id && element.elType === 'section') {
|
|
3355
|
+
if (element.elements && element.elements.length > 0) {
|
|
3356
|
+
const firstColumn = element.elements[0];
|
|
3357
|
+
targetLocation.column_id = firstColumn.id;
|
|
3358
|
+
if (args.position !== undefined && args.position >= 0 && args.position < firstColumn.elements.length) {
|
|
3359
|
+
firstColumn.elements.splice(args.position, 0, widgetToMove);
|
|
3360
|
+
targetLocation.new_position = args.position;
|
|
3361
|
+
}
|
|
3362
|
+
else {
|
|
3363
|
+
firstColumn.elements.push(widgetToMove);
|
|
3364
|
+
targetLocation.new_position = firstColumn.elements.length - 1;
|
|
3365
|
+
}
|
|
3366
|
+
return true;
|
|
3367
|
+
}
|
|
3368
|
+
}
|
|
3369
|
+
if (element.elements && element.elements.length > 0) {
|
|
3370
|
+
if (findTargetAndAddWidget(element.elements)) {
|
|
3371
|
+
return true;
|
|
3372
|
+
}
|
|
3373
|
+
}
|
|
3374
|
+
}
|
|
3375
|
+
return false;
|
|
3376
|
+
};
|
|
3377
|
+
if (!findTargetAndAddWidget(elementorData)) {
|
|
3378
|
+
return this.createErrorResponse(`Target container not found (section_id: ${args.target_section_id}, column_id: ${args.target_column_id})`, "OPERATION_ERROR", "API_ERROR", "Operation failed");
|
|
3379
|
+
}
|
|
3380
|
+
// Update the page
|
|
3381
|
+
await this.updateElementorData({
|
|
3382
|
+
post_id: args.post_id,
|
|
3383
|
+
elementor_data: JSON.stringify(elementorData)
|
|
3384
|
+
});
|
|
3385
|
+
return this.createSuccessResponse({
|
|
3386
|
+
post_id: args.post_id,
|
|
3387
|
+
widget_id: args.widget_id,
|
|
3388
|
+
widget_type: widgetToMove.widgetType || null,
|
|
3389
|
+
source_location: sourceLocation,
|
|
3390
|
+
target_location: targetLocation,
|
|
3391
|
+
operation: "widget_move"
|
|
3392
|
+
}, `Widget moved successfully! Widget ID: ${args.widget_id}`);
|
|
3393
|
+
}
|
|
3394
|
+
catch (error) {
|
|
3395
|
+
if (error instanceof McpError) {
|
|
3396
|
+
throw error;
|
|
3397
|
+
}
|
|
3398
|
+
return this.createErrorResponse(`Failed to move widget: ${error.message}`, "OPERATION_ERROR", "API_ERROR", "Operation failed");
|
|
3399
|
+
}
|
|
3400
|
+
}
|
|
3401
|
+
// Element Management Tools
|
|
3402
|
+
async deleteElementorElement(args) {
|
|
3403
|
+
this.ensureAuthenticated();
|
|
3404
|
+
try {
|
|
3405
|
+
// Get current Elementor data using safe parsing utility
|
|
3406
|
+
const parsedResult = await this.safeGetElementorData(args.post_id);
|
|
3407
|
+
if (!parsedResult.success || !parsedResult.data) {
|
|
3408
|
+
return this.createErrorResponse(`Failed to get Elementor data for post/page ID ${args.post_id}: ${parsedResult.error || 'Unknown error'}`, "OPERATION_ERROR", "API_ERROR", "Operation failed");
|
|
3409
|
+
}
|
|
3410
|
+
const elementorData = parsedResult.data;
|
|
3411
|
+
let elementDeleted = false;
|
|
3412
|
+
let deletedElement = null;
|
|
3413
|
+
let parentContainer = null;
|
|
3414
|
+
let remainingElementsInParent = 0;
|
|
3415
|
+
// Function to find and delete element
|
|
3416
|
+
const findAndDeleteElement = (elements, parent = null) => {
|
|
3417
|
+
for (let i = 0; i < elements.length; i++) {
|
|
3418
|
+
const element = elements[i];
|
|
3419
|
+
if (element.id === args.element_id) {
|
|
3420
|
+
deletedElement = { ...element };
|
|
3421
|
+
parentContainer = parent ? {
|
|
3422
|
+
type: parent.elType,
|
|
3423
|
+
id: parent.id
|
|
3424
|
+
} : null;
|
|
3425
|
+
elements.splice(i, 1);
|
|
3426
|
+
remainingElementsInParent = elements.length;
|
|
3427
|
+
return true;
|
|
3428
|
+
}
|
|
3429
|
+
if (element.elements && element.elements.length > 0) {
|
|
3430
|
+
if (findAndDeleteElement(element.elements, element)) {
|
|
3431
|
+
return true;
|
|
3432
|
+
}
|
|
3433
|
+
}
|
|
3434
|
+
}
|
|
3435
|
+
return false;
|
|
3436
|
+
};
|
|
3437
|
+
elementDeleted = findAndDeleteElement(elementorData);
|
|
3438
|
+
if (!elementDeleted) {
|
|
3439
|
+
return this.createErrorResponse(`Element ID ${args.element_id} not found`, "OPERATION_ERROR", "API_ERROR", "Operation failed");
|
|
3440
|
+
}
|
|
3441
|
+
// Update the page
|
|
3442
|
+
await this.updateElementorData({
|
|
3443
|
+
post_id: args.post_id,
|
|
3444
|
+
elementor_data: JSON.stringify(elementorData)
|
|
3445
|
+
});
|
|
3446
|
+
return this.createSuccessResponse({
|
|
3447
|
+
post_id: args.post_id,
|
|
3448
|
+
deleted_element_id: args.element_id,
|
|
3449
|
+
element_type: deletedElement.elType,
|
|
3450
|
+
widget_type: deletedElement.widgetType || null,
|
|
3451
|
+
parent_container: parentContainer,
|
|
3452
|
+
operation: "element_deletion",
|
|
3453
|
+
remaining_elements_in_parent: remainingElementsInParent
|
|
3454
|
+
}, `Element deleted successfully! Element ID: ${args.element_id}`);
|
|
3455
|
+
}
|
|
3456
|
+
catch (error) {
|
|
3457
|
+
if (error instanceof McpError) {
|
|
3458
|
+
throw error;
|
|
3459
|
+
}
|
|
3460
|
+
return this.createErrorResponse(`Failed to delete element: ${error.message}`, "OPERATION_ERROR", "API_ERROR", "Operation failed");
|
|
3461
|
+
}
|
|
3462
|
+
}
|
|
3463
|
+
async reorderElements(args) {
|
|
3464
|
+
this.ensureAuthenticated();
|
|
3465
|
+
try {
|
|
3466
|
+
// Get current Elementor data using safe parsing utility
|
|
3467
|
+
const parsedResult = await this.safeGetElementorData(args.post_id);
|
|
3468
|
+
if (!parsedResult.success || !parsedResult.data) {
|
|
3469
|
+
return this.createErrorResponse("Failed to get Elementor data for post/page ID ${args.post_id}: ${parsedResult.error || 'Unknown error'}", "OPERATION_ERROR", "API_ERROR", "Operation failed");
|
|
3470
|
+
}
|
|
3471
|
+
const elementorData = parsedResult.data;
|
|
3472
|
+
let containerFound = false;
|
|
3473
|
+
let containerType = '';
|
|
3474
|
+
let newOrderDetails = [];
|
|
3475
|
+
// Function to find container and reorder elements
|
|
3476
|
+
const findAndReorderElements = (elements) => {
|
|
3477
|
+
for (let element of elements) {
|
|
3478
|
+
if (element.id === args.container_id) {
|
|
3479
|
+
containerType = element.elType;
|
|
3480
|
+
const oldElements = [...element.elements];
|
|
3481
|
+
const newElements = [];
|
|
3482
|
+
// Reorder according to provided array
|
|
3483
|
+
for (let elementId of args.element_ids) {
|
|
3484
|
+
const foundElement = oldElements.find(el => el.id === elementId);
|
|
3485
|
+
if (foundElement) {
|
|
3486
|
+
newElements.push(foundElement);
|
|
3487
|
+
}
|
|
3488
|
+
}
|
|
3489
|
+
// Add any elements that weren't in the reorder list
|
|
3490
|
+
for (let oldElement of oldElements) {
|
|
3491
|
+
if (!args.element_ids.includes(oldElement.id)) {
|
|
3492
|
+
newElements.push(oldElement);
|
|
3493
|
+
}
|
|
3494
|
+
}
|
|
3495
|
+
// Track the new order details
|
|
3496
|
+
newOrderDetails = newElements.map((el, index) => ({
|
|
3497
|
+
element_id: el.id,
|
|
3498
|
+
element_type: el.elType,
|
|
3499
|
+
widget_type: el.widgetType || null,
|
|
3500
|
+
position: index
|
|
3501
|
+
}));
|
|
3502
|
+
element.elements = newElements;
|
|
3503
|
+
return true;
|
|
3504
|
+
}
|
|
3505
|
+
if (element.elements && element.elements.length > 0) {
|
|
3506
|
+
if (findAndReorderElements(element.elements)) {
|
|
3507
|
+
return true;
|
|
3508
|
+
}
|
|
3509
|
+
}
|
|
3510
|
+
}
|
|
3511
|
+
return false;
|
|
3512
|
+
};
|
|
3513
|
+
containerFound = findAndReorderElements(elementorData);
|
|
3514
|
+
if (!containerFound) {
|
|
3515
|
+
return this.createErrorResponse(`Container ID ${args.container_id} not found`, "OPERATION_ERROR", "API_ERROR", "Operation failed");
|
|
3516
|
+
}
|
|
3517
|
+
// Update the page
|
|
3518
|
+
await this.updateElementorData({
|
|
3519
|
+
post_id: args.post_id,
|
|
3520
|
+
elementor_data: JSON.stringify(elementorData)
|
|
3521
|
+
});
|
|
3522
|
+
return this.createSuccessResponse({
|
|
3523
|
+
post_id: args.post_id,
|
|
3524
|
+
container_id: args.container_id,
|
|
3525
|
+
container_type: containerType,
|
|
3526
|
+
elements_reordered: newOrderDetails.length,
|
|
3527
|
+
new_order: newOrderDetails,
|
|
3528
|
+
operation: "element_reordering"
|
|
3529
|
+
}, `Elements reordered successfully in container: ${args.container_id}`);
|
|
3530
|
+
}
|
|
3531
|
+
catch (error) {
|
|
3532
|
+
if (error instanceof McpError) {
|
|
3533
|
+
throw error;
|
|
3534
|
+
}
|
|
3535
|
+
return this.createErrorResponse(`Failed to reorder elements: ${error.message}`, "OPERATION_ERROR", "API_ERROR", "Operation failed");
|
|
3536
|
+
}
|
|
3537
|
+
}
|
|
3538
|
+
async copyElementSettings(args) {
|
|
3539
|
+
this.ensureAuthenticated();
|
|
3540
|
+
try {
|
|
3541
|
+
// Get current Elementor data using safe parsing utility
|
|
3542
|
+
const parsedResult = await this.safeGetElementorData(args.post_id);
|
|
3543
|
+
if (!parsedResult.success || !parsedResult.data) {
|
|
3544
|
+
return this.createErrorResponse(`Failed to get Elementor data for post/page ID ${args.post_id}: ${parsedResult.error || 'Unknown error'}`, "OPERATION_ERROR", "API_ERROR", "Operation failed");
|
|
3545
|
+
}
|
|
3546
|
+
const elementorData = parsedResult.data;
|
|
3547
|
+
let sourceElement = null;
|
|
3548
|
+
let targetElement = null;
|
|
3549
|
+
// Function to find elements
|
|
3550
|
+
const findElement = (elements, elementId) => {
|
|
3551
|
+
for (let element of elements) {
|
|
3552
|
+
if (element.id === elementId) {
|
|
3553
|
+
return element;
|
|
3554
|
+
}
|
|
3555
|
+
if (element.elements && element.elements.length > 0) {
|
|
3556
|
+
const found = findElement(element.elements, elementId);
|
|
3557
|
+
if (found)
|
|
3558
|
+
return found;
|
|
3559
|
+
}
|
|
3560
|
+
}
|
|
3561
|
+
return null;
|
|
3562
|
+
};
|
|
3563
|
+
sourceElement = findElement(elementorData, args.source_element_id);
|
|
3564
|
+
targetElement = findElement(elementorData, args.target_element_id);
|
|
3565
|
+
if (!sourceElement) {
|
|
3566
|
+
return this.createErrorResponse(`Source element ID ${args.source_element_id} not found`, "OPERATION_ERROR", "API_ERROR", "Operation failed");
|
|
3567
|
+
}
|
|
3568
|
+
if (!targetElement) {
|
|
3569
|
+
return this.createErrorResponse("Target element ID ${args.target_element_id} not found", "OPERATION_ERROR", "API_ERROR", "Operation failed");
|
|
3570
|
+
}
|
|
3571
|
+
// Track what settings were copied and skipped
|
|
3572
|
+
let settingsCopied = [];
|
|
3573
|
+
let settingsSkipped = [];
|
|
3574
|
+
// Copy settings
|
|
3575
|
+
if (args.settings_to_copy && args.settings_to_copy.length > 0) {
|
|
3576
|
+
// Copy specific settings
|
|
3577
|
+
for (let setting of args.settings_to_copy) {
|
|
3578
|
+
if (sourceElement.settings && sourceElement.settings[setting] !== undefined) {
|
|
3579
|
+
if (!targetElement.settings)
|
|
3580
|
+
targetElement.settings = {};
|
|
3581
|
+
targetElement.settings[setting] = JSON.parse(JSON.stringify(sourceElement.settings[setting]));
|
|
3582
|
+
settingsCopied.push(setting);
|
|
3583
|
+
}
|
|
3584
|
+
else {
|
|
3585
|
+
settingsSkipped.push(setting);
|
|
3586
|
+
}
|
|
3587
|
+
}
|
|
3588
|
+
}
|
|
3589
|
+
else {
|
|
3590
|
+
// Copy all settings
|
|
3591
|
+
if (sourceElement.settings) {
|
|
3592
|
+
targetElement.settings = JSON.parse(JSON.stringify(sourceElement.settings));
|
|
3593
|
+
settingsCopied = Object.keys(sourceElement.settings);
|
|
3594
|
+
}
|
|
3595
|
+
}
|
|
3596
|
+
// Update the page
|
|
3597
|
+
await this.updateElementorData({
|
|
3598
|
+
post_id: args.post_id,
|
|
3599
|
+
elementor_data: JSON.stringify(elementorData)
|
|
3600
|
+
});
|
|
3601
|
+
return this.createSuccessResponse({
|
|
3602
|
+
post_id: args.post_id,
|
|
3603
|
+
source_element_id: args.source_element_id,
|
|
3604
|
+
source_element_type: sourceElement.elType,
|
|
3605
|
+
source_widget_type: sourceElement.widgetType || null,
|
|
3606
|
+
target_element_id: args.target_element_id,
|
|
3607
|
+
target_element_type: targetElement.elType,
|
|
3608
|
+
target_widget_type: targetElement.widgetType || null,
|
|
3609
|
+
operation: "settings_copy",
|
|
3610
|
+
settings_copied: settingsCopied,
|
|
3611
|
+
settings_skipped: settingsSkipped
|
|
3612
|
+
}, `Settings copied successfully from ${args.source_element_id} to ${args.target_element_id}`);
|
|
3613
|
+
}
|
|
3614
|
+
catch (error) {
|
|
3615
|
+
if (error instanceof McpError) {
|
|
3616
|
+
throw error;
|
|
3617
|
+
}
|
|
3618
|
+
return this.createErrorResponse(`Failed to copy element settings: ${error.message}`, "OPERATION_ERROR", "API_ERROR", "Operation failed");
|
|
3619
|
+
}
|
|
3620
|
+
}
|
|
3621
|
+
// Performance & Optimization
|
|
3622
|
+
async clearElementorCacheGeneral(args) {
|
|
3623
|
+
this.ensureAuthenticated();
|
|
3624
|
+
try {
|
|
3625
|
+
await this.clearElementorCache(args.post_id);
|
|
3626
|
+
return this.createSuccessResponse({
|
|
3627
|
+
operation: "cache_clear",
|
|
3628
|
+
scope: args.post_id ? "specific_post" : "general",
|
|
3629
|
+
post_id: args.post_id || null,
|
|
3630
|
+
cache_cleared: true
|
|
3631
|
+
}, args.post_id
|
|
3632
|
+
? `Cache cleared successfully for post/page ID: ${args.post_id}`
|
|
3633
|
+
: "General Elementor cache cleared successfully");
|
|
3634
|
+
}
|
|
3635
|
+
catch (error) {
|
|
3636
|
+
return this.createErrorResponse(`Failed to clear cache: ${error.message}`, "CLEAR_CACHE_ERROR", "API_ERROR", `Cache clearing operation failed${args.post_id ? ` for post/page ${args.post_id}` : ''}`);
|
|
3637
|
+
}
|
|
3638
|
+
}
|
|
3639
|
+
// Advanced Element Operations
|
|
3640
|
+
async findElementsByType(args) {
|
|
3641
|
+
this.ensureAuthenticated();
|
|
3642
|
+
try {
|
|
3643
|
+
// Get current Elementor data using safe parsing utility
|
|
3644
|
+
const parsedResult = await this.safeGetElementorData(args.post_id);
|
|
3645
|
+
if (!parsedResult.success || !parsedResult.data) {
|
|
3646
|
+
return this.createErrorResponse(`No Elementor data found for post/page ID ${args.post_id}: ${parsedResult.error || 'Unknown error'}`, 'FIND_ELEMENTS_ERROR', 'API_ERROR', 'Failed to retrieve Elementor data');
|
|
3647
|
+
}
|
|
3648
|
+
const elementorData = parsedResult.data;
|
|
3649
|
+
const foundElements = [];
|
|
3650
|
+
// Function to find elements by type
|
|
3651
|
+
const findElementsByTypeRecursive = (elements) => {
|
|
3652
|
+
for (let element of elements) {
|
|
3653
|
+
if (element.widgetType === args.widget_type) {
|
|
3654
|
+
const result = {
|
|
3655
|
+
id: element.id,
|
|
3656
|
+
widgetType: element.widgetType
|
|
3657
|
+
};
|
|
3658
|
+
if (args.include_settings && element.settings) {
|
|
3659
|
+
result.settings = element.settings;
|
|
3660
|
+
}
|
|
3661
|
+
foundElements.push(result);
|
|
3662
|
+
}
|
|
3663
|
+
if (element.elements && element.elements.length > 0) {
|
|
3664
|
+
findElementsByTypeRecursive(element.elements);
|
|
3665
|
+
}
|
|
3666
|
+
}
|
|
3667
|
+
};
|
|
3668
|
+
findElementsByTypeRecursive(elementorData);
|
|
3669
|
+
return this.createSuccessResponse({
|
|
3670
|
+
post_id: args.post_id,
|
|
3671
|
+
widget_type: args.widget_type,
|
|
3672
|
+
found_elements: foundElements,
|
|
3673
|
+
total_found: foundElements.length,
|
|
3674
|
+
include_settings: args.include_settings || false
|
|
3675
|
+
}, `Found ${foundElements.length} elements of type "${args.widget_type}" in post/page ID ${args.post_id}`);
|
|
3676
|
+
}
|
|
3677
|
+
catch (error) {
|
|
3678
|
+
if (error instanceof McpError) {
|
|
3679
|
+
throw error;
|
|
3680
|
+
}
|
|
3681
|
+
return this.createErrorResponse(`Failed to find elements by type: ${error.message}`, "FIND_ELEMENTS_ERROR", "API_ERROR", "Operation failed");
|
|
3682
|
+
}
|
|
3683
|
+
}
|
|
3684
|
+
// Template Management (Requires Elementor Pro)
|
|
3685
|
+
async createElementorTemplate(args) {
|
|
3686
|
+
return this.createErrorResponse('Template management requires Elementor Pro API access. This feature is not available in the free version.', 'PRO_FEATURE_ERROR', 'FEATURE_UNAVAILABLE', 'Elementor Pro required');
|
|
3687
|
+
}
|
|
3688
|
+
async applyTemplateToPage(args) {
|
|
3689
|
+
return this.createErrorResponse('Template management requires Elementor Pro API access. This feature is not available in the free version.', 'PRO_FEATURE_ERROR', 'FEATURE_UNAVAILABLE', 'Elementor Pro required');
|
|
3690
|
+
}
|
|
3691
|
+
async exportElementorTemplate(args) {
|
|
3692
|
+
return this.createErrorResponse('Template management requires Elementor Pro API access. This feature is not available in the free version.', 'PRO_FEATURE_ERROR', 'FEATURE_UNAVAILABLE', 'Elementor Pro required');
|
|
3693
|
+
}
|
|
3694
|
+
async importElementorTemplate(args) {
|
|
3695
|
+
return this.createErrorResponse('Template management requires Elementor Pro API access. This feature is not available in the free version.', 'PRO_FEATURE_ERROR', 'FEATURE_UNAVAILABLE', 'Elementor Pro required');
|
|
3696
|
+
}
|
|
3697
|
+
// Global Settings (Requires Elementor Pro)
|
|
3698
|
+
async getElementorGlobalColors(args) {
|
|
3699
|
+
return this.createErrorResponse('Global settings require Elementor Pro API access. This feature is not available in the free version.', 'PRO_FEATURE_ERROR', 'FEATURE_UNAVAILABLE', 'Elementor Pro required');
|
|
3700
|
+
}
|
|
3701
|
+
async updateElementorGlobalColors(args) {
|
|
3702
|
+
return this.createErrorResponse('Global settings require Elementor Pro API access. This feature is not available in the free version.', 'PRO_FEATURE_ERROR', 'FEATURE_UNAVAILABLE', 'Elementor Pro required');
|
|
3703
|
+
}
|
|
3704
|
+
async getElementorGlobalFonts(args) {
|
|
3705
|
+
return this.createErrorResponse('Global settings require Elementor Pro API access. This feature is not available in the free version.', 'PRO_FEATURE_ERROR', 'FEATURE_UNAVAILABLE', 'Elementor Pro required');
|
|
3706
|
+
}
|
|
3707
|
+
async updateElementorGlobalFonts(args) {
|
|
3708
|
+
return this.createErrorResponse('Global settings require Elementor Pro API access. This feature is not available in the free version.', 'PRO_FEATURE_ERROR', 'FEATURE_UNAVAILABLE', 'Elementor Pro required');
|
|
3709
|
+
}
|
|
3710
|
+
// Advanced Operations (Not Yet Implemented)
|
|
3711
|
+
async rebuildPageStructure(args) {
|
|
3712
|
+
return this.createErrorResponse('Page structure rebuilding is a complex operation not yet implemented. Please use individual element manipulation tools instead.', 'NOT_IMPLEMENTED', 'FEATURE_UNAVAILABLE', 'Use individual element manipulation tools for page building');
|
|
3713
|
+
}
|
|
3714
|
+
async validateElementorData(args) {
|
|
3715
|
+
return this.createErrorResponse('Data validation not yet implemented. Please check data manually using get_elementor_data.', 'NOT_IMPLEMENTED', 'FEATURE_UNAVAILABLE', 'Use get_elementor_data to manually inspect page structure');
|
|
3716
|
+
}
|
|
3717
|
+
async regenerateCSS(args) {
|
|
3718
|
+
return this.createErrorResponse('CSS regeneration requires direct server access. Please use the Elementor admin interface: Elementor → Tools → Regenerate CSS & Data.', 'SERVER_ACCESS_REQUIRED', 'FEATURE_UNAVAILABLE', 'Use WordPress admin: Elementor → Tools → Regenerate CSS & Data');
|
|
3719
|
+
}
|
|
3720
|
+
async optimizeElementorAssets(args) {
|
|
3721
|
+
return this.createErrorResponse('Asset optimization requires direct server access. Please use WordPress optimization plugins or the Elementor admin interface.', 'SERVER_ACCESS_REQUIRED', 'FEATURE_UNAVAILABLE', 'Use WordPress optimization plugins or Elementor admin interface');
|
|
3722
|
+
}
|
|
3723
|
+
async bulkUpdateWidgetSettings(args) {
|
|
3724
|
+
return this.createErrorResponse('Bulk widget updates not yet implemented. Please use individual widget update tools or find_elements_by_type to identify widgets first.', 'NOT_IMPLEMENTED', 'FEATURE_UNAVAILABLE', 'Use individual widget update tools or find_elements_by_type');
|
|
3725
|
+
}
|
|
3726
|
+
async replaceWidgetContent(args) {
|
|
3727
|
+
return this.createErrorResponse('Widget content replacement not yet implemented. Please use individual widget update tools.', 'NOT_IMPLEMENTED', 'FEATURE_UNAVAILABLE', 'Use individual widget update tools');
|
|
3728
|
+
}
|
|
3729
|
+
async getElementorCustomFields(args) {
|
|
3730
|
+
return this.createErrorResponse('Custom fields integration not yet implemented. Please use WordPress REST API to access custom fields directly.', 'NOT_IMPLEMENTED', 'FEATURE_UNAVAILABLE', 'Use WordPress REST API for custom fields access');
|
|
3731
|
+
}
|
|
3732
|
+
async updateDynamicContentSources(args) {
|
|
3733
|
+
return this.createErrorResponse('Dynamic content management not yet implemented. Please update widget settings manually with dynamic field configurations.', 'NOT_IMPLEMENTED', 'FEATURE_UNAVAILABLE', 'Update widget settings manually with dynamic field configurations');
|
|
3734
|
+
}
|
|
3735
|
+
async getElementorRevisions(args) {
|
|
3736
|
+
return this.createErrorResponse('Revision management not yet implemented. Please use WordPress admin interface to access revisions.', 'NOT_IMPLEMENTED', 'FEATURE_UNAVAILABLE', 'Use WordPress admin interface for revision management');
|
|
3737
|
+
}
|
|
3738
|
+
async restoreElementorRevision(args) {
|
|
3739
|
+
return this.createErrorResponse('Revision management not yet implemented. Please use WordPress admin interface to restore revisions.', 'NOT_IMPLEMENTED', 'FEATURE_UNAVAILABLE', 'Use WordPress admin interface for revision management');
|
|
3740
|
+
}
|
|
3741
|
+
async compareElementorRevisions(args) {
|
|
3742
|
+
return this.createErrorResponse('Revision management not yet implemented. Please use WordPress admin interface to compare revisions.', 'NOT_IMPLEMENTED', 'FEATURE_UNAVAILABLE', 'Use WordPress admin interface for revision management');
|
|
3743
|
+
}
|
|
3744
|
+
async run() {
|
|
3745
|
+
const transport = new StdioServerTransport();
|
|
3746
|
+
await this.server.connect(transport);
|
|
3747
|
+
console.error('Elementor WordPress MCP server running on stdio');
|
|
3748
|
+
}
|
|
3749
|
+
}
|
|
3750
|
+
const server = new ElementorWordPressMCP();
|
|
3751
|
+
server.run().catch(console.error);
|
|
3752
|
+
//# sourceMappingURL=index-backup.js.map
|