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