@wplaunchify/ml-mcp-server 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +22 -0
- package/README.md +220 -0
- package/build/cli.d.ts +2 -0
- package/build/cli.js +52 -0
- package/build/server.d.ts +2 -0
- package/build/server.js +97 -0
- package/build/tools/comments.d.ts +212 -0
- package/build/tools/comments.js +181 -0
- package/build/tools/fluent-affiliate.d.ts +2 -0
- package/build/tools/fluent-affiliate.js +3 -0
- package/build/tools/fluent-cart.d.ts +706 -0
- package/build/tools/fluent-cart.js +642 -0
- package/build/tools/fluent-community-BACKUP.d.ts +364 -0
- package/build/tools/fluent-community-BACKUP.js +883 -0
- package/build/tools/fluent-community-MINIMAL.d.ts +69 -0
- package/build/tools/fluent-community-MINIMAL.js +92 -0
- package/build/tools/fluent-community-design.d.ts +3 -0
- package/build/tools/fluent-community-design.js +150 -0
- package/build/tools/fluent-community-layout.d.ts +119 -0
- package/build/tools/fluent-community-layout.js +88 -0
- package/build/tools/fluent-community.d.ts +364 -0
- package/build/tools/fluent-community.js +528 -0
- package/build/tools/fluent-crm.d.ts +3 -0
- package/build/tools/fluent-crm.js +392 -0
- package/build/tools/index.d.ts +2205 -0
- package/build/tools/index.js +54 -0
- package/build/tools/media.d.ts +135 -0
- package/build/tools/media.js +168 -0
- package/build/tools/ml-canvas.d.ts +91 -0
- package/build/tools/ml-canvas.js +109 -0
- package/build/tools/ml-image-editor.d.ts +230 -0
- package/build/tools/ml-image-editor.js +270 -0
- package/build/tools/ml-media-hub.d.ts +575 -0
- package/build/tools/ml-media-hub.js +714 -0
- package/build/tools/plugin-repository.d.ts +62 -0
- package/build/tools/plugin-repository.js +149 -0
- package/build/tools/plugins.d.ts +129 -0
- package/build/tools/plugins.js +148 -0
- package/build/tools/unified-content.d.ts +313 -0
- package/build/tools/unified-content.js +615 -0
- package/build/tools/unified-taxonomies.d.ts +229 -0
- package/build/tools/unified-taxonomies.js +479 -0
- package/build/tools/users.d.ts +227 -0
- package/build/tools/users.js +182 -0
- package/build/types/wordpress-types.d.ts +151 -0
- package/build/types/wordpress-types.js +2 -0
- package/build/wordpress.d.ts +26 -0
- package/build/wordpress.js +223 -0
- package/package.json +67 -0
|
@@ -0,0 +1,615 @@
|
|
|
1
|
+
import { makeWordPressRequest, logToFile } from '../wordpress.js';
|
|
2
|
+
import { z } from 'zod';
|
|
3
|
+
// Cache for post types to reduce API calls
|
|
4
|
+
let postTypesCache = null;
|
|
5
|
+
let cacheTimestamp = 0;
|
|
6
|
+
const CACHE_DURATION = 5 * 60 * 1000; // 5 minutes
|
|
7
|
+
// Helper function to get all post types with caching
|
|
8
|
+
async function getPostTypes(forceRefresh = false) {
|
|
9
|
+
const now = Date.now();
|
|
10
|
+
if (!forceRefresh && postTypesCache && (now - cacheTimestamp) < CACHE_DURATION) {
|
|
11
|
+
logToFile('Using cached post types');
|
|
12
|
+
return postTypesCache;
|
|
13
|
+
}
|
|
14
|
+
try {
|
|
15
|
+
logToFile('Fetching post types from API');
|
|
16
|
+
const response = await makeWordPressRequest('GET', 'wp/v2/types');
|
|
17
|
+
postTypesCache = response;
|
|
18
|
+
cacheTimestamp = now;
|
|
19
|
+
return response;
|
|
20
|
+
}
|
|
21
|
+
catch (error) {
|
|
22
|
+
logToFile(`Error fetching post types: ${error.message}`);
|
|
23
|
+
throw error;
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
// Helper function to get the correct endpoint for a content type
|
|
27
|
+
function getContentEndpoint(contentType) {
|
|
28
|
+
const endpointMap = {
|
|
29
|
+
'post': 'wp/v2/posts',
|
|
30
|
+
'page': 'wp/v2/pages'
|
|
31
|
+
};
|
|
32
|
+
return endpointMap[contentType] || `wp/v2/${contentType}`;
|
|
33
|
+
}
|
|
34
|
+
// Helper function to parse URL and extract slug and potential post type hints
|
|
35
|
+
function parseUrl(url) {
|
|
36
|
+
try {
|
|
37
|
+
const urlObj = new URL(url);
|
|
38
|
+
const pathname = urlObj.pathname;
|
|
39
|
+
// Remove trailing slash and split path
|
|
40
|
+
const pathParts = pathname.replace(/\/$/, '').split('/').filter(Boolean);
|
|
41
|
+
// The slug is typically the last part of the URL
|
|
42
|
+
const slug = pathParts[pathParts.length - 1] || '';
|
|
43
|
+
// Path hints can help identify the post type
|
|
44
|
+
const pathHints = pathParts.slice(0, -1);
|
|
45
|
+
return { slug, pathHints };
|
|
46
|
+
}
|
|
47
|
+
catch (error) {
|
|
48
|
+
logToFile(`Error parsing URL ${url}: ${error}`);
|
|
49
|
+
return { slug: '', pathHints: [] };
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
// Helper function to find content across multiple post types
|
|
53
|
+
async function findContentAcrossTypes(slug, contentTypes) {
|
|
54
|
+
const typesToSearch = contentTypes || [];
|
|
55
|
+
// If no specific content types provided, get all available types
|
|
56
|
+
if (typesToSearch.length === 0) {
|
|
57
|
+
const allTypes = await getPostTypes();
|
|
58
|
+
typesToSearch.push(...Object.keys(allTypes).filter(type => type !== 'attachment' && type !== 'wp_block'));
|
|
59
|
+
}
|
|
60
|
+
logToFile(`Searching for slug "${slug}" across content types: ${typesToSearch.join(', ')}`);
|
|
61
|
+
// Search each content type for the slug
|
|
62
|
+
for (const contentType of typesToSearch) {
|
|
63
|
+
try {
|
|
64
|
+
const endpoint = getContentEndpoint(contentType);
|
|
65
|
+
const response = await makeWordPressRequest('GET', endpoint, {
|
|
66
|
+
slug: slug,
|
|
67
|
+
per_page: 1
|
|
68
|
+
});
|
|
69
|
+
if (Array.isArray(response) && response.length > 0) {
|
|
70
|
+
logToFile(`Found content with slug "${slug}" in content type "${contentType}"`);
|
|
71
|
+
return { content: response[0], contentType };
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
catch (error) {
|
|
75
|
+
logToFile(`Error searching ${contentType}: ${error}`);
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
return null;
|
|
79
|
+
}
|
|
80
|
+
// Schema definitions
|
|
81
|
+
const listContentSchema = z.object({
|
|
82
|
+
content_type: z.string().describe("The content type slug (e.g., 'post', 'page', 'product', 'documentation')"),
|
|
83
|
+
page: z.number().optional().describe("Page number (default 1)"),
|
|
84
|
+
per_page: z.number().min(1).max(100).optional().describe("Items per page (default 10, max 100)"),
|
|
85
|
+
search: z.string().optional().describe("Search term for content title or body"),
|
|
86
|
+
slug: z.string().optional().describe("Limit result to content with a specific slug"),
|
|
87
|
+
status: z.string().optional().describe("Content status (publish, draft, etc.)"),
|
|
88
|
+
author: z.union([z.number(), z.array(z.number())]).optional().describe("Author ID or array of IDs"),
|
|
89
|
+
categories: z.union([z.number(), z.array(z.number())]).optional().describe("Category ID or array of IDs (for posts)"),
|
|
90
|
+
tags: z.union([z.number(), z.array(z.number())]).optional().describe("Tag ID or array of IDs (for posts)"),
|
|
91
|
+
parent: z.number().optional().describe("Parent ID (for hierarchical content like pages)"),
|
|
92
|
+
orderby: z.string().optional().describe("Sort content by parameter"),
|
|
93
|
+
order: z.enum(['asc', 'desc']).optional().describe("Order sort attribute"),
|
|
94
|
+
after: z.string().optional().describe("ISO8601 date string to get content published after this date"),
|
|
95
|
+
before: z.string().optional().describe("ISO8601 date string to get content published before this date")
|
|
96
|
+
});
|
|
97
|
+
const getContentSchema = z.object({
|
|
98
|
+
content_type: z.string().describe("The content type slug"),
|
|
99
|
+
id: z.number().describe("Content ID")
|
|
100
|
+
});
|
|
101
|
+
const createContentSchema = z.object({
|
|
102
|
+
content_type: z.string().describe("The content type slug"),
|
|
103
|
+
title: z.string().describe("Content title"),
|
|
104
|
+
content: z.string().describe("Content body"),
|
|
105
|
+
status: z.string().optional().default('draft').describe("Content status"),
|
|
106
|
+
excerpt: z.string().optional().describe("Content excerpt"),
|
|
107
|
+
slug: z.string().optional().describe("Content slug"),
|
|
108
|
+
author: z.number().optional().describe("Author ID"),
|
|
109
|
+
parent: z.number().optional().describe("Parent ID (for hierarchical content)"),
|
|
110
|
+
categories: z.array(z.number()).optional().describe("Array of category IDs (for posts)"),
|
|
111
|
+
tags: z.array(z.number()).optional().describe("Array of tag IDs (for posts)"),
|
|
112
|
+
featured_media: z.number().optional().describe("Featured image ID"),
|
|
113
|
+
format: z.string().optional().describe("Content format"),
|
|
114
|
+
menu_order: z.number().optional().describe("Menu order (for pages)"),
|
|
115
|
+
meta: z.record(z.any()).optional().describe("Meta fields"),
|
|
116
|
+
custom_fields: z.record(z.any()).optional().describe("Custom fields specific to this content type")
|
|
117
|
+
});
|
|
118
|
+
const updateContentSchema = z.object({
|
|
119
|
+
content_type: z.string().describe("The content type slug"),
|
|
120
|
+
id: z.number().describe("Content ID"),
|
|
121
|
+
title: z.string().optional().describe("Content title"),
|
|
122
|
+
content: z.string().optional().describe("Content body"),
|
|
123
|
+
status: z.string().optional().describe("Content status"),
|
|
124
|
+
excerpt: z.string().optional().describe("Content excerpt"),
|
|
125
|
+
slug: z.string().optional().describe("Content slug"),
|
|
126
|
+
author: z.number().optional().describe("Author ID"),
|
|
127
|
+
parent: z.number().optional().describe("Parent ID"),
|
|
128
|
+
categories: z.array(z.number()).optional().describe("Array of category IDs"),
|
|
129
|
+
tags: z.array(z.number()).optional().describe("Array of tag IDs"),
|
|
130
|
+
featured_media: z.number().optional().describe("Featured image ID"),
|
|
131
|
+
format: z.string().optional().describe("Content format"),
|
|
132
|
+
menu_order: z.number().optional().describe("Menu order"),
|
|
133
|
+
meta: z.record(z.any()).optional().describe("Meta fields"),
|
|
134
|
+
custom_fields: z.record(z.any()).optional().describe("Custom fields")
|
|
135
|
+
});
|
|
136
|
+
const deleteContentSchema = z.object({
|
|
137
|
+
content_type: z.string().describe("The content type slug"),
|
|
138
|
+
id: z.number().describe("Content ID"),
|
|
139
|
+
force: z.boolean().optional().describe("Whether to bypass trash and force deletion")
|
|
140
|
+
});
|
|
141
|
+
const discoverContentTypesSchema = z.object({
|
|
142
|
+
refresh_cache: z.boolean().optional().describe("Force refresh the content types cache")
|
|
143
|
+
});
|
|
144
|
+
const findContentByUrlSchema = z.object({
|
|
145
|
+
url: z.string().describe("The full URL of the content to find"),
|
|
146
|
+
update_fields: z.object({
|
|
147
|
+
title: z.string().optional(),
|
|
148
|
+
content: z.string().optional(),
|
|
149
|
+
status: z.string().optional(),
|
|
150
|
+
meta: z.record(z.any()).optional(),
|
|
151
|
+
custom_fields: z.record(z.any()).optional()
|
|
152
|
+
}).optional().describe("Optional fields to update after finding the content")
|
|
153
|
+
});
|
|
154
|
+
const getContentBySlugSchema = z.object({
|
|
155
|
+
slug: z.string().describe("The slug to search for"),
|
|
156
|
+
content_types: z.array(z.string()).optional().describe("Content types to search in (defaults to all)")
|
|
157
|
+
});
|
|
158
|
+
export const unifiedContentTools = [
|
|
159
|
+
{
|
|
160
|
+
name: "list_content",
|
|
161
|
+
description: "Lists content of any type (posts, pages, or custom post types) with filtering and pagination",
|
|
162
|
+
inputSchema: { type: "object", properties: listContentSchema.shape }
|
|
163
|
+
},
|
|
164
|
+
{
|
|
165
|
+
name: "get_content",
|
|
166
|
+
description: "Gets specific content by ID and content type",
|
|
167
|
+
inputSchema: { type: "object", properties: getContentSchema.shape }
|
|
168
|
+
},
|
|
169
|
+
{
|
|
170
|
+
name: "create_content",
|
|
171
|
+
description: "Creates new content of any type",
|
|
172
|
+
inputSchema: { type: "object", properties: createContentSchema.shape }
|
|
173
|
+
},
|
|
174
|
+
{
|
|
175
|
+
name: "update_content",
|
|
176
|
+
description: "Updates existing content of any type",
|
|
177
|
+
inputSchema: { type: "object", properties: updateContentSchema.shape }
|
|
178
|
+
},
|
|
179
|
+
{
|
|
180
|
+
name: "delete_content",
|
|
181
|
+
description: "Deletes content of any type",
|
|
182
|
+
inputSchema: { type: "object", properties: deleteContentSchema.shape }
|
|
183
|
+
},
|
|
184
|
+
{
|
|
185
|
+
name: "discover_content_types",
|
|
186
|
+
description: "Discovers all available content types (built-in and custom) in the WordPress site",
|
|
187
|
+
inputSchema: { type: "object", properties: discoverContentTypesSchema.shape }
|
|
188
|
+
},
|
|
189
|
+
{
|
|
190
|
+
name: "find_content_by_url",
|
|
191
|
+
description: "Finds content by its URL, automatically detecting the content type, and optionally updates it",
|
|
192
|
+
inputSchema: { type: "object", properties: findContentByUrlSchema.shape }
|
|
193
|
+
},
|
|
194
|
+
{
|
|
195
|
+
name: "get_content_by_slug",
|
|
196
|
+
description: "Searches for content by slug across one or more content types",
|
|
197
|
+
inputSchema: { type: "object", properties: getContentBySlugSchema.shape }
|
|
198
|
+
}
|
|
199
|
+
];
|
|
200
|
+
export const unifiedContentHandlers = {
|
|
201
|
+
list_content: async (params) => {
|
|
202
|
+
try {
|
|
203
|
+
const endpoint = getContentEndpoint(params.content_type);
|
|
204
|
+
const { content_type, ...queryParams } = params;
|
|
205
|
+
const response = await makeWordPressRequest('GET', endpoint, queryParams);
|
|
206
|
+
return {
|
|
207
|
+
toolResult: {
|
|
208
|
+
content: [{
|
|
209
|
+
type: 'text',
|
|
210
|
+
text: JSON.stringify(response, null, 2)
|
|
211
|
+
}],
|
|
212
|
+
isError: false
|
|
213
|
+
}
|
|
214
|
+
};
|
|
215
|
+
}
|
|
216
|
+
catch (error) {
|
|
217
|
+
return {
|
|
218
|
+
toolResult: {
|
|
219
|
+
content: [{
|
|
220
|
+
type: 'text',
|
|
221
|
+
text: `Error listing content: ${error.message}`
|
|
222
|
+
}],
|
|
223
|
+
isError: true
|
|
224
|
+
}
|
|
225
|
+
};
|
|
226
|
+
}
|
|
227
|
+
},
|
|
228
|
+
get_content: async (params) => {
|
|
229
|
+
try {
|
|
230
|
+
const endpoint = getContentEndpoint(params.content_type);
|
|
231
|
+
const response = await makeWordPressRequest('GET', `${endpoint}/${params.id}`);
|
|
232
|
+
return {
|
|
233
|
+
toolResult: {
|
|
234
|
+
content: [{
|
|
235
|
+
type: 'text',
|
|
236
|
+
text: JSON.stringify(response, null, 2)
|
|
237
|
+
}],
|
|
238
|
+
isError: false
|
|
239
|
+
}
|
|
240
|
+
};
|
|
241
|
+
}
|
|
242
|
+
catch (error) {
|
|
243
|
+
return {
|
|
244
|
+
toolResult: {
|
|
245
|
+
content: [{
|
|
246
|
+
type: 'text',
|
|
247
|
+
text: `Error getting content: ${error.message}`
|
|
248
|
+
}],
|
|
249
|
+
isError: true
|
|
250
|
+
}
|
|
251
|
+
};
|
|
252
|
+
}
|
|
253
|
+
},
|
|
254
|
+
create_content: async (params) => {
|
|
255
|
+
try {
|
|
256
|
+
const endpoint = getContentEndpoint(params.content_type);
|
|
257
|
+
const contentData = {
|
|
258
|
+
title: params.title,
|
|
259
|
+
content: params.content,
|
|
260
|
+
status: params.status,
|
|
261
|
+
excerpt: params.excerpt,
|
|
262
|
+
slug: params.slug,
|
|
263
|
+
author: params.author,
|
|
264
|
+
parent: params.parent,
|
|
265
|
+
featured_media: params.featured_media,
|
|
266
|
+
format: params.format,
|
|
267
|
+
menu_order: params.menu_order
|
|
268
|
+
};
|
|
269
|
+
// Add post-specific fields
|
|
270
|
+
if (params.categories)
|
|
271
|
+
contentData.categories = params.categories;
|
|
272
|
+
if (params.tags)
|
|
273
|
+
contentData.tags = params.tags;
|
|
274
|
+
// Add meta fields
|
|
275
|
+
if (params.meta)
|
|
276
|
+
contentData.meta = params.meta;
|
|
277
|
+
// Add custom fields
|
|
278
|
+
if (params.custom_fields) {
|
|
279
|
+
Object.assign(contentData, params.custom_fields);
|
|
280
|
+
}
|
|
281
|
+
// Remove undefined values
|
|
282
|
+
Object.keys(contentData).forEach(key => {
|
|
283
|
+
if (contentData[key] === undefined) {
|
|
284
|
+
delete contentData[key];
|
|
285
|
+
}
|
|
286
|
+
});
|
|
287
|
+
const response = await makeWordPressRequest('POST', endpoint, contentData);
|
|
288
|
+
return {
|
|
289
|
+
toolResult: {
|
|
290
|
+
content: [{
|
|
291
|
+
type: 'text',
|
|
292
|
+
text: JSON.stringify(response, null, 2)
|
|
293
|
+
}],
|
|
294
|
+
isError: false
|
|
295
|
+
}
|
|
296
|
+
};
|
|
297
|
+
}
|
|
298
|
+
catch (error) {
|
|
299
|
+
return {
|
|
300
|
+
toolResult: {
|
|
301
|
+
content: [{
|
|
302
|
+
type: 'text',
|
|
303
|
+
text: `Error creating content: ${error.message}`
|
|
304
|
+
}],
|
|
305
|
+
isError: true
|
|
306
|
+
}
|
|
307
|
+
};
|
|
308
|
+
}
|
|
309
|
+
},
|
|
310
|
+
update_content: async (params) => {
|
|
311
|
+
try {
|
|
312
|
+
const endpoint = getContentEndpoint(params.content_type);
|
|
313
|
+
const updateData = {};
|
|
314
|
+
// Only include defined fields
|
|
315
|
+
if (params.title !== undefined)
|
|
316
|
+
updateData.title = params.title;
|
|
317
|
+
if (params.content !== undefined)
|
|
318
|
+
updateData.content = params.content;
|
|
319
|
+
if (params.status !== undefined)
|
|
320
|
+
updateData.status = params.status;
|
|
321
|
+
if (params.excerpt !== undefined)
|
|
322
|
+
updateData.excerpt = params.excerpt;
|
|
323
|
+
if (params.slug !== undefined)
|
|
324
|
+
updateData.slug = params.slug;
|
|
325
|
+
if (params.author !== undefined)
|
|
326
|
+
updateData.author = params.author;
|
|
327
|
+
if (params.parent !== undefined)
|
|
328
|
+
updateData.parent = params.parent;
|
|
329
|
+
if (params.featured_media !== undefined)
|
|
330
|
+
updateData.featured_media = params.featured_media;
|
|
331
|
+
if (params.format !== undefined)
|
|
332
|
+
updateData.format = params.format;
|
|
333
|
+
if (params.menu_order !== undefined)
|
|
334
|
+
updateData.menu_order = params.menu_order;
|
|
335
|
+
if (params.categories !== undefined)
|
|
336
|
+
updateData.categories = params.categories;
|
|
337
|
+
if (params.tags !== undefined)
|
|
338
|
+
updateData.tags = params.tags;
|
|
339
|
+
if (params.meta !== undefined)
|
|
340
|
+
updateData.meta = params.meta;
|
|
341
|
+
// Add custom fields
|
|
342
|
+
if (params.custom_fields) {
|
|
343
|
+
Object.assign(updateData, params.custom_fields);
|
|
344
|
+
}
|
|
345
|
+
const response = await makeWordPressRequest('POST', `${endpoint}/${params.id}`, updateData);
|
|
346
|
+
return {
|
|
347
|
+
toolResult: {
|
|
348
|
+
content: [{
|
|
349
|
+
type: 'text',
|
|
350
|
+
text: JSON.stringify(response, null, 2)
|
|
351
|
+
}],
|
|
352
|
+
isError: false
|
|
353
|
+
}
|
|
354
|
+
};
|
|
355
|
+
}
|
|
356
|
+
catch (error) {
|
|
357
|
+
return {
|
|
358
|
+
toolResult: {
|
|
359
|
+
content: [{
|
|
360
|
+
type: 'text',
|
|
361
|
+
text: `Error updating content: ${error.message}`
|
|
362
|
+
}],
|
|
363
|
+
isError: true
|
|
364
|
+
}
|
|
365
|
+
};
|
|
366
|
+
}
|
|
367
|
+
},
|
|
368
|
+
delete_content: async (params) => {
|
|
369
|
+
try {
|
|
370
|
+
const endpoint = getContentEndpoint(params.content_type);
|
|
371
|
+
const response = await makeWordPressRequest('DELETE', `${endpoint}/${params.id}`, {
|
|
372
|
+
force: params.force || false
|
|
373
|
+
});
|
|
374
|
+
return {
|
|
375
|
+
toolResult: {
|
|
376
|
+
content: [{
|
|
377
|
+
type: 'text',
|
|
378
|
+
text: JSON.stringify(response, null, 2)
|
|
379
|
+
}],
|
|
380
|
+
isError: false
|
|
381
|
+
}
|
|
382
|
+
};
|
|
383
|
+
}
|
|
384
|
+
catch (error) {
|
|
385
|
+
return {
|
|
386
|
+
toolResult: {
|
|
387
|
+
content: [{
|
|
388
|
+
type: 'text',
|
|
389
|
+
text: `Error deleting content: ${error.message}`
|
|
390
|
+
}],
|
|
391
|
+
isError: true
|
|
392
|
+
}
|
|
393
|
+
};
|
|
394
|
+
}
|
|
395
|
+
},
|
|
396
|
+
discover_content_types: async (params) => {
|
|
397
|
+
try {
|
|
398
|
+
const contentTypes = await getPostTypes(params.refresh_cache || false);
|
|
399
|
+
// Format the response to be more readable
|
|
400
|
+
const formattedTypes = Object.entries(contentTypes).map(([slug, type]) => ({
|
|
401
|
+
slug,
|
|
402
|
+
name: type.name,
|
|
403
|
+
description: type.description,
|
|
404
|
+
rest_base: type.rest_base,
|
|
405
|
+
hierarchical: type.hierarchical,
|
|
406
|
+
supports: type.supports,
|
|
407
|
+
taxonomies: type.taxonomies
|
|
408
|
+
}));
|
|
409
|
+
return {
|
|
410
|
+
toolResult: {
|
|
411
|
+
content: [{
|
|
412
|
+
type: 'text',
|
|
413
|
+
text: JSON.stringify(formattedTypes, null, 2)
|
|
414
|
+
}],
|
|
415
|
+
isError: false
|
|
416
|
+
}
|
|
417
|
+
};
|
|
418
|
+
}
|
|
419
|
+
catch (error) {
|
|
420
|
+
return {
|
|
421
|
+
toolResult: {
|
|
422
|
+
content: [{
|
|
423
|
+
type: 'text',
|
|
424
|
+
text: `Error discovering content types: ${error.message}`
|
|
425
|
+
}],
|
|
426
|
+
isError: true
|
|
427
|
+
}
|
|
428
|
+
};
|
|
429
|
+
}
|
|
430
|
+
},
|
|
431
|
+
find_content_by_url: async (params) => {
|
|
432
|
+
try {
|
|
433
|
+
const { slug, pathHints } = parseUrl(params.url);
|
|
434
|
+
if (!slug) {
|
|
435
|
+
throw new Error('Could not extract slug from URL');
|
|
436
|
+
}
|
|
437
|
+
logToFile(`Searching for content with slug: ${slug}, path hints: ${pathHints.join('/')}`);
|
|
438
|
+
// Try to guess content types based on URL structure
|
|
439
|
+
const priorityTypes = [];
|
|
440
|
+
// Common URL patterns to content type mappings
|
|
441
|
+
const pathMappings = {
|
|
442
|
+
'documentation': ['documentation', 'docs', 'doc'],
|
|
443
|
+
'docs': ['documentation', 'docs', 'doc'],
|
|
444
|
+
'products': ['product'],
|
|
445
|
+
'portfolio': ['portfolio', 'project'],
|
|
446
|
+
'services': ['service'],
|
|
447
|
+
'testimonials': ['testimonial'],
|
|
448
|
+
'team': ['team_member', 'staff'],
|
|
449
|
+
'events': ['event'],
|
|
450
|
+
'courses': ['course', 'lesson']
|
|
451
|
+
};
|
|
452
|
+
// Check path hints for potential content types
|
|
453
|
+
for (const hint of pathHints) {
|
|
454
|
+
const mappedTypes = pathMappings[hint.toLowerCase()];
|
|
455
|
+
if (mappedTypes) {
|
|
456
|
+
priorityTypes.push(...mappedTypes);
|
|
457
|
+
}
|
|
458
|
+
}
|
|
459
|
+
// Always check standard content types as fallback
|
|
460
|
+
priorityTypes.push('post', 'page');
|
|
461
|
+
// Remove duplicates
|
|
462
|
+
const typesToSearch = [...new Set(priorityTypes)];
|
|
463
|
+
// Find the content
|
|
464
|
+
const result = await findContentAcrossTypes(slug, typesToSearch);
|
|
465
|
+
if (!result) {
|
|
466
|
+
// If not found in priority types, search all types
|
|
467
|
+
const allResult = await findContentAcrossTypes(slug);
|
|
468
|
+
if (!allResult) {
|
|
469
|
+
throw new Error(`No content found with URL: ${params.url}`);
|
|
470
|
+
}
|
|
471
|
+
const { content, contentType } = allResult;
|
|
472
|
+
// Update if requested
|
|
473
|
+
if (params.update_fields) {
|
|
474
|
+
const endpoint = getContentEndpoint(contentType);
|
|
475
|
+
const updateData = {};
|
|
476
|
+
if (params.update_fields.title !== undefined)
|
|
477
|
+
updateData.title = params.update_fields.title;
|
|
478
|
+
if (params.update_fields.content !== undefined)
|
|
479
|
+
updateData.content = params.update_fields.content;
|
|
480
|
+
if (params.update_fields.status !== undefined)
|
|
481
|
+
updateData.status = params.update_fields.status;
|
|
482
|
+
if (params.update_fields.meta !== undefined)
|
|
483
|
+
updateData.meta = params.update_fields.meta;
|
|
484
|
+
if (params.update_fields.custom_fields !== undefined) {
|
|
485
|
+
Object.assign(updateData, params.update_fields.custom_fields);
|
|
486
|
+
}
|
|
487
|
+
const updatedContent = await makeWordPressRequest('POST', `${endpoint}/${content.id}`, updateData);
|
|
488
|
+
return {
|
|
489
|
+
toolResult: {
|
|
490
|
+
content: [{
|
|
491
|
+
type: 'text',
|
|
492
|
+
text: JSON.stringify({
|
|
493
|
+
found: true,
|
|
494
|
+
content_type: contentType,
|
|
495
|
+
content_id: content.id,
|
|
496
|
+
original_url: params.url,
|
|
497
|
+
updated: true,
|
|
498
|
+
content: updatedContent
|
|
499
|
+
}, null, 2)
|
|
500
|
+
}],
|
|
501
|
+
isError: false
|
|
502
|
+
}
|
|
503
|
+
};
|
|
504
|
+
}
|
|
505
|
+
return {
|
|
506
|
+
toolResult: {
|
|
507
|
+
content: [{
|
|
508
|
+
type: 'text',
|
|
509
|
+
text: JSON.stringify({
|
|
510
|
+
found: true,
|
|
511
|
+
content_type: contentType,
|
|
512
|
+
content_id: content.id,
|
|
513
|
+
original_url: params.url,
|
|
514
|
+
content: content
|
|
515
|
+
}, null, 2)
|
|
516
|
+
}],
|
|
517
|
+
isError: false
|
|
518
|
+
}
|
|
519
|
+
};
|
|
520
|
+
}
|
|
521
|
+
const { content, contentType } = result;
|
|
522
|
+
// Update if requested
|
|
523
|
+
if (params.update_fields) {
|
|
524
|
+
const endpoint = getContentEndpoint(contentType);
|
|
525
|
+
const updateData = {};
|
|
526
|
+
if (params.update_fields.title !== undefined)
|
|
527
|
+
updateData.title = params.update_fields.title;
|
|
528
|
+
if (params.update_fields.content !== undefined)
|
|
529
|
+
updateData.content = params.update_fields.content;
|
|
530
|
+
if (params.update_fields.status !== undefined)
|
|
531
|
+
updateData.status = params.update_fields.status;
|
|
532
|
+
if (params.update_fields.meta !== undefined)
|
|
533
|
+
updateData.meta = params.update_fields.meta;
|
|
534
|
+
if (params.update_fields.custom_fields !== undefined) {
|
|
535
|
+
Object.assign(updateData, params.update_fields.custom_fields);
|
|
536
|
+
}
|
|
537
|
+
const updatedContent = await makeWordPressRequest('POST', `${endpoint}/${content.id}`, updateData);
|
|
538
|
+
return {
|
|
539
|
+
toolResult: {
|
|
540
|
+
content: [{
|
|
541
|
+
type: 'text',
|
|
542
|
+
text: JSON.stringify({
|
|
543
|
+
found: true,
|
|
544
|
+
content_type: contentType,
|
|
545
|
+
content_id: content.id,
|
|
546
|
+
original_url: params.url,
|
|
547
|
+
updated: true,
|
|
548
|
+
content: updatedContent
|
|
549
|
+
}, null, 2)
|
|
550
|
+
}],
|
|
551
|
+
isError: false
|
|
552
|
+
}
|
|
553
|
+
};
|
|
554
|
+
}
|
|
555
|
+
return {
|
|
556
|
+
toolResult: {
|
|
557
|
+
content: [{
|
|
558
|
+
type: 'text',
|
|
559
|
+
text: JSON.stringify({
|
|
560
|
+
found: true,
|
|
561
|
+
content_type: contentType,
|
|
562
|
+
content_id: content.id,
|
|
563
|
+
original_url: params.url,
|
|
564
|
+
content: content
|
|
565
|
+
}, null, 2)
|
|
566
|
+
}],
|
|
567
|
+
isError: false
|
|
568
|
+
}
|
|
569
|
+
};
|
|
570
|
+
}
|
|
571
|
+
catch (error) {
|
|
572
|
+
return {
|
|
573
|
+
toolResult: {
|
|
574
|
+
content: [{
|
|
575
|
+
type: 'text',
|
|
576
|
+
text: `Error finding content by URL: ${error.message}`
|
|
577
|
+
}],
|
|
578
|
+
isError: true
|
|
579
|
+
}
|
|
580
|
+
};
|
|
581
|
+
}
|
|
582
|
+
},
|
|
583
|
+
get_content_by_slug: async (params) => {
|
|
584
|
+
try {
|
|
585
|
+
const result = await findContentAcrossTypes(params.slug, params.content_types);
|
|
586
|
+
if (!result) {
|
|
587
|
+
throw new Error(`No content found with slug: ${params.slug}`);
|
|
588
|
+
}
|
|
589
|
+
return {
|
|
590
|
+
toolResult: {
|
|
591
|
+
content: [{
|
|
592
|
+
type: 'text',
|
|
593
|
+
text: JSON.stringify({
|
|
594
|
+
found: true,
|
|
595
|
+
content_type: result.contentType,
|
|
596
|
+
content: result.content
|
|
597
|
+
}, null, 2)
|
|
598
|
+
}],
|
|
599
|
+
isError: false
|
|
600
|
+
}
|
|
601
|
+
};
|
|
602
|
+
}
|
|
603
|
+
catch (error) {
|
|
604
|
+
return {
|
|
605
|
+
toolResult: {
|
|
606
|
+
content: [{
|
|
607
|
+
type: 'text',
|
|
608
|
+
text: `Error getting content by slug: ${error.message}`
|
|
609
|
+
}],
|
|
610
|
+
isError: true
|
|
611
|
+
}
|
|
612
|
+
};
|
|
613
|
+
}
|
|
614
|
+
}
|
|
615
|
+
};
|