mcp-wordpress 1.5.2 → 2.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/README.md +332 -61
- package/dist/cache/CacheInvalidation.d.ts.map +1 -1
- package/dist/cache/CacheInvalidation.js +4 -4
- package/dist/cache/CacheInvalidation.js.map +1 -1
- package/dist/client/MockWordPressClient.d.ts +55 -0
- package/dist/client/MockWordPressClient.d.ts.map +1 -0
- package/dist/client/MockWordPressClient.js +369 -0
- package/dist/client/MockWordPressClient.js.map +1 -0
- package/dist/client/api.d.ts +1 -0
- package/dist/client/api.d.ts.map +1 -1
- package/dist/client/api.js +26 -60
- package/dist/client/api.js.map +1 -1
- package/dist/client/managers/AuthenticationManager.d.ts.map +1 -1
- package/dist/client/managers/AuthenticationManager.js +4 -3
- package/dist/client/managers/AuthenticationManager.js.map +1 -1
- package/dist/config/ConfigurationSchema.d.ts +3 -3
- package/dist/config/ConfigurationSchema.d.ts.map +1 -1
- package/dist/config/ConfigurationSchema.js +7 -24
- package/dist/config/ConfigurationSchema.js.map +1 -1
- package/dist/config/ServerConfiguration.d.ts +8 -0
- package/dist/config/ServerConfiguration.d.ts.map +1 -1
- package/dist/config/ServerConfiguration.js +80 -31
- package/dist/config/ServerConfiguration.js.map +1 -1
- package/dist/docs/DocumentationGenerator.d.ts.map +1 -1
- package/dist/docs/DocumentationGenerator.js +5 -7
- package/dist/docs/DocumentationGenerator.js.map +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +33 -29
- package/dist/index.js.map +1 -1
- package/dist/security/InputValidator.d.ts.map +1 -1
- package/dist/security/InputValidator.js +3 -11
- package/dist/security/InputValidator.js.map +1 -1
- package/dist/server/ToolRegistry.d.ts +4 -0
- package/dist/server/ToolRegistry.d.ts.map +1 -1
- package/dist/server/ToolRegistry.js +71 -8
- package/dist/server/ToolRegistry.js.map +1 -1
- package/dist/tools/auth.d.ts.map +1 -1
- package/dist/tools/auth.js +8 -3
- package/dist/tools/auth.js.map +1 -1
- package/dist/tools/posts.d.ts.map +1 -1
- package/dist/tools/posts.js +287 -20
- package/dist/tools/posts.js.map +1 -1
- package/dist/tools/site.d.ts.map +1 -1
- package/dist/tools/site.js +47 -9
- package/dist/tools/site.js.map +1 -1
- package/dist/tools/users.d.ts.map +1 -1
- package/dist/tools/users.js +113 -10
- package/dist/tools/users.js.map +1 -1
- package/dist/utils/enhancedError.d.ts +61 -0
- package/dist/utils/enhancedError.d.ts.map +1 -0
- package/dist/utils/enhancedError.js +221 -0
- package/dist/utils/enhancedError.js.map +1 -0
- package/dist/utils/streaming.d.ts +104 -0
- package/dist/utils/streaming.d.ts.map +1 -0
- package/dist/utils/streaming.js +312 -0
- package/dist/utils/streaming.js.map +1 -0
- package/dist/utils/validation.d.ts +19 -3
- package/dist/utils/validation.d.ts.map +1 -1
- package/dist/utils/validation.js +174 -24
- package/dist/utils/validation.js.map +1 -1
- package/docs/ARCHITECTURE.md +850 -0
- package/docs/CACHING.md +20 -17
- package/docs/CONFIGURATION.md +660 -0
- package/docs/DOCKER.md +61 -60
- package/docs/EVALUATION.md +397 -0
- package/docs/INSTALLATION.md +423 -0
- package/docs/PERFORMANCE_MONITORING.md +17 -15
- package/docs/SECURITY.md +621 -0
- package/docs/SECURITY_TESTING.md +22 -26
- package/docs/TEST_SITE_SETUP.md +136 -0
- package/docs/TROUBLESHOOTING.md +578 -0
- package/docs/api/README.md +76 -91
- package/docs/api/categories/auth.md +0 -2
- package/docs/api/categories/cache.md +0 -2
- package/docs/api/categories/comment.md +0 -2
- package/docs/api/categories/media.md +0 -2
- package/docs/api/categories/page.md +0 -2
- package/docs/api/categories/performance.md +0 -2
- package/docs/api/categories/post.md +0 -2
- package/docs/api/categories/site.md +0 -2
- package/docs/api/categories/taxonomy.md +0 -2
- package/docs/api/categories/user.md +0 -2
- package/docs/api/summary.json +1 -1
- package/docs/api/tools/wp_approve_comment.md +11 -3
- package/docs/api/tools/wp_cache_clear.md +14 -5
- package/docs/api/tools/wp_cache_info.md +14 -5
- package/docs/api/tools/wp_cache_stats.md +14 -5
- package/docs/api/tools/wp_cache_warm.md +14 -5
- package/docs/api/tools/wp_create_application_password.md +11 -3
- package/docs/api/tools/wp_create_category.md +11 -3
- package/docs/api/tools/wp_create_comment.md +14 -5
- package/docs/api/tools/wp_create_page.md +13 -5
- package/docs/api/tools/wp_create_post.md +14 -7
- package/docs/api/tools/wp_create_tag.md +11 -3
- package/docs/api/tools/wp_create_user.md +13 -5
- package/docs/api/tools/wp_delete_application_password.md +11 -3
- package/docs/api/tools/wp_delete_category.md +11 -3
- package/docs/api/tools/wp_delete_comment.md +11 -3
- package/docs/api/tools/wp_delete_media.md +10 -3
- package/docs/api/tools/wp_delete_page.md +10 -3
- package/docs/api/tools/wp_delete_post.md +11 -5
- package/docs/api/tools/wp_delete_tag.md +11 -3
- package/docs/api/tools/wp_delete_user.md +10 -3
- package/docs/api/tools/wp_get_application_passwords.md +11 -3
- package/docs/api/tools/wp_get_auth_status.md +11 -3
- package/docs/api/tools/wp_get_category.md +11 -3
- package/docs/api/tools/wp_get_comment.md +11 -3
- package/docs/api/tools/wp_get_current_user.md +11 -3
- package/docs/api/tools/wp_get_media.md +11 -3
- package/docs/api/tools/wp_get_page.md +11 -3
- package/docs/api/tools/wp_get_page_revisions.md +11 -3
- package/docs/api/tools/wp_get_post.md +12 -5
- package/docs/api/tools/wp_get_post_revisions.md +11 -3
- package/docs/api/tools/wp_get_site_settings.md +10 -3
- package/docs/api/tools/wp_get_tag.md +11 -3
- package/docs/api/tools/wp_get_user.md +11 -3
- package/docs/api/tools/wp_list_categories.md +11 -3
- package/docs/api/tools/wp_list_comments.md +11 -3
- package/docs/api/tools/wp_list_media.md +14 -5
- package/docs/api/tools/wp_list_pages.md +14 -5
- package/docs/api/tools/wp_list_posts.md +15 -7
- package/docs/api/tools/wp_list_tags.md +11 -3
- package/docs/api/tools/wp_list_users.md +11 -3
- package/docs/api/tools/wp_performance_alerts.md +17 -7
- package/docs/api/tools/wp_performance_benchmark.md +17 -7
- package/docs/api/tools/wp_performance_export.md +17 -7
- package/docs/api/tools/wp_performance_history.md +17 -7
- package/docs/api/tools/wp_performance_optimize.md +17 -7
- package/docs/api/tools/wp_performance_stats.md +17 -7
- package/docs/api/tools/wp_search_site.md +11 -3
- package/docs/api/tools/wp_spam_comment.md +11 -3
- package/docs/api/tools/wp_switch_auth_method.md +14 -5
- package/docs/api/tools/wp_test_auth.md +11 -3
- package/docs/api/tools/wp_update_category.md +11 -3
- package/docs/api/tools/wp_update_comment.md +14 -5
- package/docs/api/tools/wp_update_media.md +14 -5
- package/docs/api/tools/wp_update_page.md +13 -5
- package/docs/api/tools/wp_update_post.md +14 -7
- package/docs/api/tools/wp_update_site_settings.md +14 -5
- package/docs/api/tools/wp_update_tag.md +11 -3
- package/docs/api/tools/wp_update_user.md +13 -5
- package/docs/api/tools/wp_upload_media.md +13 -5
- package/docs/api/types/WordPressPost.md +2 -0
- package/docs/code-improvements.md +40 -0
- package/docs/contract-testing.md +1 -1
- package/docs/developer/API_REFERENCE.md +19 -59
- package/docs/developer/ARCHITECTURE.md +8 -11
- package/docs/developer/BUILD_SYSTEM.md +2 -2
- package/docs/developer/CONTRIBUTING.md +3 -5
- package/docs/developer/GITHUB_ACTIONS_SETUP.md +2 -2
- package/docs/developer/MIGRATION_GUIDE.md +5 -6
- package/docs/developer/README.md +2 -1
- package/docs/developer/REFACTORING.md +9 -15
- package/docs/developer/RELEASE_PROCESS.md +4 -3
- package/docs/developer/TESTING.md +2 -2
- package/docs/examples/claude-desktop-config.md +8 -0
- package/docs/integrations/claude-desktop.md +426 -0
- package/docs/integrations/cline.md +537 -0
- package/docs/integrations/vs-code.md +515 -0
- package/docs/releases/COMMUNITY_ANNOUNCEMENT_v1.1.2.md +30 -23
- package/docs/releases/RELEASE_NOTES_v1.1.2.md +7 -6
- package/docs/testing-configurations.md +11 -0
- package/docs/user-guides/DOCKER_NPM_DTX_SETUP.md +3 -2
- package/docs/user-guides/DOCKER_SETUP.md +3 -2
- package/docs/user-guides/DTX_SETUP.md +6 -5
- package/docs/user-guides/DXT_INSTALLATION.md +4 -4
- package/docs/user-guides/NPM_SETUP.md +4 -2
- package/docs/user-guides/NPX_SETUP.md +4 -2
- package/docs/user-guides/SMITHERY_SETUP.md +402 -0
- package/docs/wordpress-rest-api-authentication-troubleshooting.md +45 -42
- package/package.json +12 -2
- package/src/cache/CacheInvalidation.ts +7 -18
- package/src/client/MockWordPressClient.ts +398 -0
- package/src/client/api.ts +77 -237
- package/src/client/managers/AuthenticationManager.ts +19 -56
- package/src/config/ConfigurationSchema.ts +14 -45
- package/src/config/ServerConfiguration.ts +98 -71
- package/src/docs/DocumentationGenerator.ts +39 -123
- package/src/dxt-entry.cjs +4 -1
- package/src/index.ts +35 -54
- package/src/security/InputValidator.ts +15 -57
- package/src/server/ToolRegistry.ts +88 -17
- package/src/tools/auth.ts +15 -22
- package/src/tools/posts.ts +347 -64
- package/src/tools/site.ts +69 -46
- package/src/tools/users.ts +142 -44
- package/src/utils/enhancedError.ts +248 -0
- package/src/utils/streaming.ts +428 -0
- package/src/utils/validation.ts +253 -92
- package/dist/mcp-wordpress-1.5.2.tgz +0 -0
package/src/tools/posts.ts
CHANGED
|
@@ -1,10 +1,9 @@
|
|
|
1
1
|
import { WordPressClient } from "../client/api.js";
|
|
2
|
-
import {
|
|
3
|
-
CreatePostRequest,
|
|
4
|
-
PostQueryParams,
|
|
5
|
-
UpdatePostRequest,
|
|
6
|
-
} from "../types/wordpress.js";
|
|
2
|
+
import { CreatePostRequest, PostQueryParams, UpdatePostRequest } from "../types/wordpress.js";
|
|
7
3
|
import { getErrorMessage } from "../utils/error.js";
|
|
4
|
+
import { ErrorHandlers, EnhancedError } from "../utils/enhancedError.js";
|
|
5
|
+
import { validateId, validatePaginationParams, validatePostParams } from "../utils/validation.js";
|
|
6
|
+
import { WordPressDataStreamer, StreamingUtils, StreamingResult } from "../utils/streaming.js";
|
|
8
7
|
|
|
9
8
|
/**
|
|
10
9
|
* Provides tools for managing posts on a WordPress site.
|
|
@@ -19,7 +18,15 @@ export class PostTools {
|
|
|
19
18
|
return [
|
|
20
19
|
{
|
|
21
20
|
name: "wp_list_posts",
|
|
22
|
-
description:
|
|
21
|
+
description:
|
|
22
|
+
"Lists posts from a WordPress site with comprehensive filtering options. Supports search, status filtering, and category/tag filtering with enhanced metadata display.\n\n" +
|
|
23
|
+
"**Usage Examples:**\n" +
|
|
24
|
+
"• Basic listing: `wp_list_posts`\n" +
|
|
25
|
+
'• Search posts: `wp_list_posts --search="AI trends"`\n' +
|
|
26
|
+
'• Filter by status: `wp_list_posts --status="draft"`\n' +
|
|
27
|
+
"• Category filtering: `wp_list_posts --categories=[1,2,3]`\n" +
|
|
28
|
+
"• Paginated results: `wp_list_posts --per_page=20 --page=2`\n" +
|
|
29
|
+
'• Combined filters: `wp_list_posts --search="WordPress" --status="publish" --per_page=10`',
|
|
23
30
|
parameters: [
|
|
24
31
|
{
|
|
25
32
|
name: "per_page",
|
|
@@ -54,7 +61,8 @@ export class PostTools {
|
|
|
54
61
|
},
|
|
55
62
|
{
|
|
56
63
|
name: "wp_get_post",
|
|
57
|
-
description:
|
|
64
|
+
description:
|
|
65
|
+
"Retrieves detailed information about a single post including metadata, content statistics, and management links.",
|
|
58
66
|
parameters: [
|
|
59
67
|
{
|
|
60
68
|
name: "id",
|
|
@@ -67,7 +75,14 @@ export class PostTools {
|
|
|
67
75
|
},
|
|
68
76
|
{
|
|
69
77
|
name: "wp_create_post",
|
|
70
|
-
description:
|
|
78
|
+
description:
|
|
79
|
+
"Creates a new WordPress post with comprehensive validation and detailed success feedback including management links.\n\n" +
|
|
80
|
+
"**Usage Examples:**\n" +
|
|
81
|
+
'• Simple post: `wp_create_post --title="My New Post" --content="<p>Hello World!</p>"`\n' +
|
|
82
|
+
'• Draft post: `wp_create_post --title="Draft Post" --status="draft"`\n' +
|
|
83
|
+
'• Categorized post: `wp_create_post --title="Tech News" --categories=[1,5] --tags=[10,20]`\n' +
|
|
84
|
+
'• Scheduled post: `wp_create_post --title="Future Post" --status="future" --date="2024-12-25T10:00:00"`\n' +
|
|
85
|
+
'• Complete post: `wp_create_post --title="Complete Post" --content="<p>Content</p>" --excerpt="Summary" --status="publish"`',
|
|
71
86
|
parameters: [
|
|
72
87
|
{
|
|
73
88
|
name: "title",
|
|
@@ -108,7 +123,7 @@ export class PostTools {
|
|
|
108
123
|
},
|
|
109
124
|
{
|
|
110
125
|
name: "wp_update_post",
|
|
111
|
-
description: "Updates an existing post.",
|
|
126
|
+
description: "Updates an existing WordPress post with validation and detailed confirmation.",
|
|
112
127
|
parameters: [
|
|
113
128
|
{
|
|
114
129
|
name: "id",
|
|
@@ -137,7 +152,7 @@ export class PostTools {
|
|
|
137
152
|
},
|
|
138
153
|
{
|
|
139
154
|
name: "wp_delete_post",
|
|
140
|
-
description: "Deletes a post.",
|
|
155
|
+
description: "Deletes a WordPress post with option for permanent deletion or moving to trash.",
|
|
141
156
|
parameters: [
|
|
142
157
|
{
|
|
143
158
|
name: "id",
|
|
@@ -148,15 +163,14 @@ export class PostTools {
|
|
|
148
163
|
{
|
|
149
164
|
name: "force",
|
|
150
165
|
type: "boolean",
|
|
151
|
-
description:
|
|
152
|
-
"If true, permanently delete. If false, move to trash. Defaults to false.",
|
|
166
|
+
description: "If true, permanently delete. If false, move to trash. Defaults to false.",
|
|
153
167
|
},
|
|
154
168
|
],
|
|
155
169
|
handler: this.handleDeletePost.bind(this),
|
|
156
170
|
},
|
|
157
171
|
{
|
|
158
172
|
name: "wp_get_post_revisions",
|
|
159
|
-
description: "Retrieves
|
|
173
|
+
description: "Retrieves the revision history for a specific post showing author and modification dates.",
|
|
160
174
|
parameters: [
|
|
161
175
|
{
|
|
162
176
|
name: "id",
|
|
@@ -170,63 +184,345 @@ export class PostTools {
|
|
|
170
184
|
];
|
|
171
185
|
}
|
|
172
186
|
|
|
173
|
-
public async handleListPosts(
|
|
174
|
-
client: WordPressClient,
|
|
175
|
-
params: PostQueryParams,
|
|
176
|
-
): Promise<any> {
|
|
187
|
+
public async handleListPosts(client: WordPressClient, params: PostQueryParams): Promise<any> {
|
|
177
188
|
try {
|
|
178
|
-
|
|
189
|
+
// Enhanced input validation and sanitization
|
|
190
|
+
const paginationValidated = validatePaginationParams({
|
|
191
|
+
page: params.page,
|
|
192
|
+
per_page: params.per_page,
|
|
193
|
+
offset: params.offset,
|
|
194
|
+
});
|
|
195
|
+
|
|
196
|
+
const sanitizedParams = {
|
|
197
|
+
...params,
|
|
198
|
+
...paginationValidated,
|
|
199
|
+
};
|
|
200
|
+
|
|
201
|
+
// Validate and sanitize search term
|
|
202
|
+
if (sanitizedParams.search) {
|
|
203
|
+
sanitizedParams.search = sanitizedParams.search.trim();
|
|
204
|
+
if (sanitizedParams.search.length === 0) {
|
|
205
|
+
delete sanitizedParams.search;
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
// Validate category and tag IDs if provided
|
|
210
|
+
if (sanitizedParams.categories) {
|
|
211
|
+
sanitizedParams.categories = sanitizedParams.categories.map((id) => validateId(id, "category ID"));
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
if (sanitizedParams.tags) {
|
|
215
|
+
sanitizedParams.tags = sanitizedParams.tags.map((id) => validateId(id, "tag ID"));
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
// Validate status parameter
|
|
219
|
+
if (sanitizedParams.status) {
|
|
220
|
+
const validStatuses = ["publish", "future", "draft", "pending", "private"];
|
|
221
|
+
const statusesToCheck = Array.isArray(sanitizedParams.status)
|
|
222
|
+
? sanitizedParams.status
|
|
223
|
+
: [sanitizedParams.status];
|
|
224
|
+
|
|
225
|
+
for (const statusToCheck of statusesToCheck) {
|
|
226
|
+
if (!validStatuses.includes(statusToCheck)) {
|
|
227
|
+
throw ErrorHandlers.validationError("status", statusToCheck, "one of: " + validStatuses.join(", "));
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
// Performance optimization: set reasonable defaults
|
|
233
|
+
if (!sanitizedParams.per_page) {
|
|
234
|
+
sanitizedParams.per_page = 10; // Default to 10 posts for better performance
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
const posts = await client.getPosts(sanitizedParams);
|
|
179
238
|
if (posts.length === 0) {
|
|
180
|
-
|
|
239
|
+
const searchInfo = sanitizedParams.search ? ` matching "${sanitizedParams.search}"` : "";
|
|
240
|
+
const statusInfo = sanitizedParams.status ? ` with status "${sanitizedParams.status}"` : "";
|
|
241
|
+
return `No posts found${searchInfo}${statusInfo}. Try adjusting your search criteria or check if posts exist.`;
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
// Use streaming for large result sets (>50 posts)
|
|
245
|
+
if (posts.length > 50) {
|
|
246
|
+
const streamResults: StreamingResult<any>[] = [];
|
|
247
|
+
|
|
248
|
+
for await (const result of WordPressDataStreamer.streamPosts(posts, {
|
|
249
|
+
includeAuthor: true,
|
|
250
|
+
includeCategories: true,
|
|
251
|
+
includeTags: true,
|
|
252
|
+
batchSize: 20,
|
|
253
|
+
})) {
|
|
254
|
+
streamResults.push(result);
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
return StreamingUtils.formatStreamingResponse(streamResults, "posts");
|
|
181
258
|
}
|
|
259
|
+
|
|
260
|
+
// Add comprehensive site context information
|
|
261
|
+
const siteUrl = client.getSiteUrl ? client.getSiteUrl() : "Unknown site";
|
|
262
|
+
const totalPosts = posts.length;
|
|
263
|
+
const statusCounts = posts.reduce(
|
|
264
|
+
(acc, p) => {
|
|
265
|
+
acc[p.status] = (acc[p.status] || 0) + 1;
|
|
266
|
+
return acc;
|
|
267
|
+
},
|
|
268
|
+
{} as Record<string, number>,
|
|
269
|
+
);
|
|
270
|
+
|
|
271
|
+
// Enhanced metadata
|
|
272
|
+
const metadata = [
|
|
273
|
+
`📊 **Posts Summary**: ${totalPosts} total`,
|
|
274
|
+
`📝 **Status Breakdown**: ${Object.entries(statusCounts)
|
|
275
|
+
.map(([status, count]) => `${status}: ${count}`)
|
|
276
|
+
.join(", ")}`,
|
|
277
|
+
`🌐 **Source**: ${siteUrl}`,
|
|
278
|
+
`📅 **Retrieved**: ${new Date().toLocaleString()}`,
|
|
279
|
+
...(params.search ? [`🔍 **Search Term**: "${params.search}"`] : []),
|
|
280
|
+
...(params.categories ? [`📁 **Categories**: ${params.categories.join(", ")}`] : []),
|
|
281
|
+
...(params.tags ? [`🏷️ **Tags**: ${params.tags.join(", ")}`] : []),
|
|
282
|
+
];
|
|
283
|
+
|
|
284
|
+
// Fetch additional metadata for enhanced responses
|
|
285
|
+
const authorIds = [...new Set(posts.map((p) => p.author).filter(Boolean))];
|
|
286
|
+
const categoryIds = [...new Set(posts.flatMap((p) => p.categories || []))];
|
|
287
|
+
const tagIds = [...new Set(posts.flatMap((p) => p.tags || []))];
|
|
288
|
+
|
|
289
|
+
// Fetch authors, categories, and tags in parallel for better performance
|
|
290
|
+
const [authors, categories, tags] = await Promise.all([
|
|
291
|
+
authorIds.length > 0
|
|
292
|
+
? Promise.all(
|
|
293
|
+
authorIds.map(async (id) => {
|
|
294
|
+
try {
|
|
295
|
+
const user = await client.getUser(id);
|
|
296
|
+
return { id, name: user.name || user.username || `User ${id}` };
|
|
297
|
+
} catch {
|
|
298
|
+
return { id, name: `User ${id}` };
|
|
299
|
+
}
|
|
300
|
+
}),
|
|
301
|
+
)
|
|
302
|
+
: [],
|
|
303
|
+
categoryIds.length > 0
|
|
304
|
+
? Promise.all(
|
|
305
|
+
categoryIds.map(async (id) => {
|
|
306
|
+
try {
|
|
307
|
+
const category = await client.getCategory(id);
|
|
308
|
+
return { id, name: category.name || `Category ${id}` };
|
|
309
|
+
} catch {
|
|
310
|
+
return { id, name: `Category ${id}` };
|
|
311
|
+
}
|
|
312
|
+
}),
|
|
313
|
+
)
|
|
314
|
+
: [],
|
|
315
|
+
tagIds.length > 0
|
|
316
|
+
? Promise.all(
|
|
317
|
+
tagIds.map(async (id) => {
|
|
318
|
+
try {
|
|
319
|
+
const tag = await client.getTag(id);
|
|
320
|
+
return { id, name: tag.name || `Tag ${id}` };
|
|
321
|
+
} catch {
|
|
322
|
+
return { id, name: `Tag ${id}` };
|
|
323
|
+
}
|
|
324
|
+
}),
|
|
325
|
+
)
|
|
326
|
+
: [],
|
|
327
|
+
]);
|
|
328
|
+
|
|
329
|
+
// Create lookup maps for performance
|
|
330
|
+
const authorMap = new Map(authors.map((a) => [a.id, a.name]));
|
|
331
|
+
const categoryMap = new Map(categories.map((c) => [c.id, c.name]));
|
|
332
|
+
const tagMap = new Map(tags.map((t) => [t.id, t.name]));
|
|
333
|
+
|
|
182
334
|
const content =
|
|
183
|
-
|
|
335
|
+
metadata.join("\n") +
|
|
336
|
+
"\n\n" +
|
|
184
337
|
posts
|
|
185
|
-
.map(
|
|
186
|
-
(p)
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
338
|
+
.map((p) => {
|
|
339
|
+
const date = new Date(p.date);
|
|
340
|
+
const formattedDate = date.toLocaleDateString("en-US", {
|
|
341
|
+
year: "numeric",
|
|
342
|
+
month: "short",
|
|
343
|
+
day: "numeric",
|
|
344
|
+
});
|
|
345
|
+
const excerpt = p.excerpt?.rendered
|
|
346
|
+
? p.excerpt.rendered.replace(/<[^>]*>/g, "").substring(0, 80) + "..."
|
|
347
|
+
: "";
|
|
348
|
+
|
|
349
|
+
// Enhanced metadata
|
|
350
|
+
const authorName = authorMap.get(p.author) || `User ${p.author}`;
|
|
351
|
+
const postCategories = (p.categories || []).map((id) => categoryMap.get(id) || `Category ${id}`);
|
|
352
|
+
const postTags = (p.tags || []).map((id) => tagMap.get(id) || `Tag ${id}`);
|
|
353
|
+
|
|
354
|
+
let postInfo = `- ID ${p.id}: **${p.title.rendered}** (${p.status})\n`;
|
|
355
|
+
postInfo += ` 👤 Author: ${authorName}\n`;
|
|
356
|
+
postInfo += ` 📅 Published: ${formattedDate}\n`;
|
|
357
|
+
if (postCategories.length > 0) {
|
|
358
|
+
postInfo += ` 📁 Categories: ${postCategories.join(", ")}\n`;
|
|
359
|
+
}
|
|
360
|
+
if (postTags.length > 0) {
|
|
361
|
+
postInfo += ` 🏷️ Tags: ${postTags.join(", ")}\n`;
|
|
362
|
+
}
|
|
363
|
+
if (excerpt) {
|
|
364
|
+
postInfo += ` 📝 Excerpt: ${excerpt}\n`;
|
|
365
|
+
}
|
|
366
|
+
postInfo += ` 🔗 Link: ${p.link}`;
|
|
367
|
+
|
|
368
|
+
return postInfo;
|
|
369
|
+
})
|
|
370
|
+
.join("\n\n");
|
|
371
|
+
|
|
372
|
+
// Add pagination guidance for large result sets
|
|
373
|
+
let finalContent = content;
|
|
374
|
+
if (posts.length >= (sanitizedParams.per_page || 10)) {
|
|
375
|
+
finalContent += `\n\n📄 **Pagination Tip**: Use \`per_page\` parameter to control results (max 100). Current: ${sanitizedParams.per_page || 10}`;
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
return finalContent;
|
|
191
379
|
} catch (error) {
|
|
192
380
|
throw new Error(`Failed to list posts: ${getErrorMessage(error)}`);
|
|
193
381
|
}
|
|
194
382
|
}
|
|
195
383
|
|
|
196
|
-
public async handleGetPost(
|
|
197
|
-
client: WordPressClient,
|
|
198
|
-
params: { id: number },
|
|
199
|
-
): Promise<any> {
|
|
384
|
+
public async handleGetPost(client: WordPressClient, params: { id: number }): Promise<any> {
|
|
200
385
|
try {
|
|
386
|
+
// Input validation
|
|
387
|
+
if (!params.id || typeof params.id !== "number" || params.id <= 0) {
|
|
388
|
+
throw ErrorHandlers.validationError("id", params.id, "positive integer");
|
|
389
|
+
}
|
|
390
|
+
|
|
201
391
|
const post = await client.getPost(params.id);
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
392
|
+
|
|
393
|
+
// Fetch additional metadata for enhanced response
|
|
394
|
+
const [author, categories, tags] = await Promise.all([
|
|
395
|
+
post.author
|
|
396
|
+
? (async () => {
|
|
397
|
+
try {
|
|
398
|
+
const user = await client.getUser(post.author);
|
|
399
|
+
return user.name || user.username || `User ${post.author}`;
|
|
400
|
+
} catch {
|
|
401
|
+
return `User ${post.author}`;
|
|
402
|
+
}
|
|
403
|
+
})()
|
|
404
|
+
: "Unknown",
|
|
405
|
+
post.categories && post.categories.length > 0
|
|
406
|
+
? Promise.all(
|
|
407
|
+
post.categories.map(async (id) => {
|
|
408
|
+
try {
|
|
409
|
+
const category = await client.getCategory(id);
|
|
410
|
+
return category.name || `Category ${id}`;
|
|
411
|
+
} catch {
|
|
412
|
+
return `Category ${id}`;
|
|
413
|
+
}
|
|
414
|
+
}),
|
|
415
|
+
)
|
|
416
|
+
: [],
|
|
417
|
+
post.tags && post.tags.length > 0
|
|
418
|
+
? Promise.all(
|
|
419
|
+
post.tags.map(async (id) => {
|
|
420
|
+
try {
|
|
421
|
+
const tag = await client.getTag(id);
|
|
422
|
+
return tag.name || `Tag ${id}`;
|
|
423
|
+
} catch {
|
|
424
|
+
return `Tag ${id}`;
|
|
425
|
+
}
|
|
426
|
+
}),
|
|
427
|
+
)
|
|
428
|
+
: [],
|
|
429
|
+
]);
|
|
430
|
+
|
|
431
|
+
// Enhanced post details with comprehensive metadata
|
|
432
|
+
const siteUrl = client.getSiteUrl ? client.getSiteUrl() : "Unknown site";
|
|
433
|
+
const publishedDate = new Date(post.date);
|
|
434
|
+
const modifiedDate = new Date(post.modified);
|
|
435
|
+
const excerpt = post.excerpt?.rendered
|
|
436
|
+
? post.excerpt.rendered.replace(/<[^>]*>/g, "").substring(0, 150) + "..."
|
|
437
|
+
: "No excerpt available";
|
|
438
|
+
const wordCount = post.content?.rendered ? post.content.rendered.replace(/<[^>]*>/g, "").split(/\s+/).length : 0;
|
|
439
|
+
|
|
440
|
+
let content = `**📄 Post Details (ID: ${post.id})**\n\n`;
|
|
441
|
+
content += `**📋 Basic Information:**\n`;
|
|
442
|
+
content += `- **Title:** ${post.title.rendered}\n`;
|
|
443
|
+
content += `- **Status:** ${post.status}\n`;
|
|
444
|
+
content += `- **Type:** ${post.type}\n`;
|
|
445
|
+
content += `- **Author:** ${author}\n`;
|
|
446
|
+
content += `- **Slug:** ${post.slug}\n\n`;
|
|
447
|
+
|
|
448
|
+
content += `**📅 Dates:**\n`;
|
|
449
|
+
content += `- **Published:** ${publishedDate.toLocaleString()}\n`;
|
|
450
|
+
content += `- **Modified:** ${modifiedDate.toLocaleString()}\n\n`;
|
|
451
|
+
|
|
452
|
+
content += `**📊 Content:**\n`;
|
|
453
|
+
content += `- **Word Count:** ~${wordCount} words\n`;
|
|
454
|
+
content += `- **Excerpt:** ${excerpt}\n\n`;
|
|
455
|
+
|
|
456
|
+
if (categories.length > 0) {
|
|
457
|
+
content += `**📁 Categories:** ${categories.join(", ")}\n`;
|
|
458
|
+
}
|
|
459
|
+
if (tags.length > 0) {
|
|
460
|
+
content += `**🏷️ Tags:** ${tags.join(", ")}\n`;
|
|
461
|
+
}
|
|
462
|
+
if (categories.length > 0 || tags.length > 0) {
|
|
463
|
+
content += `\n`;
|
|
464
|
+
}
|
|
465
|
+
|
|
466
|
+
content += `**🔗 Links:**\n`;
|
|
467
|
+
content += `- **Permalink:** ${post.link}\n`;
|
|
468
|
+
content += `- **Edit Link:** ${post.link.replace(/\/$/, "")}/wp-admin/post.php?post=${post.id}&action=edit\n\n`;
|
|
469
|
+
content += `**🌐 Source:** ${siteUrl}\n`;
|
|
470
|
+
content += `**📅 Retrieved:** ${new Date().toLocaleString()}`;
|
|
471
|
+
|
|
208
472
|
return content;
|
|
209
473
|
} catch (error) {
|
|
210
|
-
|
|
474
|
+
// Handle specific error cases
|
|
475
|
+
const errorMessage = getErrorMessage(error);
|
|
476
|
+
|
|
477
|
+
if (errorMessage.includes("Invalid post ID") || errorMessage.includes("not found")) {
|
|
478
|
+
throw ErrorHandlers.postNotFound(params.id, error);
|
|
479
|
+
}
|
|
480
|
+
|
|
481
|
+
if (errorMessage.includes("401") || errorMessage.includes("Unauthorized")) {
|
|
482
|
+
throw ErrorHandlers.authenticationFailed(error);
|
|
483
|
+
}
|
|
484
|
+
|
|
485
|
+
if (errorMessage.includes("403") || errorMessage.includes("Forbidden")) {
|
|
486
|
+
throw ErrorHandlers.permissionDenied("get post", error);
|
|
487
|
+
}
|
|
488
|
+
|
|
489
|
+
if (errorMessage.includes("timeout") || errorMessage.includes("network")) {
|
|
490
|
+
throw ErrorHandlers.connectionError(error);
|
|
491
|
+
}
|
|
492
|
+
|
|
493
|
+
// Generic error with suggestions
|
|
494
|
+
throw ErrorHandlers.generic("get post", error);
|
|
211
495
|
}
|
|
212
496
|
}
|
|
213
497
|
|
|
214
|
-
public async handleCreatePost(
|
|
215
|
-
client: WordPressClient,
|
|
216
|
-
params: CreatePostRequest,
|
|
217
|
-
): Promise<any> {
|
|
498
|
+
public async handleCreatePost(client: WordPressClient, params: CreatePostRequest): Promise<any> {
|
|
218
499
|
try {
|
|
219
|
-
|
|
220
|
-
|
|
500
|
+
// Enhanced input validation using new validation utilities
|
|
501
|
+
const validatedParams = validatePostParams(params);
|
|
502
|
+
|
|
503
|
+
const post = await client.createPost(validatedParams);
|
|
504
|
+
const siteUrl = client.getSiteUrl ? client.getSiteUrl() : "Unknown site";
|
|
505
|
+
|
|
506
|
+
return (
|
|
507
|
+
`✅ **Post Created Successfully!**\n\n` +
|
|
508
|
+
`**📄 Post Details:**\n` +
|
|
509
|
+
`- **ID:** ${post.id}\n` +
|
|
510
|
+
`- **Title:** ${post.title.rendered}\n` +
|
|
511
|
+
`- **Status:** ${post.status}\n` +
|
|
512
|
+
`- **Link:** ${post.link}\n` +
|
|
513
|
+
`- **Edit Link:** ${post.link.replace(/\/$/, "")}/wp-admin/post.php?post=${post.id}&action=edit\n\n` +
|
|
514
|
+
`**🌐 Site:** ${siteUrl}\n` +
|
|
515
|
+
`**📅 Created:** ${new Date().toLocaleString()}`
|
|
516
|
+
);
|
|
221
517
|
} catch (error) {
|
|
518
|
+
if (error instanceof EnhancedError) {
|
|
519
|
+
throw error;
|
|
520
|
+
}
|
|
222
521
|
throw new Error(`Failed to create post: ${getErrorMessage(error)}`);
|
|
223
522
|
}
|
|
224
523
|
}
|
|
225
524
|
|
|
226
|
-
public async handleUpdatePost(
|
|
227
|
-
client: WordPressClient,
|
|
228
|
-
params: UpdatePostRequest & { id: number },
|
|
229
|
-
): Promise<any> {
|
|
525
|
+
public async handleUpdatePost(client: WordPressClient, params: UpdatePostRequest & { id: number }): Promise<any> {
|
|
230
526
|
try {
|
|
231
527
|
const post = await client.updatePost(params);
|
|
232
528
|
return `✅ Post ${post.id} updated successfully.`;
|
|
@@ -235,10 +531,7 @@ export class PostTools {
|
|
|
235
531
|
}
|
|
236
532
|
}
|
|
237
533
|
|
|
238
|
-
public async handleDeletePost(
|
|
239
|
-
client: WordPressClient,
|
|
240
|
-
params: { id: number; force?: boolean },
|
|
241
|
-
): Promise<any> {
|
|
534
|
+
public async handleDeletePost(client: WordPressClient, params: { id: number; force?: boolean }): Promise<any> {
|
|
242
535
|
try {
|
|
243
536
|
await client.deletePost(params.id, params.force);
|
|
244
537
|
const action = params.force ? "permanently deleted" : "moved to trash";
|
|
@@ -248,10 +541,7 @@ export class PostTools {
|
|
|
248
541
|
}
|
|
249
542
|
}
|
|
250
543
|
|
|
251
|
-
public async handleGetPostRevisions(
|
|
252
|
-
client: WordPressClient,
|
|
253
|
-
params: { id: number },
|
|
254
|
-
): Promise<any> {
|
|
544
|
+
public async handleGetPostRevisions(client: WordPressClient, params: { id: number }): Promise<any> {
|
|
255
545
|
try {
|
|
256
546
|
const revisions = await client.getPostRevisions(params.id);
|
|
257
547
|
if (revisions.length === 0) {
|
|
@@ -260,18 +550,11 @@ export class PostTools {
|
|
|
260
550
|
const content =
|
|
261
551
|
`Found ${revisions.length} revisions for post ${params.id}:\n\n` +
|
|
262
552
|
revisions
|
|
263
|
-
.map(
|
|
264
|
-
(r) =>
|
|
265
|
-
`- Revision by user ID ${r.author} at ${new Date(
|
|
266
|
-
r.modified,
|
|
267
|
-
).toLocaleString()}`,
|
|
268
|
-
)
|
|
553
|
+
.map((r) => `- Revision by user ID ${r.author} at ${new Date(r.modified).toLocaleString()}`)
|
|
269
554
|
.join("\n");
|
|
270
555
|
return content;
|
|
271
556
|
} catch (error) {
|
|
272
|
-
throw new Error(
|
|
273
|
-
`Failed to get post revisions: ${getErrorMessage(error)}`,
|
|
274
|
-
);
|
|
557
|
+
throw new Error(`Failed to get post revisions: ${getErrorMessage(error)}`);
|
|
275
558
|
}
|
|
276
559
|
}
|
|
277
560
|
}
|