@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.
Files changed (49) hide show
  1. package/LICENSE +22 -0
  2. package/README.md +220 -0
  3. package/build/cli.d.ts +2 -0
  4. package/build/cli.js +52 -0
  5. package/build/server.d.ts +2 -0
  6. package/build/server.js +97 -0
  7. package/build/tools/comments.d.ts +212 -0
  8. package/build/tools/comments.js +181 -0
  9. package/build/tools/fluent-affiliate.d.ts +2 -0
  10. package/build/tools/fluent-affiliate.js +3 -0
  11. package/build/tools/fluent-cart.d.ts +706 -0
  12. package/build/tools/fluent-cart.js +642 -0
  13. package/build/tools/fluent-community-BACKUP.d.ts +364 -0
  14. package/build/tools/fluent-community-BACKUP.js +883 -0
  15. package/build/tools/fluent-community-MINIMAL.d.ts +69 -0
  16. package/build/tools/fluent-community-MINIMAL.js +92 -0
  17. package/build/tools/fluent-community-design.d.ts +3 -0
  18. package/build/tools/fluent-community-design.js +150 -0
  19. package/build/tools/fluent-community-layout.d.ts +119 -0
  20. package/build/tools/fluent-community-layout.js +88 -0
  21. package/build/tools/fluent-community.d.ts +364 -0
  22. package/build/tools/fluent-community.js +528 -0
  23. package/build/tools/fluent-crm.d.ts +3 -0
  24. package/build/tools/fluent-crm.js +392 -0
  25. package/build/tools/index.d.ts +2205 -0
  26. package/build/tools/index.js +54 -0
  27. package/build/tools/media.d.ts +135 -0
  28. package/build/tools/media.js +168 -0
  29. package/build/tools/ml-canvas.d.ts +91 -0
  30. package/build/tools/ml-canvas.js +109 -0
  31. package/build/tools/ml-image-editor.d.ts +230 -0
  32. package/build/tools/ml-image-editor.js +270 -0
  33. package/build/tools/ml-media-hub.d.ts +575 -0
  34. package/build/tools/ml-media-hub.js +714 -0
  35. package/build/tools/plugin-repository.d.ts +62 -0
  36. package/build/tools/plugin-repository.js +149 -0
  37. package/build/tools/plugins.d.ts +129 -0
  38. package/build/tools/plugins.js +148 -0
  39. package/build/tools/unified-content.d.ts +313 -0
  40. package/build/tools/unified-content.js +615 -0
  41. package/build/tools/unified-taxonomies.d.ts +229 -0
  42. package/build/tools/unified-taxonomies.js +479 -0
  43. package/build/tools/users.d.ts +227 -0
  44. package/build/tools/users.js +182 -0
  45. package/build/types/wordpress-types.d.ts +151 -0
  46. package/build/types/wordpress-types.js +2 -0
  47. package/build/wordpress.d.ts +26 -0
  48. package/build/wordpress.js +223 -0
  49. 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
+ };