mcp-wordpress 2.6.4 → 2.8.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 (332) hide show
  1. package/README.md +1 -1
  2. package/dist/cache/CacheInvalidation.d.ts +25 -6
  3. package/dist/cache/CacheInvalidation.d.ts.map +1 -1
  4. package/dist/cache/CacheInvalidation.js +168 -16
  5. package/dist/cache/CacheInvalidation.js.map +1 -1
  6. package/dist/cache/HttpCacheWrapper.d.ts.map +1 -1
  7. package/dist/cache/HttpCacheWrapper.js +3 -4
  8. package/dist/cache/HttpCacheWrapper.js.map +1 -1
  9. package/dist/cache/SEOCacheManager.d.ts +150 -0
  10. package/dist/cache/SEOCacheManager.d.ts.map +1 -0
  11. package/dist/cache/SEOCacheManager.js +275 -0
  12. package/dist/cache/SEOCacheManager.js.map +1 -0
  13. package/dist/client/SEOWordPressClient.d.ts +164 -0
  14. package/dist/client/SEOWordPressClient.d.ts.map +1 -0
  15. package/dist/client/SEOWordPressClient.js +674 -0
  16. package/dist/client/SEOWordPressClient.js.map +1 -0
  17. package/dist/client/api.d.ts.map +1 -1
  18. package/dist/client/api.js +50 -20
  19. package/dist/client/api.js.map +1 -1
  20. package/dist/client/auth.js +19 -19
  21. package/dist/client/auth.js.map +1 -1
  22. package/dist/client/index.d.ts +11 -0
  23. package/dist/client/index.d.ts.map +1 -0
  24. package/dist/client/index.js +14 -0
  25. package/dist/client/index.js.map +1 -0
  26. package/dist/client/managers/AuthManager.d.ts +39 -0
  27. package/dist/client/managers/AuthManager.d.ts.map +1 -0
  28. package/dist/client/managers/AuthManager.js +142 -0
  29. package/dist/client/managers/AuthManager.js.map +1 -0
  30. package/dist/client/managers/AuthenticationManager.d.ts.map +1 -1
  31. package/dist/client/managers/AuthenticationManager.js +10 -9
  32. package/dist/client/managers/AuthenticationManager.js.map +1 -1
  33. package/dist/client/managers/BaseManager.d.ts.map +1 -1
  34. package/dist/client/managers/BaseManager.js +12 -0
  35. package/dist/client/managers/BaseManager.js.map +1 -1
  36. package/dist/client/managers/ComposedAuthenticationManager.d.ts +94 -0
  37. package/dist/client/managers/ComposedAuthenticationManager.d.ts.map +1 -0
  38. package/dist/client/managers/ComposedAuthenticationManager.js +340 -0
  39. package/dist/client/managers/ComposedAuthenticationManager.js.map +1 -0
  40. package/dist/client/managers/ComposedManagerFactory.d.ts +104 -0
  41. package/dist/client/managers/ComposedManagerFactory.d.ts.map +1 -0
  42. package/dist/client/managers/ComposedManagerFactory.js +180 -0
  43. package/dist/client/managers/ComposedManagerFactory.js.map +1 -0
  44. package/dist/client/managers/ComposedRequestManager.d.ts +82 -0
  45. package/dist/client/managers/ComposedRequestManager.d.ts.map +1 -0
  46. package/dist/client/managers/ComposedRequestManager.js +260 -0
  47. package/dist/client/managers/ComposedRequestManager.js.map +1 -0
  48. package/dist/client/managers/JWTAuthImplementation.d.ts +86 -0
  49. package/dist/client/managers/JWTAuthImplementation.d.ts.map +1 -0
  50. package/dist/client/managers/JWTAuthImplementation.js +240 -0
  51. package/dist/client/managers/JWTAuthImplementation.js.map +1 -0
  52. package/dist/client/managers/ManagersIndex.d.ts +6 -0
  53. package/dist/client/managers/ManagersIndex.d.ts.map +1 -0
  54. package/dist/client/managers/ManagersIndex.js +6 -0
  55. package/dist/client/managers/ManagersIndex.js.map +1 -0
  56. package/dist/client/managers/RequestManager.d.ts.map +1 -1
  57. package/dist/client/managers/RequestManager.js +5 -3
  58. package/dist/client/managers/RequestManager.js.map +1 -1
  59. package/dist/client/managers/composed/MigrationAdapter.d.ts +80 -0
  60. package/dist/client/managers/composed/MigrationAdapter.d.ts.map +1 -0
  61. package/dist/client/managers/composed/MigrationAdapter.js +214 -0
  62. package/dist/client/managers/composed/MigrationAdapter.js.map +1 -0
  63. package/dist/client/managers/composed/index.d.ts +23 -0
  64. package/dist/client/managers/composed/index.d.ts.map +1 -0
  65. package/dist/client/managers/composed/index.js +26 -0
  66. package/dist/client/managers/composed/index.js.map +1 -0
  67. package/dist/client/managers/implementations/ConfigurationProviderImpl.d.ts +27 -0
  68. package/dist/client/managers/implementations/ConfigurationProviderImpl.d.ts.map +1 -0
  69. package/dist/client/managers/implementations/ConfigurationProviderImpl.js +41 -0
  70. package/dist/client/managers/implementations/ConfigurationProviderImpl.js.map +1 -0
  71. package/dist/client/managers/implementations/ErrorHandlerImpl.d.ts +31 -0
  72. package/dist/client/managers/implementations/ErrorHandlerImpl.d.ts.map +1 -0
  73. package/dist/client/managers/implementations/ErrorHandlerImpl.js +73 -0
  74. package/dist/client/managers/implementations/ErrorHandlerImpl.js.map +1 -0
  75. package/dist/client/managers/implementations/ParameterValidatorImpl.d.ts +47 -0
  76. package/dist/client/managers/implementations/ParameterValidatorImpl.d.ts.map +1 -0
  77. package/dist/client/managers/implementations/ParameterValidatorImpl.js +141 -0
  78. package/dist/client/managers/implementations/ParameterValidatorImpl.js.map +1 -0
  79. package/dist/client/managers/interfaces/ManagerInterfaces.d.ts +147 -0
  80. package/dist/client/managers/interfaces/ManagerInterfaces.d.ts.map +1 -0
  81. package/dist/client/managers/interfaces/ManagerInterfaces.js +6 -0
  82. package/dist/client/managers/interfaces/ManagerInterfaces.js.map +1 -0
  83. package/dist/config/Config.d.ts +30 -0
  84. package/dist/config/Config.d.ts.map +1 -1
  85. package/dist/config/Config.js +30 -0
  86. package/dist/config/Config.js.map +1 -1
  87. package/dist/config/ConfigurationSchema.d.ts +75 -198
  88. package/dist/config/ConfigurationSchema.d.ts.map +1 -1
  89. package/dist/config/ConfigurationSchema.js +17 -17
  90. package/dist/config/ConfigurationSchema.js.map +1 -1
  91. package/dist/config/ServerConfiguration.d.ts +2 -2
  92. package/dist/config/ServerConfiguration.d.ts.map +1 -1
  93. package/dist/config/ServerConfiguration.js +15 -13
  94. package/dist/config/ServerConfiguration.js.map +1 -1
  95. package/dist/config/index.d.ts +8 -0
  96. package/dist/config/index.d.ts.map +1 -0
  97. package/dist/config/index.js +11 -0
  98. package/dist/config/index.js.map +1 -0
  99. package/dist/docs/DocumentationGenerator.js +2 -2
  100. package/dist/docs/DocumentationGenerator.js.map +1 -1
  101. package/dist/dxt-entry.js +3 -3
  102. package/dist/dxt-entry.js.map +1 -1
  103. package/dist/index.d.ts +1 -0
  104. package/dist/index.d.ts.map +1 -1
  105. package/dist/index.js +38 -37
  106. package/dist/index.js.map +1 -1
  107. package/dist/performance/MetricsCollector.d.ts.map +1 -1
  108. package/dist/performance/MetricsCollector.js +5 -4
  109. package/dist/performance/MetricsCollector.js.map +1 -1
  110. package/dist/security/AISecurityScanner.js +7 -7
  111. package/dist/security/AISecurityScanner.js.map +1 -1
  112. package/dist/security/AutomatedRemediation.d.ts.map +1 -1
  113. package/dist/security/AutomatedRemediation.js +11 -11
  114. package/dist/security/AutomatedRemediation.js.map +1 -1
  115. package/dist/security/InputValidator.d.ts +50 -126
  116. package/dist/security/InputValidator.d.ts.map +1 -1
  117. package/dist/security/InputValidator.js +9 -9
  118. package/dist/security/InputValidator.js.map +1 -1
  119. package/dist/security/SecurityCIPipeline.d.ts +47 -5
  120. package/dist/security/SecurityCIPipeline.d.ts.map +1 -1
  121. package/dist/security/SecurityCIPipeline.js +409 -50
  122. package/dist/security/SecurityCIPipeline.js.map +1 -1
  123. package/dist/security/SecurityConfigManager.js +10 -10
  124. package/dist/security/SecurityConfigManager.js.map +1 -1
  125. package/dist/security/SecurityMonitoring.js +4 -4
  126. package/dist/security/SecurityMonitoring.js.map +1 -1
  127. package/dist/security/SecurityReviewer.d.ts.map +1 -1
  128. package/dist/security/SecurityReviewer.js +13 -6
  129. package/dist/security/SecurityReviewer.js.map +1 -1
  130. package/dist/security/index.js +3 -3
  131. package/dist/security/index.js.map +1 -1
  132. package/dist/server/ConnectionTester.js +5 -5
  133. package/dist/server/ConnectionTester.js.map +1 -1
  134. package/dist/server/ToolRegistry.d.ts +2 -2
  135. package/dist/server/ToolRegistry.d.ts.map +1 -1
  136. package/dist/server/ToolRegistry.js +7 -6
  137. package/dist/server/ToolRegistry.js.map +1 -1
  138. package/dist/server/index.d.ts +7 -0
  139. package/dist/server/index.d.ts.map +1 -0
  140. package/dist/server/index.js +9 -0
  141. package/dist/server/index.js.map +1 -0
  142. package/dist/tools/BaseToolManager.d.ts.map +1 -1
  143. package/dist/tools/BaseToolManager.js +11 -11
  144. package/dist/tools/BaseToolManager.js.map +1 -1
  145. package/dist/tools/auth.d.ts.map +1 -1
  146. package/dist/tools/auth.js +7 -7
  147. package/dist/tools/auth.js.map +1 -1
  148. package/dist/tools/comments.d.ts.map +1 -1
  149. package/dist/tools/comments.js +14 -14
  150. package/dist/tools/comments.js.map +1 -1
  151. package/dist/tools/index.d.ts +1 -0
  152. package/dist/tools/index.d.ts.map +1 -1
  153. package/dist/tools/index.js +1 -0
  154. package/dist/tools/index.js.map +1 -1
  155. package/dist/tools/media.d.ts.map +1 -1
  156. package/dist/tools/media.js +14 -11
  157. package/dist/tools/media.js.map +1 -1
  158. package/dist/tools/pages.d.ts.map +1 -1
  159. package/dist/tools/pages.js +12 -12
  160. package/dist/tools/pages.js.map +1 -1
  161. package/dist/tools/performance.d.ts.map +1 -1
  162. package/dist/tools/performance.js +13 -11
  163. package/dist/tools/performance.js.map +1 -1
  164. package/dist/tools/posts/PostHandlers.d.ts.map +1 -1
  165. package/dist/tools/posts/PostHandlers.js +16 -16
  166. package/dist/tools/posts/PostHandlers.js.map +1 -1
  167. package/dist/tools/posts/PostToolDefinitions.d.ts.map +1 -1
  168. package/dist/tools/posts/index.d.ts.map +1 -1
  169. package/dist/tools/seo/BulkOperations.d.ts +113 -0
  170. package/dist/tools/seo/BulkOperations.d.ts.map +1 -0
  171. package/dist/tools/seo/BulkOperations.js +398 -0
  172. package/dist/tools/seo/BulkOperations.js.map +1 -0
  173. package/dist/tools/seo/SEOHandlers.d.ts +55 -0
  174. package/dist/tools/seo/SEOHandlers.d.ts.map +1 -0
  175. package/dist/tools/seo/SEOHandlers.js +255 -0
  176. package/dist/tools/seo/SEOHandlers.js.map +1 -0
  177. package/dist/tools/seo/SEOToolDefinitions.d.ts +59 -0
  178. package/dist/tools/seo/SEOToolDefinitions.d.ts.map +1 -0
  179. package/dist/tools/seo/SEOToolDefinitions.js +385 -0
  180. package/dist/tools/seo/SEOToolDefinitions.js.map +1 -0
  181. package/dist/tools/seo/SEOTools.d.ts +203 -0
  182. package/dist/tools/seo/SEOTools.d.ts.map +1 -0
  183. package/dist/tools/seo/SEOTools.js +708 -0
  184. package/dist/tools/seo/SEOTools.js.map +1 -0
  185. package/dist/tools/seo/analyzers/ContentAnalyzer.d.ts +94 -0
  186. package/dist/tools/seo/analyzers/ContentAnalyzer.d.ts.map +1 -0
  187. package/dist/tools/seo/analyzers/ContentAnalyzer.js +402 -0
  188. package/dist/tools/seo/analyzers/ContentAnalyzer.js.map +1 -0
  189. package/dist/tools/seo/auditors/SiteAuditor.d.ts +121 -0
  190. package/dist/tools/seo/auditors/SiteAuditor.d.ts.map +1 -0
  191. package/dist/tools/seo/auditors/SiteAuditor.js +600 -0
  192. package/dist/tools/seo/auditors/SiteAuditor.js.map +1 -0
  193. package/dist/tools/seo/generators/MetaGenerator.d.ts +128 -0
  194. package/dist/tools/seo/generators/MetaGenerator.d.ts.map +1 -0
  195. package/dist/tools/seo/generators/MetaGenerator.js +547 -0
  196. package/dist/tools/seo/generators/MetaGenerator.js.map +1 -0
  197. package/dist/tools/seo/generators/SchemaGenerator.d.ts +204 -0
  198. package/dist/tools/seo/generators/SchemaGenerator.d.ts.map +1 -0
  199. package/dist/tools/seo/generators/SchemaGenerator.js +670 -0
  200. package/dist/tools/seo/generators/SchemaGenerator.js.map +1 -0
  201. package/dist/tools/seo/index.d.ts +17 -0
  202. package/dist/tools/seo/index.d.ts.map +1 -0
  203. package/dist/tools/seo/index.js +18 -0
  204. package/dist/tools/seo/index.js.map +1 -0
  205. package/dist/tools/seo/optimizers/InternalLinkingSuggester.d.ts +186 -0
  206. package/dist/tools/seo/optimizers/InternalLinkingSuggester.d.ts.map +1 -0
  207. package/dist/tools/seo/optimizers/InternalLinkingSuggester.js +683 -0
  208. package/dist/tools/seo/optimizers/InternalLinkingSuggester.js.map +1 -0
  209. package/dist/tools/site.d.ts.map +1 -1
  210. package/dist/tools/site.js +12 -12
  211. package/dist/tools/site.js.map +1 -1
  212. package/dist/tools/taxonomies.d.ts.map +1 -1
  213. package/dist/tools/taxonomies.js +20 -20
  214. package/dist/tools/taxonomies.js.map +1 -1
  215. package/dist/tools/users.d.ts.map +1 -1
  216. package/dist/tools/users.js +12 -12
  217. package/dist/tools/users.js.map +1 -1
  218. package/dist/types/client.d.ts +8 -6
  219. package/dist/types/client.d.ts.map +1 -1
  220. package/dist/types/client.js.map +1 -1
  221. package/dist/types/seo.d.ts +473 -0
  222. package/dist/types/seo.d.ts.map +1 -0
  223. package/dist/types/seo.js +94 -0
  224. package/dist/types/seo.js.map +1 -0
  225. package/dist/utils/enhancedError.js +1 -1
  226. package/dist/utils/enhancedError.js.map +1 -1
  227. package/dist/utils/error.d.ts.map +1 -1
  228. package/dist/utils/error.js +0 -1
  229. package/dist/utils/error.js.map +1 -1
  230. package/dist/utils/index.d.ts +12 -0
  231. package/dist/utils/index.d.ts.map +1 -0
  232. package/dist/utils/index.js +18 -0
  233. package/dist/utils/index.js.map +1 -0
  234. package/dist/utils/logger.js +3 -3
  235. package/dist/utils/logger.js.map +1 -1
  236. package/dist/utils/toolWrapper.d.ts +2 -2
  237. package/dist/utils/toolWrapper.js +8 -8
  238. package/dist/utils/toolWrapper.js.map +1 -1
  239. package/dist/utils/validation/core.d.ts.map +1 -1
  240. package/dist/utils/validation/core.js.map +1 -1
  241. package/dist/utils/validation/index.d.ts.map +1 -1
  242. package/dist/utils/validation/index.js.map +1 -1
  243. package/dist/utils/validation/network.js +3 -3
  244. package/dist/utils/validation/network.js.map +1 -1
  245. package/dist/utils/validation/rateLimit.js.map +1 -1
  246. package/dist/utils/validation/security.js.map +1 -1
  247. package/dist/utils/validation/wordpress.js.map +1 -1
  248. package/dist/utils/version.d.ts +144 -0
  249. package/dist/utils/version.d.ts.map +1 -0
  250. package/dist/utils/version.js +318 -0
  251. package/dist/utils/version.js.map +1 -0
  252. package/package.json +21 -55
  253. package/src/cache/CacheInvalidation.ts +183 -20
  254. package/src/cache/HttpCacheWrapper.ts +8 -5
  255. package/src/cache/SEOCacheManager.ts +330 -0
  256. package/src/cache/__tests__/CacheInvalidation.test.ts +6 -11
  257. package/src/cache/__tests__/CachedWordPressClient.test.ts +37 -62
  258. package/src/client/SEOWordPressClient.ts +876 -0
  259. package/src/client/api.ts +50 -21
  260. package/src/client/auth.ts +19 -19
  261. package/src/client/index.ts +16 -0
  262. package/src/client/managers/AuthManager.ts +175 -0
  263. package/src/client/managers/AuthenticationManager.ts +16 -14
  264. package/src/client/managers/BaseManager.ts +24 -5
  265. package/src/client/managers/ComposedAuthenticationManager.ts +409 -0
  266. package/src/client/managers/ComposedManagerFactory.ts +231 -0
  267. package/src/client/managers/ComposedRequestManager.ts +336 -0
  268. package/src/client/managers/JWTAuthImplementation.ts +326 -0
  269. package/src/client/managers/ManagersIndex.ts +6 -0
  270. package/src/client/managers/RequestManager.ts +9 -7
  271. package/src/client/managers/composed/MigrationAdapter.ts +263 -0
  272. package/src/client/managers/composed/index.ts +47 -0
  273. package/src/client/managers/implementations/ConfigurationProviderImpl.ts +52 -0
  274. package/src/client/managers/implementations/ErrorHandlerImpl.ts +102 -0
  275. package/src/client/managers/implementations/ParameterValidatorImpl.ts +221 -0
  276. package/src/client/managers/interfaces/ManagerInterfaces.ts +171 -0
  277. package/src/config/Config.ts +63 -0
  278. package/src/config/ConfigurationSchema.ts +17 -17
  279. package/src/config/ServerConfiguration.ts +18 -16
  280. package/src/config/index.ts +13 -0
  281. package/src/docs/DocumentationGenerator.ts +2 -2
  282. package/src/dxt-entry.ts +3 -3
  283. package/src/index.ts +43 -43
  284. package/src/performance/MetricsCollector.ts +15 -11
  285. package/src/security/AISecurityScanner.ts +7 -7
  286. package/src/security/AutomatedRemediation.ts +13 -11
  287. package/src/security/InputValidator.ts +10 -9
  288. package/src/security/SecurityCIPipeline.ts +514 -57
  289. package/src/security/SecurityConfigManager.ts +10 -10
  290. package/src/security/SecurityMonitoring.ts +5 -5
  291. package/src/security/SecurityReviewer.ts +13 -6
  292. package/src/security/index.ts +3 -3
  293. package/src/server/ConnectionTester.ts +5 -5
  294. package/src/server/ToolRegistry.ts +9 -8
  295. package/src/server/index.ts +10 -0
  296. package/src/tools/BaseToolManager.ts +55 -83
  297. package/src/tools/auth.ts +21 -12
  298. package/src/tools/comments.ts +23 -19
  299. package/src/tools/index.ts +1 -0
  300. package/src/tools/media.ts +23 -20
  301. package/src/tools/pages.ts +20 -13
  302. package/src/tools/performance.ts +101 -32
  303. package/src/tools/posts/PostHandlers.ts +23 -23
  304. package/src/tools/posts/PostToolDefinitions.ts +1 -1
  305. package/src/tools/posts/index.ts +2 -2
  306. package/src/tools/seo/BulkOperations.ts +557 -0
  307. package/src/tools/seo/SEOHandlers.ts +296 -0
  308. package/src/tools/seo/SEOToolDefinitions.ts +402 -0
  309. package/src/tools/seo/SEOTools.ts +871 -0
  310. package/src/tools/seo/analyzers/ContentAnalyzer.ts +493 -0
  311. package/src/tools/seo/auditors/SiteAuditor.ts +787 -0
  312. package/src/tools/seo/generators/MetaGenerator.ts +694 -0
  313. package/src/tools/seo/generators/SchemaGenerator.ts +955 -0
  314. package/src/tools/seo/index.ts +47 -0
  315. package/src/tools/seo/optimizers/InternalLinkingSuggester.ts +934 -0
  316. package/src/tools/site.ts +27 -26
  317. package/src/tools/taxonomies.ts +29 -25
  318. package/src/tools/users.ts +20 -13
  319. package/src/types/client.ts +8 -6
  320. package/src/types/seo.ts +546 -0
  321. package/src/utils/enhancedError.ts +1 -1
  322. package/src/utils/error.ts +1 -2
  323. package/src/utils/index.ts +23 -0
  324. package/src/utils/logger.ts +3 -3
  325. package/src/utils/toolWrapper.ts +10 -10
  326. package/src/utils/validation/core.ts +2 -2
  327. package/src/utils/validation/index.ts +2 -2
  328. package/src/utils/validation/network.ts +5 -5
  329. package/src/utils/validation/rateLimit.ts +1 -1
  330. package/src/utils/validation/security.ts +1 -1
  331. package/src/utils/validation/wordpress.ts +1 -1
  332. package/src/utils/version.ts +402 -0
