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
@@ -1,5 +1,6 @@
1
1
  import { WordPressClient } from "./api.js";
2
2
  import { WordPressClientConfig } from "../types/client.js";
3
+ import { LoggerFactory } from "../utils/logger.js";
3
4
  import type {
4
5
  WordPressPost,
5
6
  WordPressUser,
@@ -15,6 +16,7 @@ import type {
15
16
  */
16
17
  export class MockWordPressClient extends WordPressClient {
17
18
  private isMockMode = true;
19
+ private logger = LoggerFactory.api("mock");
18
20
 
19
21
  constructor(config: WordPressClientConfig) {
20
22
  super(config);
@@ -24,7 +26,7 @@ export class MockWordPressClient extends WordPressClient {
24
26
  * Override authenticate to not make actual requests
25
27
  */
26
28
  async authenticate(): Promise<boolean> {
27
- console.log("INFO: Mock authentication - skipping actual connection");
29
+ this.logger.info("Mock authentication - skipping actual connection");
28
30
  return true;
29
31
  }
30
32
 
@@ -332,7 +334,7 @@ export class MockWordPressClient extends WordPressClient {
332
334
  /**
333
335
  * Mock search
334
336
  */
335
- async search(query: string, types?: string[], subtype?: string): Promise<any[]> {
337
+ async search(query: string, types?: string[], subtype?: string): Promise<import("../types/wordpress.js").WordPressSearchResult[]> {
336
338
  if (!query) {
337
339
  return [];
338
340
  }
package/src/client/api.ts CHANGED
@@ -17,6 +17,7 @@ import type {
17
17
  ClientStats,
18
18
  } from "../types/client.js";
19
19
  import { WordPressAPIError, AuthenticationError, RateLimitError } from "../types/client.js";
20
+ import { config } from "../config/Config.js";
20
21
  import type {
21
22
  WordPressPost,
22
23
  WordPressPage,
@@ -45,16 +46,19 @@ import type {
45
46
  UpdateTagRequest,
46
47
  UploadMediaRequest,
47
48
  UpdateMediaRequest,
49
+ WordPressSiteInfo,
50
+ WordPressSearchResult,
48
51
  } from "../types/wordpress.js";
49
52
  import { debug, logError, startTimer } from "../utils/debug.js";
53
+ import type { QueuedRequest } from "../types/requests.js";
50
54
 
51
55
  /**
52
56
  * WordPress REST API Client
53
- *
57
+ *
54
58
  * A comprehensive client for interacting with the WordPress REST API v2.
55
59
  * Provides full CRUD operations for posts, pages, media, users, comments,
56
60
  * categories, tags, and site settings with robust error handling and performance optimization.
57
- *
61
+ *
58
62
  * Features:
59
63
  * - Multiple authentication methods (App Passwords, JWT, Basic Auth, API Key)
60
64
  * - Automatic retry logic with exponential backoff
@@ -63,7 +67,7 @@ import { debug, logError, startTimer } from "../utils/debug.js";
63
67
  * - Performance monitoring and request statistics
64
68
  * - Caching support for improved performance
65
69
  * - Multi-site configuration support
66
- *
70
+ *
67
71
  * @example
68
72
  * ```typescript
69
73
  * // Initialize with app password authentication
@@ -75,14 +79,14 @@ import { debug, logError, startTimer } from "../utils/debug.js";
75
79
  * password: 'xxxx xxxx xxxx xxxx xxxx xxxx'
76
80
  * }
77
81
  * });
78
- *
82
+ *
79
83
  * // Create a new post
80
84
  * const post = await client.createPost({
81
85
  * title: 'My New Post',
82
86
  * content: '<p>This is the content</p>',
83
87
  * status: 'publish'
84
88
  * });
85
- *
89
+ *
86
90
  * // List posts with filtering
87
91
  * const posts = await client.getPosts({
88
92
  * search: 'WordPress',
@@ -90,7 +94,7 @@ import { debug, logError, startTimer } from "../utils/debug.js";
90
94
  * per_page: 10
91
95
  * });
92
96
  * ```
93
- *
97
+ *
94
98
  * @since 1.0.0
95
99
  * @author MCP WordPress Team
96
100
  * @implements {IWordPressClient}
@@ -101,7 +105,7 @@ export class WordPressClient implements IWordPressClient {
101
105
  private timeout: number;
102
106
  private maxRetries: number;
103
107
  private auth: AuthConfig;
104
- private requestQueue: any[] = [];
108
+ private requestQueue: QueuedRequest[] = [];
105
109
  private lastRequestTime: number = 0;
106
110
  private requestInterval: number;
107
111
  private authenticated: boolean = false;
@@ -110,10 +114,10 @@ export class WordPressClient implements IWordPressClient {
110
114
 
111
115
  /**
112
116
  * Creates a new WordPress API client instance.
113
- *
117
+ *
114
118
  * Initializes the client with configuration options for connecting to a WordPress site.
115
119
  * Supports multiple authentication methods and automatic environment variable detection.
116
- *
120
+ *
117
121
  * @param {Partial<WordPressClientConfig>} [options={}] - Configuration options for the client
118
122
  * @param {string} [options.baseUrl] - WordPress site URL (falls back to WORDPRESS_SITE_URL env var)
119
123
  * @param {number} [options.timeout=30000] - Request timeout in milliseconds
@@ -121,7 +125,7 @@ export class WordPressClient implements IWordPressClient {
121
125
  * @param {AuthConfig} [options.auth] - Authentication configuration (auto-detected from env if not provided)
122
126
  * @param {boolean} [options.enableCache=true] - Whether to enable response caching
123
127
  * @param {number} [options.cacheMaxAge=300000] - Cache max age in milliseconds (5 minutes default)
124
- *
128
+ *
125
129
  * @example
126
130
  * ```typescript
127
131
  * // Basic configuration with app password
@@ -133,11 +137,11 @@ export class WordPressClient implements IWordPressClient {
133
137
  * password: 'xxxx xxxx xxxx xxxx xxxx xxxx'
134
138
  * }
135
139
  * });
136
- *
140
+ *
137
141
  * // Configuration with environment variables
138
142
  * // Set WORDPRESS_SITE_URL, WORDPRESS_USERNAME, WORDPRESS_APP_PASSWORD
139
143
  * const client = new WordPressClient(); // Auto-detects from env
140
- *
144
+ *
141
145
  * // Custom timeout and retry settings
142
146
  * const client = new WordPressClient({
143
147
  * baseUrl: 'https://mysite.com',
@@ -146,22 +150,26 @@ export class WordPressClient implements IWordPressClient {
146
150
  * auth: { method: 'app-password', username: 'user', password: 'pass' }
147
151
  * });
148
152
  * ```
149
- *
153
+ *
150
154
  * @throws {Error} When required configuration is missing or invalid
151
- *
155
+ *
152
156
  * @since 1.0.0
153
157
  */
154
158
  constructor(options: Partial<WordPressClientConfig> = {}) {
155
- this.baseUrl = options.baseUrl || process.env.WORDPRESS_SITE_URL || "";
159
+ const cfg = config();
160
+ const baseUrl = options.baseUrl || cfg.wordpress.siteUrl || "";
161
+
162
+ // Validate and sanitize base URL
163
+ this.baseUrl = this.validateAndSanitizeUrl(baseUrl);
156
164
  this.apiUrl = "";
157
- this.timeout = options.timeout || parseInt(process.env.WORDPRESS_TIMEOUT || "30000");
158
- this.maxRetries = options.maxRetries || parseInt(process.env.WORDPRESS_MAX_RETRIES || "3");
165
+ this.timeout = options.timeout || cfg.wordpress.timeout;
166
+ this.maxRetries = options.maxRetries || cfg.wordpress.maxRetries;
159
167
 
160
168
  // Authentication configuration
161
169
  this.auth = options.auth || this.getAuthFromEnv();
162
170
 
163
171
  // Rate limiting
164
- this.requestInterval = 60000 / parseInt(process.env.RATE_LIMIT || "60");
172
+ this.requestInterval = 60000 / cfg.security.rateLimit;
165
173
 
166
174
  // Initialize stats
167
175
  this._stats = {
@@ -198,58 +206,101 @@ export class WordPressClient implements IWordPressClient {
198
206
  return this.baseUrl;
199
207
  }
200
208
 
209
+ /**
210
+ * Validate and sanitize URL for security
211
+ */
212
+ private validateAndSanitizeUrl(url: string): string {
213
+ if (!url) {
214
+ throw new Error("WordPress site URL is required");
215
+ }
216
+
217
+ try {
218
+ const parsed = new URL(url);
219
+
220
+ // Only allow HTTP/HTTPS protocols
221
+ if (!["http:", "https:"].includes(parsed.protocol)) {
222
+ throw new Error("Only HTTP and HTTPS protocols are allowed");
223
+ }
224
+
225
+ // Prevent localhost/private IP access in production
226
+ if (config().app.isProduction) {
227
+ const hostname = parsed.hostname.toLowerCase();
228
+ if (
229
+ hostname === "localhost" ||
230
+ hostname === "127.0.0.1" ||
231
+ hostname === "::1" ||
232
+ hostname.match(/^10\./) ||
233
+ hostname.match(/^172\.(1[6-9]|2[0-9]|3[01])\./) ||
234
+ hostname.match(/^192\.168\./)
235
+ ) {
236
+ throw new Error("Private/localhost URLs not allowed in production");
237
+ }
238
+ }
239
+
240
+ // Return clean URL without query parameters or fragments
241
+ return `${parsed.protocol}//${parsed.host}${parsed.pathname}`.replace(/\/$/, "");
242
+ } catch (error) {
243
+ if (error instanceof TypeError) {
244
+ throw new Error("Invalid WordPress site URL format");
245
+ }
246
+ throw error;
247
+ }
248
+ }
249
+
201
250
  private getAuthFromEnv(): AuthConfig {
202
- const authMethod = process.env.WORDPRESS_AUTH_METHOD as AuthMethod;
251
+ const cfg = config();
252
+ const wp = cfg.wordpress;
253
+ const authMethod = wp.authMethod as AuthMethod;
203
254
 
204
255
  // Use explicit auth method if set
205
- if (authMethod === "app-password" && process.env.WORDPRESS_USERNAME && process.env.WORDPRESS_APP_PASSWORD) {
256
+ if (authMethod === "app-password" && wp.username && wp.appPassword) {
206
257
  return {
207
258
  method: "app-password",
208
- username: process.env.WORDPRESS_USERNAME,
209
- appPassword: process.env.WORDPRESS_APP_PASSWORD,
259
+ username: wp.username,
260
+ appPassword: wp.appPassword,
210
261
  };
211
262
  }
212
263
 
213
264
  // Try Application Password first (fallback)
214
- if (process.env.WORDPRESS_USERNAME && process.env.WORDPRESS_APP_PASSWORD) {
265
+ if (wp.username && wp.appPassword) {
215
266
  return {
216
267
  method: "app-password",
217
- username: process.env.WORDPRESS_USERNAME,
218
- appPassword: process.env.WORDPRESS_APP_PASSWORD,
268
+ username: wp.username,
269
+ appPassword: wp.appPassword,
219
270
  };
220
271
  }
221
272
 
222
273
  // Try JWT
223
- if (process.env.WORDPRESS_JWT_SECRET && process.env.WORDPRESS_USERNAME && process.env.WORDPRESS_PASSWORD) {
274
+ if (wp.jwtSecret && wp.username && wp.password) {
224
275
  return {
225
276
  method: "jwt",
226
- secret: process.env.WORDPRESS_JWT_SECRET,
227
- username: process.env.WORDPRESS_USERNAME,
228
- password: process.env.WORDPRESS_PASSWORD,
277
+ secret: wp.jwtSecret,
278
+ username: wp.username,
279
+ password: wp.password,
229
280
  };
230
281
  }
231
282
 
232
283
  // Try API Key
233
- if (process.env.WORDPRESS_API_KEY) {
284
+ if (wp.apiKey) {
234
285
  return {
235
286
  method: "api-key",
236
- apiKey: process.env.WORDPRESS_API_KEY,
287
+ apiKey: wp.apiKey,
237
288
  };
238
289
  }
239
290
 
240
291
  // Try Cookie
241
- if (process.env.WORDPRESS_COOKIE_NONCE) {
292
+ if (wp.cookieNonce) {
242
293
  return {
243
294
  method: "cookie",
244
- nonce: process.env.WORDPRESS_COOKIE_NONCE,
295
+ nonce: wp.cookieNonce,
245
296
  };
246
297
  }
247
298
 
248
299
  // Default to basic authentication
249
300
  return {
250
301
  method: "basic",
251
- username: process.env.WORDPRESS_USERNAME || "",
252
- password: process.env.WORDPRESS_PASSWORD || process.env.WORDPRESS_APP_PASSWORD || "",
302
+ username: wp.username || "",
303
+ password: wp.password || wp.appPassword || "",
253
304
  };
254
305
  }
255
306
 
@@ -442,10 +493,10 @@ export class WordPressClient implements IWordPressClient {
442
493
  /**
443
494
  * Make authenticated request to WordPress REST API
444
495
  */
445
- async request<T = any>(
496
+ async request<T = unknown>(
446
497
  method: HTTPMethod,
447
498
  endpoint: string,
448
- data: any = null,
499
+ data: unknown = null,
449
500
  options: RequestOptions = {},
450
501
  ): Promise<T> {
451
502
  const timer = startTimer();
@@ -469,7 +520,7 @@ export class WordPressClient implements IWordPressClient {
469
520
  const requestTimeout = options.timeout || this.timeout;
470
521
  const timeoutId = setTimeout(() => controller.abort(), requestTimeout);
471
522
 
472
- const fetchOptions: any = {
523
+ const fetchOptions: RequestInit & { headers: Record<string, string> } = {
473
524
  ...options, // Spread options first
474
525
  method,
475
526
  headers, // Headers come after to ensure auth headers aren't overridden
@@ -478,17 +529,21 @@ export class WordPressClient implements IWordPressClient {
478
529
 
479
530
  // Add body for POST/PUT/PATCH requests
480
531
  if (data && ["POST", "PUT", "PATCH"].includes(method)) {
481
- if (data instanceof FormData || (data && typeof data.append === "function")) {
532
+ if (
533
+ data instanceof FormData ||
534
+ (typeof data === "object" && data && "append" in data && typeof data.append === "function")
535
+ ) {
482
536
  // For FormData, check if it has getHeaders method (form-data package)
483
- if (data && typeof data.getHeaders === "function") {
537
+ if (typeof (data as { getHeaders?: () => Record<string, string> }).getHeaders === "function") {
484
538
  // Use headers from form-data package
485
- const formHeaders = data.getHeaders();
539
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
540
+ const formHeaders = (data as any).getHeaders();
486
541
  Object.assign(headers, formHeaders);
487
542
  } else {
488
543
  // For native FormData, don't set Content-Type (let fetch set it with boundary)
489
544
  delete headers["Content-Type"];
490
545
  }
491
- fetchOptions.body = data;
546
+ fetchOptions.body = data as FormData;
492
547
  } else if (Buffer.isBuffer(data)) {
493
548
  // For Buffer data (manual multipart), keep Content-Type from headers
494
549
  fetchOptions.body = data;
@@ -550,6 +605,54 @@ export class WordPressClient implements IWordPressClient {
550
605
  );
551
606
  }
552
607
 
608
+ // Fallback for 404 errors - try index.php approach for REST API
609
+ if (response.status === 404 && attempt === 0 && url.includes("/wp-json/wp/v2")) {
610
+ debug.log(`404 on pretty permalinks, trying index.php approach`);
611
+
612
+ // Parse the URL to handle query parameters correctly
613
+ const urlObj = new URL(url);
614
+ const endpoint = urlObj.pathname.replace("/wp-json/wp/v2", "");
615
+ const queryParams = urlObj.searchParams.toString();
616
+
617
+ let fallbackUrl = `${urlObj.origin}/index.php?rest_route=/wp/v2${endpoint}`;
618
+ if (queryParams) {
619
+ fallbackUrl += `&${queryParams}`;
620
+ }
621
+
622
+ try {
623
+ // Create a new timeout for the fallback request
624
+ const fallbackController = new AbortController();
625
+ const fallbackTimeoutId = setTimeout(() => {
626
+ fallbackController.abort();
627
+ }, requestTimeout);
628
+
629
+ const fallbackOptions = { ...fetchOptions, signal: fallbackController.signal };
630
+ const fallbackResponse = await fetch(fallbackUrl, fallbackOptions);
631
+ clearTimeout(fallbackTimeoutId);
632
+
633
+ if (fallbackResponse.ok) {
634
+ const responseText = await fallbackResponse.text();
635
+ if (!responseText) {
636
+ this._stats.successfulRequests++;
637
+ const duration = timer.end();
638
+ this.updateAverageResponseTime(duration);
639
+ return null as T;
640
+ }
641
+
642
+ const result = JSON.parse(responseText);
643
+ this._stats.successfulRequests++;
644
+ const duration = timer.end();
645
+ this.updateAverageResponseTime(duration);
646
+ return result;
647
+ } else {
648
+ // If fallback also fails, continue with original error
649
+ debug.log(`Fallback also failed with status ${fallbackResponse.status}`);
650
+ }
651
+ } catch (fallbackError) {
652
+ debug.log(`Fallback request failed: ${(fallbackError as Error).message}`);
653
+ }
654
+ }
655
+
553
656
  throw new WordPressAPIError(errorMessage, response.status);
554
657
  }
555
658
 
@@ -583,7 +686,7 @@ export class WordPressClient implements IWordPressClient {
583
686
  lastError = error as Error;
584
687
 
585
688
  // Handle timeout errors
586
- if ((error as any).name === "AbortError") {
689
+ if ((error as Error & { name?: string }).name === "AbortError") {
587
690
  lastError = new Error(`Request timeout after ${requestTimeout}ms`);
588
691
  }
589
692
 
@@ -623,23 +726,23 @@ export class WordPressClient implements IWordPressClient {
623
726
  }
624
727
 
625
728
  // HTTP method helpers
626
- async get<T = any>(endpoint: string, options?: RequestOptions): Promise<T> {
729
+ async get<T = unknown>(endpoint: string, options?: RequestOptions): Promise<T> {
627
730
  return this.request<T>("GET", endpoint, null, options);
628
731
  }
629
732
 
630
- async post<T = any>(endpoint: string, data?: any, options?: RequestOptions): Promise<T> {
733
+ async post<T = unknown>(endpoint: string, data?: unknown, options?: RequestOptions): Promise<T> {
631
734
  return this.request<T>("POST", endpoint, data, options);
632
735
  }
633
736
 
634
- async put<T = any>(endpoint: string, data?: any, options?: RequestOptions): Promise<T> {
737
+ async put<T = unknown>(endpoint: string, data?: unknown, options?: RequestOptions): Promise<T> {
635
738
  return this.request<T>("PUT", endpoint, data, options);
636
739
  }
637
740
 
638
- async patch<T = any>(endpoint: string, data?: any, options?: RequestOptions): Promise<T> {
741
+ async patch<T = unknown>(endpoint: string, data?: unknown, options?: RequestOptions): Promise<T> {
639
742
  return this.request<T>("PATCH", endpoint, data, options);
640
743
  }
641
744
 
642
- async delete<T = any>(endpoint: string, options?: RequestOptions): Promise<T> {
745
+ async delete<T = unknown>(endpoint: string, options?: RequestOptions): Promise<T> {
643
746
  return this.request<T>("DELETE", endpoint, null, options);
644
747
  }
645
748
 
@@ -647,7 +750,7 @@ export class WordPressClient implements IWordPressClient {
647
750
 
648
751
  // Posts
649
752
  async getPosts(params?: PostQueryParams): Promise<WordPressPost[]> {
650
- const queryString = params ? "?" + new URLSearchParams(params as any).toString() : "";
753
+ const queryString = params ? "?" + new URLSearchParams(params as Record<string, string>).toString() : "";
651
754
  return this.get<WordPressPost[]>(`posts${queryString}`);
652
755
  }
653
756
 
@@ -674,7 +777,10 @@ export class WordPressClient implements IWordPressClient {
674
777
 
675
778
  // Pages
676
779
  async getPages(params?: PostQueryParams): Promise<WordPressPage[]> {
677
- const queryString = params ? "?" + new URLSearchParams(params as any).toString() : "";
780
+ const normalizedParams = params
781
+ ? Object.fromEntries(Object.entries(params).map(([k, v]) => [k, String(v)]))
782
+ : undefined;
783
+ const queryString = normalizedParams ? "?" + new URLSearchParams(normalizedParams).toString() : "";
678
784
  return this.get<WordPressPage[]>(`pages${queryString}`);
679
785
  }
680
786
 
@@ -701,7 +807,10 @@ export class WordPressClient implements IWordPressClient {
701
807
 
702
808
  // Media
703
809
  async getMedia(params?: MediaQueryParams): Promise<WordPressMedia[]> {
704
- const queryString = params ? "?" + new URLSearchParams(params as any).toString() : "";
810
+ const normalizedParams = params
811
+ ? Object.fromEntries(Object.entries(params).map(([k, v]) => [k, String(v)]))
812
+ : undefined;
813
+ const queryString = normalizedParams ? "?" + new URLSearchParams(normalizedParams).toString() : "";
705
814
  return this.get<WordPressMedia[]>(`media${queryString}`);
706
815
  }
707
816
 
@@ -781,7 +890,10 @@ export class WordPressClient implements IWordPressClient {
781
890
 
782
891
  // Users
783
892
  async getUsers(params?: UserQueryParams): Promise<WordPressUser[]> {
784
- const queryString = params ? "?" + new URLSearchParams(params as any).toString() : "";
893
+ const normalizedParams = params
894
+ ? Object.fromEntries(Object.entries(params).map(([k, v]) => [k, String(v)]))
895
+ : undefined;
896
+ const queryString = normalizedParams ? "?" + new URLSearchParams(normalizedParams).toString() : "";
785
897
  return this.get<WordPressUser[]>(`users${queryString}`);
786
898
  }
787
899
 
@@ -809,7 +921,10 @@ export class WordPressClient implements IWordPressClient {
809
921
 
810
922
  // Comments
811
923
  async getComments(params?: CommentQueryParams): Promise<WordPressComment[]> {
812
- const queryString = params ? "?" + new URLSearchParams(params as any).toString() : "";
924
+ const normalizedParams = params
925
+ ? Object.fromEntries(Object.entries(params).map(([k, v]) => [k, String(v)]))
926
+ : undefined;
927
+ const queryString = normalizedParams ? "?" + new URLSearchParams(normalizedParams).toString() : "";
813
928
  return this.get<WordPressComment[]>(`comments${queryString}`);
814
929
  }
815
930
 
@@ -845,8 +960,11 @@ export class WordPressClient implements IWordPressClient {
845
960
  }
846
961
 
847
962
  // Taxonomies
848
- async getCategories(params?: any): Promise<WordPressCategory[]> {
849
- const queryString = params ? "?" + new URLSearchParams(params).toString() : "";
963
+ async getCategories(params?: Record<string, string | number | boolean>): Promise<WordPressCategory[]> {
964
+ const normalizedParams = params
965
+ ? Object.fromEntries(Object.entries(params).map(([k, v]) => [k, String(v)]))
966
+ : undefined;
967
+ const queryString = normalizedParams ? "?" + new URLSearchParams(normalizedParams).toString() : "";
850
968
  return this.get<WordPressCategory[]>(`categories${queryString}`);
851
969
  }
852
970
 
@@ -867,8 +985,11 @@ export class WordPressClient implements IWordPressClient {
867
985
  return this.delete(`categories/${id}?force=${force}`);
868
986
  }
869
987
 
870
- async getTags(params?: any): Promise<WordPressTag[]> {
871
- const queryString = params ? "?" + new URLSearchParams(params).toString() : "";
988
+ async getTags(params?: Record<string, string | number | boolean>): Promise<WordPressTag[]> {
989
+ const normalizedParams = params
990
+ ? Object.fromEntries(Object.entries(params).map(([k, v]) => [k, String(v)]))
991
+ : undefined;
992
+ const queryString = normalizedParams ? "?" + new URLSearchParams(normalizedParams).toString() : "";
872
993
  return this.get<WordPressTag[]>(`tags${queryString}`);
873
994
  }
874
995
 
@@ -898,7 +1019,7 @@ export class WordPressClient implements IWordPressClient {
898
1019
  return this.post<WordPressSiteSettings>("settings", settings);
899
1020
  }
900
1021
 
901
- async getSiteInfo(): Promise<any> {
1022
+ async getSiteInfo(): Promise<WordPressSiteInfo> {
902
1023
  return this.get("");
903
1024
  }
904
1025
 
@@ -912,7 +1033,7 @@ export class WordPressClient implements IWordPressClient {
912
1033
  name: string,
913
1034
  appId?: string,
914
1035
  ): Promise<WordPressApplicationPassword> {
915
- const data: any = { name };
1036
+ const data: Record<string, unknown> = { name };
916
1037
  if (appId) data.app_id = appId;
917
1038
  return this.post<WordPressApplicationPassword>(`users/${userId}/application-passwords`, data);
918
1039
  }
@@ -922,12 +1043,12 @@ export class WordPressClient implements IWordPressClient {
922
1043
  }
923
1044
 
924
1045
  // Search
925
- async search(query: string, types?: string[], subtype?: string): Promise<any[]> {
1046
+ async search(query: string, types?: string[], subtype?: string): Promise<WordPressSearchResult[]> {
926
1047
  const params = new URLSearchParams({ search: query });
927
1048
  if (types) params.append("type", types.join(","));
928
1049
  if (subtype) params.append("subtype", subtype);
929
1050
 
930
- return this.get<any[]>(`search?${params.toString()}`);
1051
+ return this.get<WordPressSearchResult[]>(`search?${params.toString()}`);
931
1052
  }
932
1053
 
933
1054
  // Utility Methods
@@ -940,7 +1061,7 @@ export class WordPressClient implements IWordPressClient {
940
1061
  }
941
1062
  }
942
1063
 
943
- async getServerInfo(): Promise<Record<string, any>> {
1064
+ async getServerInfo(): Promise<Record<string, unknown>> {
944
1065
  return this.get("");
945
1066
  }
946
1067
 
@@ -948,10 +1069,11 @@ export class WordPressClient implements IWordPressClient {
948
1069
  return /^[a-zA-Z0-9\/\-_]+$/.test(endpoint);
949
1070
  }
950
1071
 
951
- buildUrl(endpoint: string, params?: Record<string, any>): string {
1072
+ buildUrl(endpoint: string, params?: Record<string, unknown>): string {
952
1073
  const url = `${this.apiUrl}/${endpoint.replace(/^\/+/, "")}`;
953
1074
  if (params) {
954
- const searchParams = new URLSearchParams(params);
1075
+ const normalizedParams = Object.fromEntries(Object.entries(params).map(([k, v]) => [k, String(v)]));
1076
+ const searchParams = new URLSearchParams(normalizedParams);
955
1077
  return `${url}?${searchParams.toString()}`;
956
1078
  }
957
1079
  return url;