mcp-wordpress 2.4.2 → 2.5.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 (348) hide show
  1. package/README.md +114 -48
  2. package/dist/ajv-patch.js +34 -0
  3. package/dist/cache/CacheInvalidation.d.ts +3 -1
  4. package/dist/cache/CacheInvalidation.d.ts.map +1 -1
  5. package/dist/cache/CacheInvalidation.js +10 -4
  6. package/dist/cache/CacheInvalidation.js.map +1 -1
  7. package/dist/cache/CacheManager.d.ts +3 -2
  8. package/dist/cache/CacheManager.d.ts.map +1 -1
  9. package/dist/cache/CacheManager.js +11 -3
  10. package/dist/cache/CacheManager.js.map +1 -1
  11. package/dist/cache/HttpCacheWrapper.d.ts +7 -6
  12. package/dist/cache/HttpCacheWrapper.d.ts.map +1 -1
  13. package/dist/cache/HttpCacheWrapper.js +8 -5
  14. package/dist/cache/HttpCacheWrapper.js.map +1 -1
  15. package/dist/cache/__tests__/HttpCacheWrapper.test.js +6 -5
  16. package/dist/cache/__tests__/HttpCacheWrapper.test.js.map +1 -1
  17. package/dist/cache/index.d.ts +3 -3
  18. package/dist/cache/index.d.ts.map +1 -1
  19. package/dist/cache/index.js +1 -1
  20. package/dist/cache/index.js.map +1 -1
  21. package/dist/client/CachedWordPressClient.d.ts +23 -9
  22. package/dist/client/CachedWordPressClient.d.ts.map +1 -1
  23. package/dist/client/CachedWordPressClient.js +4 -1
  24. package/dist/client/CachedWordPressClient.js.map +1 -1
  25. package/dist/client/MockWordPressClient.d.ts +2 -1
  26. package/dist/client/MockWordPressClient.d.ts.map +1 -1
  27. package/dist/client/MockWordPressClient.js +3 -1
  28. package/dist/client/MockWordPressClient.js.map +1 -1
  29. package/dist/client/api.d.ts +17 -13
  30. package/dist/client/api.d.ts.map +1 -1
  31. package/dist/client/api.js +135 -30
  32. package/dist/client/api.js.map +1 -1
  33. package/dist/client/auth.d.ts.map +1 -1
  34. package/dist/client/auth.js +2 -3
  35. package/dist/client/auth.js.map +1 -1
  36. package/dist/client/managers/AuthenticationManager.d.ts +55 -2
  37. package/dist/client/managers/AuthenticationManager.d.ts.map +1 -1
  38. package/dist/client/managers/AuthenticationManager.js +269 -71
  39. package/dist/client/managers/AuthenticationManager.js.map +1 -1
  40. package/dist/client/managers/BaseManager.d.ts +3 -3
  41. package/dist/client/managers/BaseManager.d.ts.map +1 -1
  42. package/dist/client/managers/BaseManager.js +11 -5
  43. package/dist/client/managers/BaseManager.js.map +1 -1
  44. package/dist/client/managers/RequestManager.d.ts +2 -2
  45. package/dist/client/managers/RequestManager.d.ts.map +1 -1
  46. package/dist/client/managers/RequestManager.js +25 -12
  47. package/dist/client/managers/RequestManager.js.map +1 -1
  48. package/dist/config/Config.d.ts +155 -0
  49. package/dist/config/Config.d.ts.map +1 -0
  50. package/dist/config/Config.js +215 -0
  51. package/dist/config/Config.js.map +1 -0
  52. package/dist/config/ConfigurationSchema.d.ts +21 -21
  53. package/dist/config/ConfigurationSchema.d.ts.map +1 -1
  54. package/dist/config/ConfigurationSchema.js +19 -2
  55. package/dist/config/ConfigurationSchema.js.map +1 -1
  56. package/dist/config/ServerConfiguration.d.ts +2 -1
  57. package/dist/config/ServerConfiguration.d.ts.map +1 -1
  58. package/dist/config/ServerConfiguration.js +50 -41
  59. package/dist/config/ServerConfiguration.js.map +1 -1
  60. package/dist/docs/DocumentationGenerator.d.ts +9 -8
  61. package/dist/docs/DocumentationGenerator.d.ts.map +1 -1
  62. package/dist/docs/DocumentationGenerator.js +10 -7
  63. package/dist/docs/DocumentationGenerator.js.map +1 -1
  64. package/dist/docs/MarkdownFormatter.d.ts.map +1 -1
  65. package/dist/docs/MarkdownFormatter.js +3 -2
  66. package/dist/docs/MarkdownFormatter.js.map +1 -1
  67. package/dist/dxt-entry.cjs +81 -0
  68. package/dist/dxt-entry.js +15 -14
  69. package/dist/dxt-entry.js.map +1 -1
  70. package/dist/index.d.ts +3 -1
  71. package/dist/index.d.ts.map +1 -1
  72. package/dist/index.js +37 -21
  73. package/dist/index.js.map +1 -1
  74. package/dist/performance/MetricsCollector.d.ts +13 -7
  75. package/dist/performance/MetricsCollector.d.ts.map +1 -1
  76. package/dist/performance/MetricsCollector.js +69 -27
  77. package/dist/performance/MetricsCollector.js.map +1 -1
  78. package/dist/performance/PerformanceAnalytics.d.ts +8 -2
  79. package/dist/performance/PerformanceAnalytics.d.ts.map +1 -1
  80. package/dist/performance/PerformanceAnalytics.js +17 -47
  81. package/dist/performance/PerformanceAnalytics.js.map +1 -1
  82. package/dist/performance/PerformanceMonitor.d.ts +2 -1
  83. package/dist/performance/PerformanceMonitor.d.ts.map +1 -1
  84. package/dist/performance/PerformanceMonitor.js +12 -13
  85. package/dist/performance/PerformanceMonitor.js.map +1 -1
  86. package/dist/performance/index.d.ts +2 -2
  87. package/dist/performance/index.d.ts.map +1 -1
  88. package/dist/security/AISecurityScanner.d.ts +1 -0
  89. package/dist/security/AISecurityScanner.d.ts.map +1 -1
  90. package/dist/security/AISecurityScanner.js +22 -12
  91. package/dist/security/AISecurityScanner.js.map +1 -1
  92. package/dist/security/AutomatedRemediation.d.ts +4 -3
  93. package/dist/security/AutomatedRemediation.d.ts.map +1 -1
  94. package/dist/security/AutomatedRemediation.js +46 -15
  95. package/dist/security/AutomatedRemediation.js.map +1 -1
  96. package/dist/security/InputValidator.d.ts +13 -9
  97. package/dist/security/InputValidator.d.ts.map +1 -1
  98. package/dist/security/InputValidator.js +4 -2
  99. package/dist/security/InputValidator.js.map +1 -1
  100. package/dist/security/SecurityCIPipeline.d.ts +1 -1
  101. package/dist/security/SecurityCIPipeline.d.ts.map +1 -1
  102. package/dist/security/SecurityCIPipeline.js +38 -29
  103. package/dist/security/SecurityCIPipeline.js.map +1 -1
  104. package/dist/security/SecurityConfig.d.ts +3 -3
  105. package/dist/security/SecurityConfig.d.ts.map +1 -1
  106. package/dist/security/SecurityConfig.js +13 -9
  107. package/dist/security/SecurityConfig.js.map +1 -1
  108. package/dist/security/SecurityConfigManager.d.ts +2 -2
  109. package/dist/security/SecurityConfigManager.d.ts.map +1 -1
  110. package/dist/security/SecurityConfigManager.js +20 -15
  111. package/dist/security/SecurityConfigManager.js.map +1 -1
  112. package/dist/security/SecurityMonitoring.d.ts +2 -2
  113. package/dist/security/SecurityMonitoring.d.ts.map +1 -1
  114. package/dist/security/SecurityMonitoring.js +19 -17
  115. package/dist/security/SecurityMonitoring.js.map +1 -1
  116. package/dist/security/SecurityReviewer.d.ts.map +1 -1
  117. package/dist/security/SecurityReviewer.js +10 -7
  118. package/dist/security/SecurityReviewer.js.map +1 -1
  119. package/dist/security/index.d.ts +24 -23
  120. package/dist/security/index.d.ts.map +1 -1
  121. package/dist/security/index.js +52 -23
  122. package/dist/security/index.js.map +1 -1
  123. package/dist/server/ConnectionTester.d.ts +12 -4
  124. package/dist/server/ConnectionTester.d.ts.map +1 -1
  125. package/dist/server/ConnectionTester.js +96 -22
  126. package/dist/server/ConnectionTester.js.map +1 -1
  127. package/dist/server/ToolRegistry.d.ts +2 -2
  128. package/dist/server/ToolRegistry.d.ts.map +1 -1
  129. package/dist/server/ToolRegistry.js +10 -5
  130. package/dist/server/ToolRegistry.js.map +1 -1
  131. package/dist/tools/BaseToolManager.d.ts +47 -11
  132. package/dist/tools/BaseToolManager.d.ts.map +1 -1
  133. package/dist/tools/BaseToolManager.js +168 -29
  134. package/dist/tools/BaseToolManager.js.map +1 -1
  135. package/dist/tools/auth.d.ts +16 -10
  136. package/dist/tools/auth.d.ts.map +1 -1
  137. package/dist/tools/auth.js +3 -2
  138. package/dist/tools/auth.js.map +1 -1
  139. package/dist/tools/cache.d.ts +30 -30
  140. package/dist/tools/cache.d.ts.map +1 -1
  141. package/dist/tools/cache.js +1 -6
  142. package/dist/tools/cache.js.map +1 -1
  143. package/dist/tools/comments.d.ts +20 -20
  144. package/dist/tools/comments.d.ts.map +1 -1
  145. package/dist/tools/comments.js +16 -9
  146. package/dist/tools/comments.js.map +1 -1
  147. package/dist/tools/media.d.ts +18 -16
  148. package/dist/tools/media.d.ts.map +1 -1
  149. package/dist/tools/media.js +16 -15
  150. package/dist/tools/media.js.map +1 -1
  151. package/dist/tools/pages.d.ts +19 -17
  152. package/dist/tools/pages.d.ts.map +1 -1
  153. package/dist/tools/pages.js +16 -12
  154. package/dist/tools/pages.js.map +1 -1
  155. package/dist/tools/performance.d.ts +11 -1
  156. package/dist/tools/performance.d.ts.map +1 -1
  157. package/dist/tools/performance.js +67 -34
  158. package/dist/tools/performance.js.map +1 -1
  159. package/dist/tools/posts/PostHandlers.d.ts +46 -0
  160. package/dist/tools/posts/PostHandlers.d.ts.map +1 -0
  161. package/dist/tools/posts/PostHandlers.js +400 -0
  162. package/dist/tools/posts/PostHandlers.js.map +1 -0
  163. package/dist/tools/posts/PostToolDefinitions.d.ts +37 -0
  164. package/dist/tools/posts/PostToolDefinitions.d.ts.map +1 -0
  165. package/dist/tools/posts/PostToolDefinitions.js +236 -0
  166. package/dist/tools/posts/PostToolDefinitions.js.map +1 -0
  167. package/dist/tools/posts/index.d.ts +138 -0
  168. package/dist/tools/posts/index.d.ts.map +1 -0
  169. package/dist/tools/posts/index.js +163 -0
  170. package/dist/tools/posts/index.js.map +1 -0
  171. package/dist/tools/posts.d.ts +10 -246
  172. package/dist/tools/posts.d.ts.map +1 -1
  173. package/dist/tools/posts.js +11 -723
  174. package/dist/tools/posts.js.map +1 -1
  175. package/dist/tools/site.d.ts +19 -18
  176. package/dist/tools/site.d.ts.map +1 -1
  177. package/dist/tools/site.js +14 -10
  178. package/dist/tools/site.js.map +1 -1
  179. package/dist/tools/taxonomies.d.ts +23 -24
  180. package/dist/tools/taxonomies.d.ts.map +1 -1
  181. package/dist/tools/taxonomies.js +24 -18
  182. package/dist/tools/taxonomies.js.map +1 -1
  183. package/dist/tools/users.d.ts +20 -15
  184. package/dist/tools/users.d.ts.map +1 -1
  185. package/dist/tools/users.js +12 -8
  186. package/dist/tools/users.js.map +1 -1
  187. package/dist/types/client.d.ts +48 -41
  188. package/dist/types/client.d.ts.map +1 -1
  189. package/dist/types/client.js +30 -5
  190. package/dist/types/client.js.map +1 -1
  191. package/dist/types/enhanced.d.ts +237 -0
  192. package/dist/types/enhanced.d.ts.map +1 -0
  193. package/dist/types/enhanced.js +49 -0
  194. package/dist/types/enhanced.js.map +1 -0
  195. package/dist/types/index.d.ts +15 -12
  196. package/dist/types/index.d.ts.map +1 -1
  197. package/dist/types/index.js +2 -0
  198. package/dist/types/index.js.map +1 -1
  199. package/dist/types/mcp.d.ts +12 -12
  200. package/dist/types/mcp.d.ts.map +1 -1
  201. package/dist/types/requests.d.ts +322 -0
  202. package/dist/types/requests.d.ts.map +1 -0
  203. package/dist/types/requests.js +8 -0
  204. package/dist/types/requests.js.map +1 -0
  205. package/dist/types/tools.d.ts +506 -0
  206. package/dist/types/tools.d.ts.map +1 -0
  207. package/dist/types/tools.js +8 -0
  208. package/dist/types/tools.js.map +1 -0
  209. package/dist/types/wordpress.d.ts +43 -15
  210. package/dist/types/wordpress.d.ts.map +1 -1
  211. package/dist/types/wordpress.js +8 -1
  212. package/dist/types/wordpress.js.map +1 -1
  213. package/dist/utils/debug.d.ts +19 -11
  214. package/dist/utils/debug.d.ts.map +1 -1
  215. package/dist/utils/debug.js +46 -10
  216. package/dist/utils/debug.js.map +1 -1
  217. package/dist/utils/enhancedError.d.ts +8 -8
  218. package/dist/utils/enhancedError.d.ts.map +1 -1
  219. package/dist/utils/enhancedError.js.map +1 -1
  220. package/dist/utils/error.d.ts +2 -4
  221. package/dist/utils/error.d.ts.map +1 -1
  222. package/dist/utils/error.js +42 -5
  223. package/dist/utils/error.js.map +1 -1
  224. package/dist/utils/logger.d.ts +106 -0
  225. package/dist/utils/logger.d.ts.map +1 -0
  226. package/dist/utils/logger.js +280 -0
  227. package/dist/utils/logger.js.map +1 -0
  228. package/dist/utils/streaming.d.ts +9 -9
  229. package/dist/utils/streaming.d.ts.map +1 -1
  230. package/dist/utils/streaming.js +71 -52
  231. package/dist/utils/streaming.js.map +1 -1
  232. package/dist/utils/toolWrapper.d.ts +9 -7
  233. package/dist/utils/toolWrapper.d.ts.map +1 -1
  234. package/dist/utils/toolWrapper.js.map +1 -1
  235. package/dist/utils/validation/core.d.ts +21 -0
  236. package/dist/utils/validation/core.d.ts.map +1 -0
  237. package/dist/utils/validation/core.js +71 -0
  238. package/dist/utils/validation/core.js.map +1 -0
  239. package/dist/utils/validation/index.d.ts +25 -0
  240. package/dist/utils/validation/index.d.ts.map +1 -0
  241. package/dist/utils/validation/index.js +29 -0
  242. package/dist/utils/validation/index.js.map +1 -0
  243. package/dist/utils/validation/network.d.ts +19 -0
  244. package/dist/utils/validation/network.d.ts.map +1 -0
  245. package/dist/utils/validation/network.js +93 -0
  246. package/dist/utils/validation/network.js.map +1 -0
  247. package/dist/utils/validation/rateLimit.d.ts +21 -0
  248. package/dist/utils/validation/rateLimit.d.ts.map +1 -0
  249. package/dist/utils/validation/rateLimit.js +43 -0
  250. package/dist/utils/validation/rateLimit.js.map +1 -0
  251. package/dist/utils/validation/security.d.ts +29 -0
  252. package/dist/utils/validation/security.d.ts.map +1 -0
  253. package/dist/utils/validation/security.js +327 -0
  254. package/dist/utils/validation/security.js.map +1 -0
  255. package/dist/utils/validation/wordpress.d.ts +31 -0
  256. package/dist/utils/validation/wordpress.d.ts.map +1 -0
  257. package/dist/utils/validation/wordpress.js +146 -0
  258. package/dist/utils/validation/wordpress.js.map +1 -0
  259. package/dist/utils/validation.d.ts +13 -82
  260. package/dist/utils/validation.d.ts.map +1 -1
  261. package/dist/utils/validation.js +25 -343
  262. package/dist/utils/validation.js.map +1 -1
  263. package/docs/BADGE_UPDATES.md +132 -0
  264. package/docs/CI_CD_IMPROVEMENTS.md +191 -0
  265. package/docs/INCREMENTAL_COVERAGE.md +183 -0
  266. package/docs/api/README.md +3 -1
  267. package/docs/api/openapi.json +5 -1
  268. package/docs/api/summary.json +1 -1
  269. package/docs/api/tools/wp_create_post.md +12 -14
  270. package/docs/examples/claude-desktop-config.md +1 -1
  271. package/docs/examples/docker-production.md +100 -93
  272. package/docs/examples/multi-site-setup.md +5 -4
  273. package/docs/examples/single-site-setup.md +3 -4
  274. package/docs/examples/use-case-workflows.md +4 -5
  275. package/docs/integrations/claude-desktop.md +31 -31
  276. package/docs/integrations/cline.md +4 -4
  277. package/docs/integrations/vs-code.md +9 -8
  278. package/docs/user-guides/SMITHERY_SETUP.md +10 -10
  279. package/package.json +44 -25
  280. package/src/cache/CacheInvalidation.ts +12 -5
  281. package/src/cache/CacheManager.ts +18 -15
  282. package/src/cache/HttpCacheWrapper.ts +30 -59
  283. package/src/cache/__tests__/HttpCacheWrapper.test.ts +6 -5
  284. package/src/cache/index.ts +3 -14
  285. package/src/client/CachedWordPressClient.ts +32 -30
  286. package/src/client/MockWordPressClient.ts +4 -2
  287. package/src/client/api.ts +186 -64
  288. package/src/client/auth.ts +15 -40
  289. package/src/client/managers/AuthenticationManager.ts +337 -77
  290. package/src/client/managers/BaseManager.ts +18 -30
  291. package/src/client/managers/RequestManager.ts +39 -44
  292. package/src/config/Config.ts +308 -0
  293. package/src/config/ConfigurationSchema.ts +23 -2
  294. package/src/config/ServerConfiguration.ts +51 -47
  295. package/src/docs/DocumentationGenerator.ts +50 -39
  296. package/src/docs/MarkdownFormatter.ts +19 -29
  297. package/src/dxt-entry.cjs +26 -16
  298. package/src/dxt-entry.ts +17 -27
  299. package/src/index.ts +42 -28
  300. package/src/performance/MetricsCollector.ts +108 -86
  301. package/src/performance/PerformanceAnalytics.ts +69 -164
  302. package/src/performance/PerformanceMonitor.ts +32 -47
  303. package/src/performance/index.ts +2 -10
  304. package/src/security/AISecurityScanner.ts +22 -12
  305. package/src/security/AutomatedRemediation.ts +49 -18
  306. package/src/security/InputValidator.ts +9 -6
  307. package/src/security/SecurityCIPipeline.ts +53 -37
  308. package/src/security/SecurityConfig.ts +22 -22
  309. package/src/security/SecurityConfigManager.ts +23 -19
  310. package/src/security/SecurityMonitoring.ts +24 -21
  311. package/src/security/SecurityReviewer.ts +10 -7
  312. package/src/security/index.ts +64 -29
  313. package/src/server/ConnectionTester.ts +120 -31
  314. package/src/server/ToolRegistry.ts +31 -21
  315. package/src/tools/BaseToolManager.ts +286 -33
  316. package/src/tools/auth.ts +20 -8
  317. package/src/tools/cache.ts +5 -15
  318. package/src/tools/comments.ts +34 -48
  319. package/src/tools/media.ts +41 -53
  320. package/src/tools/pages.ts +32 -54
  321. package/src/tools/performance.ts +141 -176
  322. package/src/tools/posts/PostHandlers.ts +474 -0
  323. package/src/tools/posts/PostToolDefinitions.ts +250 -0
  324. package/src/tools/posts/index.ts +192 -0
  325. package/src/tools/posts.ts +24 -780
  326. package/src/tools/site.ts +34 -19
  327. package/src/tools/taxonomies.ts +41 -57
  328. package/src/tools/users.ts +28 -16
  329. package/src/types/client.ts +114 -138
  330. package/src/types/enhanced.ts +318 -0
  331. package/src/types/index.ts +51 -30
  332. package/src/types/mcp.ts +20 -42
  333. package/src/types/requests.ts +378 -0
  334. package/src/types/tools.ts +608 -0
  335. package/src/types/wordpress.ts +56 -34
  336. package/src/utils/debug.ts +77 -59
  337. package/src/utils/enhancedError.ts +8 -8
  338. package/src/utils/error.ts +53 -31
  339. package/src/utils/logger.ts +351 -0
  340. package/src/utils/streaming.ts +86 -68
  341. package/src/utils/toolWrapper.ts +10 -12
  342. package/src/utils/validation/core.ts +108 -0
  343. package/src/utils/validation/index.ts +36 -0
  344. package/src/utils/validation/network.ts +132 -0
  345. package/src/utils/validation/rateLimit.ts +54 -0
  346. package/src/utils/validation/security.ts +361 -0
  347. package/src/utils/validation/wordpress.ts +180 -0
  348. package/src/utils/validation.ts +47 -470