@@ -0,0 +1,674 @@
1
+ /**
2
+ * SEO WordPress Client
3
+ *
4
+ * Extended WordPress REST API client with enhanced SEO capabilities.
5
+ * Provides specialized methods for SEO metadata management, schema markup,
6
+ * and integration with popular WordPress SEO plugins like Yoast and RankMath.
7
+ *
8
+ * Features:
9
+ * - SEO metadata retrieval and updates via REST API
10
+ * - Integration with Yoast SEO and RankMath plugin APIs
11
+ * - Schema markup management through WordPress custom fields
12
+ * - Bulk SEO operations with progress tracking
13
+ * - SEO-specific data normalization and validation
14
+ * - Plugin-agnostic metadata handling
15
+ *
16
+ * @since 2.7.0
17
+ */
18
+ import { WordPressClient } from "./api.js";
19
+ import { LoggerFactory } from "../utils/logger.js";
20
+ import { handleToolError } from "../utils/error.js";
21
+ /**
22
+ * Extended WordPress client with SEO capabilities
23
+ */
24
+ export class SEOWordPressClient extends WordPressClient {
25
+ logger = LoggerFactory.tool("seo_client");
26
+ detectedPlugin = "none";
27
+ pluginFields;
28
+ constructor(config) {
29
+ super(config);
30
+ // Define field mappings for different SEO plugins
31
+ this.pluginFields = {
32
+ yoast: {
33
+ title: "_yoast_wpseo_title",
34
+ description: "_yoast_wpseo_metadesc",
35
+ focusKeyword: "_yoast_wpseo_focuskw",
36
+ canonical: "_yoast_wpseo_canonical",
37
+ noindex: "_yoast_wpseo_meta-robots-noindex",
38
+ nofollow: "_yoast_wpseo_meta-robots-nofollow",
39
+ schema: "_yoast_wpseo_schema_page_type",
40
+ },
41
+ rankmath: {
42
+ title: "rank_math_title",
43
+ description: "rank_math_description",
44
+ focusKeyword: "rank_math_focus_keyword",
45
+ canonical: "rank_math_canonical_url",
46
+ robots: "rank_math_robots",
47
+ schema: "rank_math_rich_snippet",
48
+ },
49
+ seopress: {
50
+ title: "_seopress_titles_title",
51
+ description: "_seopress_titles_desc",
52
+ targetKeyword: "_seopress_analysis_target_kw",
53
+ canonical: "_seopress_robots_canonical",
54
+ robots: "_seopress_robots_index",
55
+ },
56
+ none: {
57
+ title: "meta_title",
58
+ description: "meta_description",
59
+ focusKeyword: "focus_keyword",
60
+ canonical: "canonical_url",
61
+ noindex: "robots_noindex",
62
+ nofollow: "robots_nofollow",
63
+ schema: "schema_markup",
64
+ },
65
+ };
66
+ }
67
+ /**
68
+ * Initialize and detect SEO plugins
69
+ */
70
+ async initializeSEO() {
71
+ this.logger.debug("Initializing SEO client and detecting plugins");
72
+ try {
73
+ // Detect active SEO plugins
74
+ await this.detectSEOPlugins();
75
+ this.logger.info("SEO client initialized", {
76
+ detectedPlugin: this.detectedPlugin,
77
+ });
78
+ }
79
+ catch (_error) {
80
+ this.logger.warn("SEO plugin detection failed, using generic approach", {
81
+ _error: _error instanceof Error ? _error.message : String(_error),
82
+ });
83
+ }
84
+ }
85
+ /**
86
+ * Detect active SEO plugins on the WordPress site
87
+ */
88
+ async detectSEOPlugins() {
89
+ try {
90
+ // Get list of installed plugins
91
+ const plugins = await this.get("/wp/v2/plugins");
92
+ if (!Array.isArray(plugins)) {
93
+ this.logger.debug("Could not retrieve plugins list");
94
+ return;
95
+ }
96
+ // Check for active SEO plugins in order of preference
97
+ const activePlugins = plugins.filter((plugin) => plugin.status === "active");
98
+ // Check for Yoast SEO
99
+ if (activePlugins.some((plugin) => plugin.slug === "wordpress-seo")) {
100
+ this.detectedPlugin = "yoast";
101
+ return;
102
+ }
103
+ // Check for RankMath
104
+ if (activePlugins.some((plugin) => plugin.slug === "seo-by-rank-math")) {
105
+ this.detectedPlugin = "rankmath";
106
+ return;
107
+ }
108
+ // Check for SEOPress
109
+ if (activePlugins.some((plugin) => plugin.slug === "wp-seopress")) {
110
+ this.detectedPlugin = "seopress";
111
+ return;
112
+ }
113
+ this.logger.debug("No SEO plugins detected, using generic metadata approach");
114
+ }
115
+ catch (_error) {
116
+ this.logger.error("Plugin detection failed", {
117
+ _error: _error instanceof Error ? _error.message : String(_error),
118
+ });
119
+ }
120
+ }
121
+ /**
122
+ * Get SEO metadata for a post or page
123
+ */
124
+ async getSEOMetadata(postId, type = "post") {
125
+ this.logger.debug("Fetching SEO metadata", { postId, type, plugin: this.detectedPlugin });
126
+ try {
127
+ // Get the post/page with meta fields
128
+ const content = type === "post"
129
+ ? await this.getPost(postId, "edit") // edit context includes meta
130
+ : await this.getPage(postId, "edit");
131
+ if (!content) {
132
+ throw new Error(`${type} with ID ${postId} not found`);
133
+ }
134
+ // Extract SEO metadata based on detected plugin
135
+ const metadata = this.extractSEOMetadata(content);
136
+ // Extract schema markup if available
137
+ const schema = this.extractSchemaMarkup(content);
138
+ // Get raw plugin data for debugging
139
+ const raw = this.getRawPluginData(content);
140
+ // For malformed plugin data (detected plugin but no actual data), return null for title/description
141
+ const pluginDetected = this.detectedPlugin !== "none";
142
+ const pluginDataMalformed = pluginDetected && !this.hasPluginData(content);
143
+ return {
144
+ postId: postId,
145
+ plugin: this.detectedPlugin,
146
+ title: pluginDataMalformed ? null : metadata.title,
147
+ description: pluginDataMalformed ? null : metadata.description,
148
+ canonical: metadata.canonical || null,
149
+ focusKeyword: metadata.focusKeyword || null,
150
+ openGraph: metadata.openGraph || { title: null, description: null },
151
+ twitter: metadata.twitterCard ? {
152
+ title: metadata.twitterCard.title || null,
153
+ description: metadata.twitterCard.description || null
154
+ } : { title: null, description: null },
155
+ schema,
156
+ raw,
157
+ lastModified: content.modified || content.date || new Date().toISOString(),
158
+ };
159
+ }
160
+ catch (_error) {
161
+ handleToolError(_error, "get SEO metadata", { postId, type });
162
+ throw _error;
163
+ }
164
+ }
165
+ /**
166
+ * Update SEO metadata for a post or page
167
+ */
168
+ async updateSEOMetadata(postId, metadata, type = "post") {
169
+ this.logger.debug("Updating SEO metadata", { postId, type, plugin: this.detectedPlugin });
170
+ try {
171
+ // Check if SEO plugin is available
172
+ if (this.detectedPlugin === "none") {
173
+ return {
174
+ success: false,
175
+ message: "No SEO plugin detected. Cannot update SEO metadata.",
176
+ postId,
177
+ plugin: this.detectedPlugin,
178
+ title: null,
179
+ description: null,
180
+ focusKeyword: null,
181
+ canonical: null,
182
+ openGraph: { title: null, description: null },
183
+ twitter: { title: null, description: null },
184
+ schema: undefined,
185
+ raw: {},
186
+ lastModified: new Date().toISOString(),
187
+ };
188
+ }
189
+ // Prepare meta fields based on detected plugin
190
+ const metaFields = this.prepareSEOMetaFields(metadata);
191
+ // Update the post/page with new meta fields
192
+ const updateData = {
193
+ id: postId,
194
+ meta: metaFields,
195
+ };
196
+ const _updatedContent = type === "post" ? await this.updatePost(updateData) : await this.updatePage(updateData);
197
+ // Return updated SEO data
198
+ return await this.getSEOMetadata(postId, type);
199
+ }
200
+ catch (_error) {
201
+ handleToolError(_error, "update SEO metadata", { postId, type });
202
+ throw _error;
203
+ }
204
+ }
205
+ /**
206
+ * Bulk get SEO metadata for multiple posts
207
+ */
208
+ async bulkGetSEOMetadata(params) {
209
+ this.logger.info("Starting bulk SEO metadata retrieval", {
210
+ postCount: params.postIds.length,
211
+ batchSize: params.batchSize || 10,
212
+ });
213
+ const results = [];
214
+ const batchSize = params.batchSize || 10;
215
+ const total = params.postIds.length;
216
+ // Process in batches
217
+ for (let i = 0; i < params.postIds.length; i += batchSize) {
218
+ const batch = params.postIds.slice(i, i + batchSize);
219
+ const batchPromises = batch.map(async (postId) => {
220
+ try {
221
+ return await this.getSEOMetadata(postId);
222
+ }
223
+ catch (_error) {
224
+ this.logger.warn("Failed to get SEO metadata for post", {
225
+ postId,
226
+ _error: _error instanceof Error ? _error.message : String(_error),
227
+ });
228
+ return null;
229
+ }
230
+ });
231
+ const batchResults = await Promise.all(batchPromises);
232
+ results.push(...batchResults.filter((r) => r !== null));
233
+ // Report progress
234
+ const processed = Math.min(i + batchSize, total);
235
+ if (params.progressCallback) {
236
+ params.progressCallback(processed, total);
237
+ }
238
+ // Small delay between batches to avoid overwhelming the server
239
+ if (i + batchSize < params.postIds.length) {
240
+ await new Promise((resolve) => setTimeout(resolve, 100));
241
+ }
242
+ }
243
+ this.logger.info("Bulk SEO metadata retrieval completed", {
244
+ requested: total,
245
+ successful: results.length,
246
+ failed: total - results.length,
247
+ });
248
+ return results;
249
+ }
250
+ /**
251
+ * Bulk update SEO metadata for multiple posts
252
+ */
253
+ async bulkUpdateSEOMetadata(params) {
254
+ this.logger.info("Starting bulk SEO metadata update", {
255
+ postCount: params.postIds.length,
256
+ batchSize: params.batchSize || 5, // Smaller batches for updates
257
+ });
258
+ const results = [];
259
+ const batchSize = params.batchSize || 5;
260
+ const total = params.postIds.length;
261
+ // Process in smaller batches for updates
262
+ for (let i = 0; i < params.postIds.length; i += batchSize) {
263
+ const batch = params.postIds.slice(i, i + batchSize);
264
+ const batchPromises = batch.map(async (postId) => {
265
+ try {
266
+ return await this.updateSEOMetadata(postId, params.metadata);
267
+ }
268
+ catch (_error) {
269
+ this.logger.warn("Failed to update SEO metadata for post", {
270
+ postId,
271
+ _error: _error instanceof Error ? _error.message : String(_error),
272
+ });
273
+ return null;
274
+ }
275
+ });
276
+ const batchResults = await Promise.all(batchPromises);
277
+ results.push(...batchResults.filter((r) => r !== null));
278
+ // Report progress
279
+ const processed = Math.min(i + batchSize, total);
280
+ if (params.progressCallback) {
281
+ params.progressCallback(processed, total);
282
+ }
283
+ // Longer delay between update batches
284
+ if (i + batchSize < params.postIds.length) {
285
+ await new Promise((resolve) => setTimeout(resolve, 200));
286
+ }
287
+ }
288
+ this.logger.info("Bulk SEO metadata update completed", {
289
+ requested: total,
290
+ successful: results.length,
291
+ failed: total - results.length,
292
+ });
293
+ return results;
294
+ }
295
+ /**
296
+ * Get all posts with SEO metadata for site audit
297
+ */
298
+ async getAllPostsWithSEO(params = {}) {
299
+ this.logger.debug("Fetching all posts with SEO data for audit", params);
300
+ const maxPosts = params.maxPosts || 100;
301
+ const results = [];
302
+ try {
303
+ // Get posts
304
+ const posts = await this.getPosts({
305
+ per_page: maxPosts,
306
+ status: ["publish"],
307
+ context: "edit", // Include meta fields
308
+ });
309
+ // Get pages if requested
310
+ let pages = [];
311
+ if (params.includePages) {
312
+ pages = await this.getPages({
313
+ per_page: Math.max(20, Math.floor(maxPosts / 5)), // 20% allocation for pages
314
+ status: ["publish"],
315
+ context: "edit",
316
+ });
317
+ }
318
+ // Process posts
319
+ for (const post of posts || []) {
320
+ try {
321
+ const seoData = await this.getSEOMetadata(post.id, "post");
322
+ results.push({ ...post, seoData });
323
+ }
324
+ catch (_error) {
325
+ // Include post without SEO data if metadata fetch fails
326
+ results.push(post);
327
+ this.logger.debug("Failed to get SEO data for post", { postId: post.id });
328
+ }
329
+ }
330
+ // Process pages
331
+ for (const page of pages) {
332
+ try {
333
+ const seoData = await this.getSEOMetadata(page.id, "page");
334
+ results.push({ ...page, seoData });
335
+ }
336
+ catch (_error) {
337
+ // Include page without SEO data if metadata fetch fails
338
+ results.push(page);
339
+ this.logger.debug("Failed to get SEO data for page", { pageId: page.id });
340
+ }
341
+ }
342
+ this.logger.info("Retrieved posts with SEO data", {
343
+ totalPosts: posts?.length || 0,
344
+ totalPages: pages.length,
345
+ withSEOData: results.filter((r) => r.seoData).length,
346
+ });
347
+ return results;
348
+ }
349
+ catch (_error) {
350
+ handleToolError(_error, "get all posts with SEO data", params);
351
+ throw _error;
352
+ }
353
+ }
354
+ /**
355
+ * Extract SEO metadata from WordPress post/page object
356
+ */
357
+ extractSEOMetadata(content) {
358
+ const meta = (Array.isArray(content.meta) ? {} : content.meta || {});
359
+ const fields = this.pluginFields[this.detectedPlugin];
360
+ // Extract basic metadata with plugin-specific field handling
361
+ const focusKeyword = this.getPluginFocusKeyword(meta, fields);
362
+ const canonical = this.extractMetaValue(meta, fields?.canonical);
363
+ const metadata = {
364
+ title: this.extractMetaValue(meta, fields?.title) || content.title?.rendered || "",
365
+ description: this.extractMetaValue(meta, fields?.description) || content.excerpt?.rendered || "",
366
+ ...(focusKeyword && { focusKeyword }),
367
+ ...(canonical && { canonical }),
368
+ };
369
+ // Extract robots directives based on plugin
370
+ if (this.detectedPlugin === "yoast" && "noindex" in fields && "nofollow" in fields) {
371
+ metadata.robots = {
372
+ index: this.extractMetaValue(meta, fields.noindex) !== "1",
373
+ follow: this.extractMetaValue(meta, fields.nofollow) !== "1",
374
+ };
375
+ }
376
+ else if (this.detectedPlugin === "rankmath" && "robots" in fields) {
377
+ const robotsValue = this.extractMetaValue(meta, fields.robots) || "";
378
+ metadata.robots = {
379
+ index: !robotsValue.includes("noindex"),
380
+ follow: !robotsValue.includes("nofollow"),
381
+ };
382
+ }
383
+ // Extract OpenGraph and Twitter data based on plugin
384
+ if (this.detectedPlugin === "yoast" && meta.yoast_head_json) {
385
+ const yoastData = meta.yoast_head_json;
386
+ metadata.openGraph = {
387
+ title: yoastData.og_title || metadata.title,
388
+ description: yoastData.og_description || metadata.description,
389
+ type: content.type === "page" ? "website" : "article",
390
+ url: content.link,
391
+ };
392
+ const twitterTitle = yoastData.twitter_title;
393
+ const twitterDescription = yoastData.twitter_description;
394
+ metadata.twitterCard = {
395
+ card: "summary",
396
+ ...(twitterTitle && { title: twitterTitle }),
397
+ ...(twitterDescription && { description: twitterDescription }),
398
+ };
399
+ }
400
+ else {
401
+ // Default OpenGraph data
402
+ metadata.openGraph = {
403
+ title: metadata.title,
404
+ description: metadata.description,
405
+ type: content.type === "page" ? "website" : "article",
406
+ url: content.link,
407
+ };
408
+ }
409
+ return metadata;
410
+ }
411
+ /**
412
+ * Extract schema markup from post meta
413
+ */
414
+ extractSchemaMarkup(content) {
415
+ const meta = (Array.isArray(content.meta) ? {} : content.meta || {});
416
+ const fields = this.pluginFields[this.detectedPlugin];
417
+ const schemaData = this.getPluginSchemaData(meta, fields);
418
+ if (!schemaData) {
419
+ return undefined;
420
+ }
421
+ try {
422
+ // Try to parse JSON-LD schema
423
+ if (typeof schemaData === "string" && schemaData.startsWith("{")) {
424
+ return JSON.parse(schemaData);
425
+ }
426
+ // Handle Yoast schema page types
427
+ if (this.detectedPlugin === "yoast" && schemaData) {
428
+ return {
429
+ "@context": "https://schema.org",
430
+ "@type": schemaData,
431
+ };
432
+ }
433
+ return undefined;
434
+ }
435
+ catch {
436
+ return undefined;
437
+ }
438
+ }
439
+ /**
440
+ * Prepare meta fields for SEO metadata update
441
+ */
442
+ prepareSEOMetaFields(metadata) {
443
+ const fields = this.pluginFields[this.detectedPlugin];
444
+ const metaFields = {};
445
+ // Map metadata to plugin-specific fields
446
+ if (metadata.title && fields?.title) {
447
+ metaFields[fields.title] = metadata.title;
448
+ }
449
+ if (metadata.description && fields?.description) {
450
+ metaFields[fields.description] = metadata.description;
451
+ }
452
+ if (metadata.focusKeyword) {
453
+ const focusKeywordField = this.getPluginFocusKeywordField(fields);
454
+ if (focusKeywordField) {
455
+ metaFields[focusKeywordField] = metadata.focusKeyword;
456
+ }
457
+ }
458
+ if (metadata.canonical && fields?.canonical) {
459
+ metaFields[fields.canonical] = metadata.canonical;
460
+ }
461
+ // Handle robots directives
462
+ if (metadata.robots && this.detectedPlugin === "yoast" && "noindex" in fields && "nofollow" in fields) {
463
+ if (fields.noindex) {
464
+ metaFields[fields.noindex] = metadata.robots.index ? "0" : "1";
465
+ }
466
+ if (fields.nofollow) {
467
+ metaFields[fields.nofollow] = metadata.robots.follow ? "0" : "1";
468
+ }
469
+ }
470
+ else if (metadata.robots && this.detectedPlugin === "rankmath" && "robots" in fields) {
471
+ const robotsArray = [];
472
+ if (!metadata.robots.index)
473
+ robotsArray.push("noindex");
474
+ if (!metadata.robots.follow)
475
+ robotsArray.push("nofollow");
476
+ metaFields[fields.robots] = robotsArray.join(",");
477
+ }
478
+ return metaFields;
479
+ }
480
+ /**
481
+ * Extract meta value with array handling
482
+ */
483
+ extractMetaValue(meta, fieldName) {
484
+ if (!fieldName) {
485
+ return null;
486
+ }
487
+ // For Yoast, check yoast_head_json first
488
+ if (this.detectedPlugin === "yoast" && meta.yoast_head_json) {
489
+ const yoastData = meta.yoast_head_json;
490
+ // Map field names to yoast_head_json properties
491
+ const yoastFieldMap = {
492
+ '_yoast_wpseo_title': 'title',
493
+ '_yoast_wpseo_metadesc': 'description',
494
+ '_yoast_wpseo_canonical': 'canonical',
495
+ '_yoast_wpseo_focuskw': 'focuskw'
496
+ };
497
+ const yoastField = yoastFieldMap[fieldName];
498
+ if (yoastField && yoastData[yoastField]) {
499
+ return yoastData[yoastField];
500
+ }
501
+ }
502
+ // Fallback to direct meta field lookup
503
+ if (!meta[fieldName]) {
504
+ return null;
505
+ }
506
+ const value = meta[fieldName];
507
+ // WordPress meta values can be arrays
508
+ if (Array.isArray(value)) {
509
+ return value[0] || null;
510
+ }
511
+ return value || null;
512
+ }
513
+ /**
514
+ * Get plugin-specific focus keyword field
515
+ */
516
+ getPluginFocusKeyword(meta, fields) {
517
+ if (this.detectedPlugin === "seopress" && "targetKeyword" in fields) {
518
+ return this.extractMetaValue(meta, fields.targetKeyword) || undefined;
519
+ }
520
+ else if ("focusKeyword" in fields) {
521
+ return this.extractMetaValue(meta, fields.focusKeyword) || undefined;
522
+ }
523
+ return undefined;
524
+ }
525
+ /**
526
+ * Get plugin-specific schema data field
527
+ */
528
+ getPluginSchemaData(meta, fields) {
529
+ if ("schema" in fields) {
530
+ return this.extractMetaValue(meta, fields.schema) || undefined;
531
+ }
532
+ return undefined;
533
+ }
534
+ /**
535
+ * Get plugin-specific focus keyword field name
536
+ */
537
+ getPluginFocusKeywordField(fields) {
538
+ if (this.detectedPlugin === "seopress" && "targetKeyword" in fields) {
539
+ return fields.targetKeyword;
540
+ }
541
+ else if ("focusKeyword" in fields) {
542
+ return fields.focusKeyword;
543
+ }
544
+ return undefined;
545
+ }
546
+ /**
547
+ * Test SEO plugin integration
548
+ */
549
+ async testSEOIntegration() {
550
+ this.logger.info("Testing SEO integration");
551
+ const result = {
552
+ pluginDetected: this.detectedPlugin,
553
+ canReadMetadata: false,
554
+ canWriteMetadata: false,
555
+ samplePostsWithSEO: 0,
556
+ errors: [],
557
+ };
558
+ try {
559
+ // Test reading metadata from recent posts
560
+ const testPosts = await this.getPosts({ per_page: 5, status: ["publish"] });
561
+ if (testPosts && testPosts.length > 0) {
562
+ let postsWithSEO = 0;
563
+ for (const post of testPosts) {
564
+ try {
565
+ const seoData = await this.getSEOMetadata(post.id);
566
+ if (seoData.title || seoData.description) {
567
+ postsWithSEO++;
568
+ }
569
+ }
570
+ catch (_error) {
571
+ result.errors?.push(`Failed to read SEO data for post ${post.id}: ${_error.message}`);
572
+ }
573
+ }
574
+ result.samplePostsWithSEO = postsWithSEO;
575
+ result.canReadMetadata = true;
576
+ }
577
+ // Test writing metadata (if we have posts to test with)
578
+ if (testPosts && testPosts.length > 0 && result.canReadMetadata) {
579
+ try {
580
+ const testPost = testPosts[0];
581
+ const originalSEO = await this.getSEOMetadata(testPost.id);
582
+ // Make a small test update
583
+ const testMetadata = {
584
+ description: (originalSEO.description || "") + " [TEST]",
585
+ };
586
+ await this.updateSEOMetadata(testPost.id, testMetadata);
587
+ // Restore original data
588
+ await this.updateSEOMetadata(testPost.id, {
589
+ description: originalSEO.description || "",
590
+ });
591
+ result.canWriteMetadata = true;
592
+ }
593
+ catch (_error) {
594
+ result.errors?.push(`Failed to write SEO data: ${_error.message}`);
595
+ }
596
+ }
597
+ this.logger.info("SEO integration test completed", result);
598
+ return result;
599
+ }
600
+ catch (_error) {
601
+ result.errors?.push(`SEO integration test failed: ${_error.message}`);
602
+ return result;
603
+ }
604
+ }
605
+ /**
606
+ * Get integration status for SEO features
607
+ */
608
+ getIntegrationStatus() {
609
+ const hasPlugin = this.detectedPlugin !== "none";
610
+ return {
611
+ hasPlugin,
612
+ plugin: this.detectedPlugin,
613
+ canReadMetadata: hasPlugin,
614
+ canWriteMetadata: hasPlugin,
615
+ features: {
616
+ metaTags: hasPlugin,
617
+ schema: hasPlugin,
618
+ socialMedia: hasPlugin,
619
+ xmlSitemap: false, // Would need additional API calls
620
+ breadcrumbs: false,
621
+ },
622
+ };
623
+ }
624
+ /**
625
+ * Check if content has plugin-specific SEO data
626
+ */
627
+ hasPluginData(content) {
628
+ const meta = (Array.isArray(content.meta) ? {} : content.meta || {});
629
+ switch (this.detectedPlugin) {
630
+ case "yoast":
631
+ // Check for yoast_head_json or individual meta fields
632
+ return (meta.yoast_head_json !== null && meta.yoast_head_json !== undefined) ||
633
+ (meta._yoast_wpseo_title !== null && meta._yoast_wpseo_title !== undefined);
634
+ case "rankmath":
635
+ return meta.rank_math_title !== null && meta.rank_math_title !== undefined;
636
+ case "seopress":
637
+ return meta._seopress_titles_title !== null && meta._seopress_titles_title !== undefined;
638
+ default:
639
+ return false;
640
+ }
641
+ }
642
+ /**
643
+ * Get raw plugin data for debugging purposes
644
+ */
645
+ getRawPluginData(content) {
646
+ const meta = (Array.isArray(content.meta) ? {} : content.meta || {});
647
+ // Return the raw plugin-specific data based on detected plugin
648
+ switch (this.detectedPlugin) {
649
+ case "yoast":
650
+ return meta.yoast_head_json || {};
651
+ case "rankmath":
652
+ // Extract RankMath specific fields
653
+ const rankMathData = {};
654
+ Object.keys(meta).forEach(key => {
655
+ if (key.startsWith('rank_math_')) {
656
+ rankMathData[key] = meta[key];
657
+ }
658
+ });
659
+ return rankMathData;
660
+ case "seopress":
661
+ // Extract SEOPress specific fields
662
+ const seopressData = {};
663
+ Object.keys(meta).forEach(key => {
664
+ if (key.startsWith('_seopress_')) {
665
+ seopressData[key] = meta[key];
666
+ }
667
+ });
668
+ return seopressData;
669
+ default:
670
+ return {};
671
+ }
672
+ }
673
+ }
674
+ //# sourceMappingURL=SEOWordPressClient.js.map