@@ -0,0 +1,474 @@
1
+ /**
2
+ * WordPress Posts Handler Implementation
3
+ *
4
+ * Implements all handler methods for WordPress post management tools.
5
+ * This module contains the business logic for post operations including
6
+ * validation, API interaction, and response formatting.
7
+ */
8
+
9
+ import { WordPressClient } from "../../client/api.js";
10
+ import { CreatePostRequest, PostQueryParams, UpdatePostRequest, WordPressPost } from "../../types/wordpress.js";
11
+ import { getErrorMessage } from "../../utils/error.js";
12
+ import { ErrorHandlers } from "../../utils/enhancedError.js";
13
+ import { validateId, validatePaginationParams, validatePostParams } from "../../utils/validation.js";
14
+ import { sanitizeHtml } from "../../utils/validation/security.js";
15
+ import { WordPressDataStreamer, StreamingUtils, StreamingResult } from "../../utils/streaming.js";
16
+
17
+ /**
18
+ * Handles listing WordPress posts with advanced filtering and pagination
19
+ */
20
+ export async function handleListPosts(
21
+ client: WordPressClient,
22
+ params: PostQueryParams,
23
+ ): Promise<WordPressPost[] | string> {
24
+ try {
25
+ // Enhanced input validation and sanitization
26
+ const paginationValidated = validatePaginationParams({
27
+ page: params.page,
28
+ per_page: params.per_page,
29
+ offset: params.offset,
30
+ });
31
+
32
+ const sanitizedParams = {
33
+ ...params,
34
+ ...paginationValidated,
35
+ };
36
+
37
+ // Validate and sanitize search term
38
+ if (sanitizedParams.search) {
39
+ sanitizedParams.search = sanitizedParams.search.trim();
40
+ if (sanitizedParams.search.length === 0) {
41
+ delete sanitizedParams.search;
42
+ }
43
+ }
44
+
45
+ // Validate category and tag IDs if provided
46
+ if (sanitizedParams.categories) {
47
+ sanitizedParams.categories = sanitizedParams.categories.map((id) => validateId(id, "category ID"));
48
+ }
49
+
50
+ if (sanitizedParams.tags) {
51
+ sanitizedParams.tags = sanitizedParams.tags.map((id) => validateId(id, "tag ID"));
52
+ }
53
+
54
+ // Validate status parameter
55
+ if (sanitizedParams.status) {
56
+ const validStatuses = ["publish", "future", "draft", "pending", "private"];
57
+ const statusesToCheck = Array.isArray(sanitizedParams.status) ? sanitizedParams.status : [sanitizedParams.status];
58
+
59
+ for (const statusToCheck of statusesToCheck) {
60
+ if (!validStatuses.includes(statusToCheck)) {
61
+ throw ErrorHandlers.validationError("status", statusToCheck, "one of: " + validStatuses.join(", "));
62
+ }
63
+ }
64
+ }
65
+
66
+ // Performance optimization: set reasonable defaults
67
+ if (!sanitizedParams.per_page) {
68
+ sanitizedParams.per_page = 10; // Default to 10 posts for better performance
69
+ }
70
+
71
+ const posts = await client.getPosts(sanitizedParams);
72
+ if (posts.length === 0) {
73
+ const searchInfo = sanitizedParams.search ? ` matching "${sanitizedParams.search}"` : "";
74
+ const statusInfo = sanitizedParams.status ? ` with status "${sanitizedParams.status}"` : "";
75
+ return `No posts found${searchInfo}${statusInfo}. Try adjusting your search criteria or check if posts exist.`;
76
+ }
77
+
78
+ // Use streaming for large result sets (>50 posts)
79
+ if (posts.length > 50) {
80
+ const streamResults: StreamingResult<unknown>[] = [];
81
+
82
+ for await (const result of WordPressDataStreamer.streamPosts(posts, {
83
+ includeAuthor: true,
84
+ includeCategories: true,
85
+ includeTags: true,
86
+ batchSize: 20,
87
+ })) {
88
+ streamResults.push(result);
89
+ }
90
+
91
+ return StreamingUtils.formatStreamingResponse(streamResults, "posts");
92
+ }
93
+
94
+ // Add comprehensive site context information
95
+ const siteUrl = client.getSiteUrl ? client.getSiteUrl() : "Unknown site";
96
+ const totalPosts = posts.length;
97
+ const statusCounts = posts.reduce(
98
+ (acc, p) => {
99
+ acc[p.status] = (acc[p.status] || 0) + 1;
100
+ return acc;
101
+ },
102
+ {} as Record<string, number>,
103
+ );
104
+
105
+ // Enhanced metadata
106
+ const metadata = [
107
+ `📊 **Posts Summary**: ${totalPosts} total`,
108
+ `📝 **Status Breakdown**: ${Object.entries(statusCounts)
109
+ .map(([status, count]) => `${status}: ${count}`)
110
+ .join(", ")}`,
111
+ `🌐 **Source**: ${siteUrl}`,
112
+ `📅 **Retrieved**: ${new Date().toLocaleString()}`,
113
+ ...(params.search ? [`🔍 **Search Term**: "${params.search}"`] : []),
114
+ ...(params.categories ? [`📁 **Categories**: ${params.categories.join(", ")}`] : []),
115
+ ...(params.tags ? [`🏷️ **Tags**: ${params.tags.join(", ")}`] : []),
116
+ ];
117
+
118
+ // Fetch additional metadata for enhanced responses
119
+ const authorIds = [...new Set(posts.map((p) => p.author).filter(Boolean))];
120
+ const categoryIds = [...new Set(posts.flatMap((p) => p.categories || []))];
121
+ const tagIds = [...new Set(posts.flatMap((p) => p.tags || []))];
122
+
123
+ // Fetch authors, categories, and tags in parallel for better performance
124
+ const [authors, categories, tags] = await Promise.all([
125
+ authorIds.length > 0
126
+ ? Promise.all(
127
+ authorIds.map(async (id) => {
128
+ try {
129
+ const user = await client.getUser(id);
130
+ return { id, name: user.name || user.username || `User ${id}` };
131
+ } catch {
132
+ return { id, name: `User ${id}` };
133
+ }
134
+ }),
135
+ )
136
+ : [],
137
+ categoryIds.length > 0
138
+ ? Promise.all(
139
+ categoryIds.map(async (id) => {
140
+ try {
141
+ const category = await client.getCategory(id);
142
+ return { id, name: category.name || `Category ${id}` };
143
+ } catch {
144
+ return { id, name: `Category ${id}` };
145
+ }
146
+ }),
147
+ )
148
+ : [],
149
+ tagIds.length > 0
150
+ ? Promise.all(
151
+ tagIds.map(async (id) => {
152
+ try {
153
+ const tag = await client.getTag(id);
154
+ return { id, name: tag.name || `Tag ${id}` };
155
+ } catch {
156
+ return { id, name: `Tag ${id}` };
157
+ }
158
+ }),
159
+ )
160
+ : [],
161
+ ]);
162
+
163
+ // Create lookup maps for performance
164
+ const authorMap = new Map(authors.map((a) => [a.id, a.name]));
165
+ const categoryMap = new Map(categories.map((c) => [c.id, c.name]));
166
+ const tagMap = new Map(tags.map((t) => [t.id, t.name]));
167
+
168
+ const content =
169
+ metadata.join("\n") +
170
+ "\n\n" +
171
+ posts
172
+ .map((p) => {
173
+ const date = new Date(p.date);
174
+ const formattedDate = date.toLocaleDateString("en-US", {
175
+ year: "numeric",
176
+ month: "short",
177
+ day: "numeric",
178
+ });
179
+ const excerpt = p.excerpt?.rendered ? sanitizeHtml(p.excerpt.rendered).substring(0, 80) + "..." : "";
180
+
181
+ // Enhanced metadata
182
+ const authorName = authorMap.get(p.author) || `User ${p.author}`;
183
+ const postCategories = (p.categories || []).map((id) => categoryMap.get(id) || `Category ${id}`);
184
+ const postTags = (p.tags || []).map((id) => tagMap.get(id) || `Tag ${id}`);
185
+
186
+ let postInfo = `- ID ${p.id}: **${p.title.rendered}** (${p.status})\n`;
187
+ postInfo += ` 👤 Author: ${authorName}\n`;
188
+ postInfo += ` 📅 Published: ${formattedDate}\n`;
189
+ if (postCategories.length > 0) {
190
+ postInfo += ` 📁 Categories: ${postCategories.join(", ")}\n`;
191
+ }
192
+ if (postTags.length > 0) {
193
+ postInfo += ` 🏷️ Tags: ${postTags.join(", ")}\n`;
194
+ }
195
+ if (excerpt) {
196
+ postInfo += ` 📝 Excerpt: ${excerpt}\n`;
197
+ }
198
+ postInfo += ` 🔗 Link: ${p.link}`;
199
+
200
+ return postInfo;
201
+ })
202
+ .join("\n\n");
203
+
204
+ // Add pagination guidance for large result sets
205
+ let finalContent = content;
206
+ if (posts.length >= (sanitizedParams.per_page || 10)) {
207
+ finalContent += `\n\n📄 **Pagination Tip**: Use \`per_page\` parameter to control results (max 100). Current: ${sanitizedParams.per_page || 10}`;
208
+ }
209
+
210
+ return finalContent;
211
+ } catch (error) {
212
+ throw new Error(`Failed to list posts: ${getErrorMessage(error)}`);
213
+ }
214
+ }
215
+
216
+ /**
217
+ * Handles retrieving a single WordPress post by ID
218
+ */
219
+ export async function handleGetPost(client: WordPressClient, params: { id: number }): Promise<WordPressPost | string> {
220
+ try {
221
+ const postId = validateId(params.id, "post ID");
222
+ const post = await client.getPost(postId);
223
+
224
+ // Get additional metadata for comprehensive response
225
+ const [author, categories, tags] = await Promise.all([
226
+ // Get author information
227
+ post.author
228
+ ? client.getUser(post.author).catch(() => ({ name: `User ${post.author}`, username: `user${post.author}` }))
229
+ : null,
230
+ // Get categories
231
+ post.categories && post.categories.length > 0
232
+ ? Promise.all(post.categories.map((id) => client.getCategory(id).catch(() => ({ id, name: `Category ${id}` }))))
233
+ : [],
234
+ // Get tags
235
+ post.tags && post.tags.length > 0
236
+ ? Promise.all(post.tags.map((id) => client.getTag(id).catch(() => ({ id, name: `Tag ${id}` }))))
237
+ : [],
238
+ ]);
239
+
240
+ // Format post content
241
+ const date = new Date(post.date);
242
+ const modifiedDate = new Date(post.modified);
243
+ const formattedDate = date.toLocaleDateString("en-US", {
244
+ year: "numeric",
245
+ month: "short",
246
+ day: "numeric",
247
+ hour: "2-digit",
248
+ minute: "2-digit",
249
+ });
250
+ const formattedModified = modifiedDate.toLocaleDateString("en-US", {
251
+ year: "numeric",
252
+ month: "short",
253
+ day: "numeric",
254
+ hour: "2-digit",
255
+ minute: "2-digit",
256
+ });
257
+
258
+ const content = post.content?.rendered || "";
259
+ const excerpt = post.excerpt?.rendered ? sanitizeHtml(post.excerpt.rendered).trim() : "";
260
+ const wordCount = sanitizeHtml(content).split(/\s+/).filter(Boolean).length;
261
+
262
+ // Build comprehensive response
263
+ let response = `# ${post.title.rendered}\n\n`;
264
+ response += `**Post ID**: ${post.id}\n`;
265
+ response += `**Status**: ${post.status}\n`;
266
+ response += `**Author**: ${author?.name || author?.username || `User ${post.author}`}\n`;
267
+ response += `**Published**: ${formattedDate}\n`;
268
+ response += `**Modified**: ${formattedModified}\n`;
269
+
270
+ if (categories.length > 0) {
271
+ response += `**Categories**: ${categories.map((c) => c.name).join(", ")}\n`;
272
+ }
273
+
274
+ if (tags.length > 0) {
275
+ response += `**Tags**: ${tags.map((t) => t.name).join(", ")}\n`;
276
+ }
277
+
278
+ response += `**Word Count**: ${wordCount}\n`;
279
+ response += `**Link**: ${post.link}\n`;
280
+
281
+ if (excerpt) {
282
+ response += `\n## Excerpt\n${excerpt}\n`;
283
+ }
284
+
285
+ if (content) {
286
+ response += `\n## Content\n${content}\n`;
287
+ }
288
+
289
+ // Add management links and metadata
290
+ const siteUrl = client.getSiteUrl ? client.getSiteUrl() : "";
291
+ if (siteUrl) {
292
+ response += `\n## Management\n`;
293
+ response += `- **Edit**: ${siteUrl}/wp-admin/post.php?post=${post.id}&action=edit\n`;
294
+ response += `- **Preview**: ${siteUrl}/?p=${post.id}&preview=true\n`;
295
+ }
296
+
297
+ return response;
298
+ } catch (error) {
299
+ if (error instanceof Error && error.message.includes("404")) {
300
+ return `Post with ID ${params.id} not found. Please verify the ID and try again.`;
301
+ }
302
+ throw new Error(`Failed to get post: ${getErrorMessage(error)}`);
303
+ }
304
+ }
305
+
306
+ /**
307
+ * Handles creating a new WordPress post
308
+ */
309
+ export async function handleCreatePost(
310
+ client: WordPressClient,
311
+ params: CreatePostRequest,
312
+ ): Promise<WordPressPost | string> {
313
+ try {
314
+ validatePostParams(params);
315
+ const post = await client.createPost(params);
316
+
317
+ // Build success response with management links
318
+ let response = `✅ **Post Created Successfully**\n\n`;
319
+ response += `**Title**: ${post.title.rendered}\n`;
320
+ response += `**ID**: ${post.id}\n`;
321
+ response += `**Status**: ${post.status}\n`;
322
+ response += `**Link**: ${post.link}\n`;
323
+
324
+ // Add management links
325
+ const siteUrl = client.getSiteUrl ? client.getSiteUrl() : "";
326
+ if (siteUrl) {
327
+ response += `\n**Management**:\n`;
328
+ response += `- Edit: ${siteUrl}/wp-admin/post.php?post=${post.id}&action=edit\n`;
329
+ if (post.status === "publish") {
330
+ response += `- View: ${post.link}\n`;
331
+ } else {
332
+ response += `- Preview: ${siteUrl}/?p=${post.id}&preview=true\n`;
333
+ }
334
+ }
335
+
336
+ return response;
337
+ } catch (error) {
338
+ throw new Error(`Failed to create post: ${getErrorMessage(error)}`);
339
+ }
340
+ }
341
+
342
+ /**
343
+ * Handles updating an existing WordPress post
344
+ */
345
+ export async function handleUpdatePost(
346
+ client: WordPressClient,
347
+ params: UpdatePostRequest & { id: number },
348
+ ): Promise<WordPressPost | string> {
349
+ try {
350
+ const postId = validateId(params.id, "post ID");
351
+
352
+ // Get original post to show what changed
353
+ const originalPost = await client.getPost(postId);
354
+
355
+ const { id: _id, ...updateData } = params;
356
+ validatePostParams(updateData);
357
+
358
+ const updatedPost = await client.updatePost({ id: postId, ...updateData });
359
+
360
+ // Build change summary
361
+ let response = `✅ **Post Updated Successfully**\n\n`;
362
+ response += `**Title**: ${updatedPost.title.rendered}\n`;
363
+ response += `**ID**: ${updatedPost.id}\n`;
364
+ response += `**Status**: ${updatedPost.status}\n`;
365
+ response += `**Modified**: ${new Date(updatedPost.modified).toLocaleString()}\n`;
366
+
367
+ // Show what changed
368
+ const changes: string[] = [];
369
+ if (params.title && originalPost.title.rendered !== updatedPost.title.rendered) {
370
+ changes.push(`Title: "${originalPost.title.rendered}" → "${updatedPost.title.rendered}"`);
371
+ }
372
+ if (params.status && originalPost.status !== updatedPost.status) {
373
+ changes.push(`Status: "${originalPost.status}" → "${updatedPost.status}"`);
374
+ }
375
+ if (params.content && originalPost.content?.rendered !== updatedPost.content?.rendered) {
376
+ changes.push("Content updated");
377
+ }
378
+ if (params.excerpt && originalPost.excerpt?.rendered !== updatedPost.excerpt?.rendered) {
379
+ changes.push("Excerpt updated");
380
+ }
381
+
382
+ if (changes.length > 0) {
383
+ response += `\n**Changes Made**:\n${changes.map((c) => `- ${c}`).join("\n")}\n`;
384
+ }
385
+
386
+ response += `\n**Link**: ${updatedPost.link}`;
387
+
388
+ return response;
389
+ } catch (error) {
390
+ if (error instanceof Error && error.message.includes("404")) {
391
+ return `Post with ID ${params.id} not found. Please verify the ID and try again.`;
392
+ }
393
+ throw new Error(`Failed to update post: ${getErrorMessage(error)}`);
394
+ }
395
+ }
396
+
397
+ /**
398
+ * Handles deleting a WordPress post
399
+ */
400
+ export async function handleDeletePost(
401
+ client: WordPressClient,
402
+ params: { id: number; force?: boolean },
403
+ ): Promise<{ deleted: boolean; previous?: WordPressPost } | string> {
404
+ try {
405
+ const postId = validateId(params.id, "post ID");
406
+ const result = await client.deletePost(postId, params.force);
407
+
408
+ if (result.deleted) {
409
+ const action = params.force ? "permanently deleted" : "moved to trash";
410
+ let response = `✅ **Post ${action} successfully**\n\n`;
411
+
412
+ if (result.previous) {
413
+ response += `**Title**: ${result.previous.title.rendered}\n`;
414
+ response += `**ID**: ${result.previous.id}\n`;
415
+ }
416
+
417
+ if (!params.force) {
418
+ response += `\n**Note**: Post moved to trash. Use \`force=true\` to permanently delete.`;
419
+ }
420
+
421
+ return response;
422
+ } else {
423
+ return `Failed to delete post with ID ${params.id}. It may not exist or you may not have permission.`;
424
+ }
425
+ } catch (error) {
426
+ if (error instanceof Error && error.message.includes("404")) {
427
+ return `Post with ID ${params.id} not found. Please verify the ID and try again.`;
428
+ }
429
+ throw new Error(`Failed to delete post: ${getErrorMessage(error)}`);
430
+ }
431
+ }
432
+
433
+ /**
434
+ * Handles retrieving post revisions
435
+ */
436
+ export async function handleGetPostRevisions(
437
+ client: WordPressClient,
438
+ params: { id: number },
439
+ ): Promise<WordPressPost[] | string> {
440
+ try {
441
+ const postId = validateId(params.id, "post ID");
442
+ const revisions = await client.getPostRevisions(postId);
443
+
444
+ if (revisions.length === 0) {
445
+ return `No revisions found for post ${params.id}. This may be because revisions are disabled or the post has no revision history.`;
446
+ }
447
+
448
+ let response = `📚 **Post Revisions** (${revisions.length} total)\n\n`;
449
+
450
+ revisions.forEach((revision, index) => {
451
+ const date = new Date(revision.date);
452
+ const formattedDate = date.toLocaleDateString("en-US", {
453
+ year: "numeric",
454
+ month: "short",
455
+ day: "numeric",
456
+ hour: "2-digit",
457
+ minute: "2-digit",
458
+ });
459
+
460
+ response += `**Revision ${index + 1}**\n`;
461
+ response += `- ID: ${revision.id}\n`;
462
+ response += `- Date: ${formattedDate}\n`;
463
+ response += `- Title: ${revision.title.rendered}\n`;
464
+ if (index < revisions.length - 1) response += "\n";
465
+ });
466
+
467
+ return response;
468
+ } catch (error) {
469
+ if (error instanceof Error && error.message.includes("404")) {
470
+ return `Post with ID ${params.id} not found. Please verify the ID and try again.`;
471
+ }
472
+ throw new Error(`Failed to get post revisions: ${getErrorMessage(error)}`);
473
+ }
474
+ }