mcp-wordpress 2.6.3 → 2.7.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 (333) 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 +390 -49
  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 +24 -58
  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 +494 -56
  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
  333. package/src/dxt-entry.cjs +0 -68
@@ -0,0 +1,871 @@
1
+ /**
2
+ * WordPress SEO Tools - Main Module
3
+ *
4
+ * This module provides comprehensive SEO management functionality for WordPress
5
+ * through the MCP protocol. It combines content analysis, metadata generation,
6
+ * schema markup, and optimization capabilities in a modular architecture.
7
+ *
8
+ * Features:
9
+ * - Content analysis for readability and keyword optimization
10
+ * - Metadata generation with AI assistance and safety filters
11
+ * - Schema markup generation and validation
12
+ * - Internal linking optimization
13
+ * - Site audits and Core Web Vitals monitoring
14
+ * - Bulk operations with progress tracking
15
+ *
16
+ * @since 2.7.0
17
+ */
18
+
19
+ import { WordPressClient } from "@/client/api.js";
20
+ import { SEOWordPressClient } from "@/client/SEOWordPressClient.js";
21
+ import { LoggerFactory } from "@/utils/logger.js";
22
+ import { validateRequired, handleToolError } from "@/utils/error.js";
23
+ import { ContentAnalyzer } from "./analyzers/ContentAnalyzer.js";
24
+ import { MetaGenerator } from "./generators/MetaGenerator.js";
25
+ import { SchemaGenerator } from "./generators/SchemaGenerator.js";
26
+ import { InternalLinkingSuggester } from "./optimizers/InternalLinkingSuggester.js";
27
+ import { SiteAuditor } from "./auditors/SiteAuditor.js";
28
+ import { BulkOperations } from "./BulkOperations.js";
29
+ import { SEOCacheManager } from "@/cache/SEOCacheManager.js";
30
+ import { seoToolDefinitions } from "./SEOToolDefinitions.js";
31
+ import {
32
+ handleAnalyzeContent,
33
+ handleGenerateMetadata,
34
+ handleBulkUpdateMetadata,
35
+ handleGenerateSchema,
36
+ handleValidateSchema,
37
+ handleSuggestInternalLinks,
38
+ handlePerformSiteAudit,
39
+ handleTrackSERPPositions,
40
+ handleKeywordResearch,
41
+ handleTestSEOIntegration,
42
+ handleGetLiveSEOData,
43
+ } from "./SEOHandlers.js";
44
+ import type {
45
+ SEOAnalysisResult,
46
+ SEOMetadata,
47
+ SchemaMarkup,
48
+ SEOToolParams,
49
+ BulkOperationResult,
50
+ SiteAuditResult,
51
+ } from "@/types/seo.js";
52
+ import type { WordPressPost } from "@/types/wordpress.js";
53
+
54
+ /**
55
+ * Main SEOTools class that provides WordPress SEO management functionality.
56
+ *
57
+ * This class serves as the interface between the MCP framework and WordPress
58
+ * SEO operations. It follows the established pattern of other tool classes
59
+ * while providing SEO-specific capabilities.
60
+ *
61
+ * The class is designed with a modular architecture:
62
+ * - Analyzers for content evaluation
63
+ * - Generators for metadata and schema creation
64
+ * - Validators for ensuring compliance
65
+ * - Optimizers for performance improvements
66
+ *
67
+ * @since 2.7.0
68
+ */
69
+ export class SEOTools {
70
+ private logger = LoggerFactory.tool("seo");
71
+ private clients: Map<string, WordPressClient> = new Map();
72
+ private seoClients: Map<string, SEOWordPressClient> = new Map();
73
+ private contentAnalyzer: ContentAnalyzer;
74
+ private metaGenerator: MetaGenerator;
75
+ private schemaGenerator: SchemaGenerator;
76
+ private cacheManager: SEOCacheManager;
77
+ // Client-dependent components created per request
78
+ // private internalLinkingSuggester: InternalLinkingSuggester;
79
+ // private siteAuditor: SiteAuditor;
80
+ // private bulkOperations: BulkOperations;
81
+
82
+ constructor() {
83
+ this.contentAnalyzer = new ContentAnalyzer();
84
+ this.metaGenerator = new MetaGenerator();
85
+ this.schemaGenerator = new SchemaGenerator();
86
+ this.cacheManager = new SEOCacheManager();
87
+ // Note: Client-dependent components will be initialized when needed in handlers
88
+ // this.internalLinkingSuggester - initialized per request
89
+ // this.siteAuditor - initialized per request
90
+ // this.bulkOperations - initialized per request
91
+ }
92
+
93
+ /**
94
+ * Analyzes content for SEO optimization opportunities.
95
+ *
96
+ * Performs comprehensive analysis including:
97
+ * - Readability scoring (Flesch-Kincaid)
98
+ * - Keyword density and distribution
99
+ * - Content structure evaluation
100
+ * - Meta tag optimization suggestions
101
+ *
102
+ * @param params - Analysis parameters including post ID and analysis type
103
+ * @returns Detailed SEO analysis with scores and recommendations
104
+ */
105
+ async analyzeContent(params: SEOToolParams): Promise<SEOAnalysisResult> {
106
+ const siteLogger = LoggerFactory.tool("wp_seo_analyze_content", params.site);
107
+
108
+ return await siteLogger.time("SEO content analysis", async () => {
109
+ try {
110
+ validateRequired(params, ["postId", "analysisType"]);
111
+ const client = this.getSiteClient(params.site);
112
+
113
+ // Check cache first
114
+ const cacheKey = `seo:analyze:${params.site}:${params.postId as number}:${params.analysisType}`;
115
+ const cached = await this.getCachedResult(cacheKey);
116
+ if (cached) {
117
+ siteLogger.debug("Cache hit for content analysis", { cacheKey });
118
+ return cached as SEOAnalysisResult;
119
+ }
120
+
121
+ // Perform analysis (implementation will be added in analyzers)
122
+ const result = await this.performAnalysis(client, params);
123
+
124
+ // Cache the result
125
+ await this.cacheResult(cacheKey, result, 21600); // 6 hours
126
+
127
+ return result;
128
+ } catch (_error) {
129
+ handleToolError(_error, "analyze content", {
130
+ site: params.site,
131
+ postId: params.postId as number,
132
+ });
133
+ throw _error; // handleToolError will format it properly
134
+ }
135
+ });
136
+ }
137
+
138
+ /**
139
+ * Generates optimized metadata for posts.
140
+ *
141
+ * Creates SEO-friendly metadata including:
142
+ * - Title tags (60 character limit)
143
+ * - Meta descriptions (155-160 characters)
144
+ * - OpenGraph tags
145
+ * - Twitter Card metadata
146
+ *
147
+ * @param params - Generation parameters including content and constraints
148
+ * @returns Generated metadata with safety filters applied
149
+ */
150
+ async generateMetadata(params: SEOToolParams): Promise<SEOMetadata> {
151
+ const siteLogger = LoggerFactory.tool("wp_seo_generate_meta", params.site);
152
+
153
+ return await siteLogger.time("Generate SEO metadata", async () => {
154
+ try {
155
+ validateRequired(params, ["postId"]);
156
+ const client = this.getSiteClient(params.site);
157
+
158
+ // Implementation will be added in generators
159
+ const metadata = await this.createMetadata(client, params);
160
+
161
+ return metadata;
162
+ } catch (_error) {
163
+ handleToolError(_error, "generate metadata", {
164
+ site: params.site,
165
+ postId: params.postId as number,
166
+ });
167
+ throw _error;
168
+ }
169
+ });
170
+ }
171
+
172
+ /**
173
+ * Performs bulk metadata updates with progress tracking.
174
+ *
175
+ * Handles large-scale metadata updates with:
176
+ * - Batch processing (10 posts per batch)
177
+ * - Progress event streaming
178
+ * - Retry logic with exponential backoff
179
+ * - Dry-run mode for validation
180
+ *
181
+ * @param params - Bulk operation parameters
182
+ * @returns Operation results with success/failure counts
183
+ */
184
+ async bulkUpdateMetadata(params: SEOToolParams): Promise<BulkOperationResult> {
185
+ const siteLogger = LoggerFactory.tool("wp_seo_bulk_update", params.site);
186
+
187
+ return await siteLogger.time("Bulk metadata update", async () => {
188
+ try {
189
+ validateRequired(params, ["postIds", "updates"]);
190
+ const client = this.getSiteClient(params.site);
191
+
192
+ // Implementation will be added
193
+ const result = await this.processBulkUpdate(client, params);
194
+
195
+ return result;
196
+ } catch (_error) {
197
+ handleToolError(_error, "bulk update metadata", {
198
+ site: params.site,
199
+ count: (params.postIds as number[])?.length,
200
+ });
201
+ throw _error;
202
+ }
203
+ });
204
+ }
205
+
206
+ /**
207
+ * Generates structured data schema markup.
208
+ *
209
+ * Creates JSON-LD schema for:
210
+ * - Article, Product, FAQ, HowTo
211
+ * - Organization, Website, BreadcrumbList
212
+ * - Event, Recipe, Course, and more
213
+ *
214
+ * @param params - Schema generation parameters
215
+ * @returns Valid JSON-LD schema markup
216
+ */
217
+ async generateSchema(params: SEOToolParams): Promise<SchemaMarkup> {
218
+ const siteLogger = LoggerFactory.tool("wp_seo_generate_schema", params.site);
219
+
220
+ return await siteLogger.time("Generate schema markup", async () => {
221
+ try {
222
+ validateRequired(params, ["postId", "schemaType"]);
223
+ const client = this.getSiteClient(params.site);
224
+
225
+ // Check cache
226
+ const cacheKey = `seo:schema:${params.site}:${params.postId as number}:${params.schemaType}`;
227
+ const cached = await this.getCachedResult(cacheKey);
228
+ if (cached) {
229
+ return cached as SchemaMarkup;
230
+ }
231
+
232
+ // Generate schema (implementation in generators)
233
+ const schema = await this.createSchema(client, params);
234
+
235
+ // Cache for 24 hours
236
+ await this.cacheResult(cacheKey, schema, 86400);
237
+
238
+ return schema;
239
+ } catch (_error) {
240
+ handleToolError(_error, "generate schema", {
241
+ site: params.site,
242
+ postId: params.postId as number,
243
+ schemaType: params.schemaType,
244
+ });
245
+ throw _error;
246
+ }
247
+ });
248
+ }
249
+
250
+ /**
251
+ * Validates schema markup for correctness.
252
+ *
253
+ * Performs validation using:
254
+ * - Local validation rules
255
+ * - Google Rich Results Test API (optional)
256
+ * - Schema.org specifications
257
+ *
258
+ * @param params - Validation parameters including schema JSON
259
+ * @returns Validation results with errors and warnings
260
+ */
261
+ async validateSchema(params: SEOToolParams): Promise<unknown> {
262
+ const siteLogger = LoggerFactory.tool("wp_seo_validate_schema", params.site);
263
+
264
+ return await siteLogger.time("Validate schema markup", async () => {
265
+ try {
266
+ validateRequired(params, ["schema"]);
267
+
268
+ // Implementation will be added in validators
269
+ const validation = await this.performSchemaValidation(params);
270
+
271
+ return validation;
272
+ } catch (_error) {
273
+ handleToolError(_error, "validate schema", {
274
+ site: params.site,
275
+ });
276
+ throw _error;
277
+ }
278
+ });
279
+ }
280
+
281
+ /**
282
+ * Suggests internal linking opportunities.
283
+ *
284
+ * Analyzes content to suggest:
285
+ * - Related posts for contextual linking
286
+ * - Anchor text recommendations
287
+ * - Hub-and-spoke content architectures
288
+ * - Topical cluster connections
289
+ *
290
+ * @param params - Linking parameters including post ID
291
+ * @returns Internal linking suggestions with confidence scores
292
+ */
293
+ async suggestInternalLinks(params: SEOToolParams): Promise<unknown> {
294
+ const siteLogger = LoggerFactory.tool("wp_seo_internal_linking", params.site);
295
+
296
+ return await siteLogger.time("Suggest internal links", async () => {
297
+ try {
298
+ validateRequired(params, ["postId"]);
299
+ const client = this.getSiteClient(params.site);
300
+
301
+ // Implementation will be added in optimizers
302
+ const suggestions = await this.findLinkingOpportunities(client, params);
303
+
304
+ return suggestions;
305
+ } catch (_error) {
306
+ handleToolError(_error, "suggest internal links", {
307
+ site: params.site,
308
+ postId: params.postId as number,
309
+ });
310
+ throw _error;
311
+ }
312
+ });
313
+ }
314
+
315
+ /**
316
+ * Performs comprehensive site SEO audit.
317
+ *
318
+ * Audits include:
319
+ * - Technical SEO issues
320
+ * - Content quality analysis
321
+ * - Core Web Vitals assessment
322
+ * - Mobile usability checks
323
+ * - Structured data validation
324
+ *
325
+ * @param params - Audit parameters including scope and depth
326
+ * @returns Detailed audit results with prioritized recommendations
327
+ */
328
+ async performSiteAudit(params: SEOToolParams): Promise<SiteAuditResult> {
329
+ const siteLogger = LoggerFactory.tool("wp_seo_site_audit", params.site);
330
+
331
+ return await siteLogger.time("Perform site audit", async () => {
332
+ try {
333
+ const client = this.getSiteClient(params.site);
334
+
335
+ // Cache key for audit results (1 hour TTL)
336
+ const cacheKey = `seo:audit:${params.site}:${params.auditType || "full"}`;
337
+ const cached = await this.getCachedResult(cacheKey);
338
+ if (cached && (!params.force as boolean)) {
339
+ return cached as SiteAuditResult;
340
+ }
341
+
342
+ // Perform audit (implementation will be added)
343
+ const audit = await this.executeSiteAudit(client, params);
344
+
345
+ // Cache for 1 hour
346
+ await this.cacheResult(cacheKey, audit, 3600);
347
+
348
+ return audit;
349
+ } catch (_error) {
350
+ handleToolError(_error, "perform site audit", {
351
+ site: params.site,
352
+ auditType: params.auditType,
353
+ });
354
+ throw _error;
355
+ }
356
+ });
357
+ }
358
+
359
+ /**
360
+ * Retrieves all SEO tool definitions for MCP registration.
361
+ *
362
+ * @returns Array of SEO tool definitions with handlers
363
+ */
364
+ public getTools(): unknown[] {
365
+ return seoToolDefinitions.map((toolDef) => ({
366
+ ...toolDef,
367
+ handler: this.getHandlerForTool(toolDef.name),
368
+ }));
369
+ }
370
+
371
+ /**
372
+ * Maps tool names to their corresponding handler methods.
373
+ *
374
+ * @param toolName - Name of the tool
375
+ * @returns Handler function for the tool
376
+ * @private
377
+ */
378
+ private getHandlerForTool(toolName: string): unknown {
379
+ const handlers: Record<string, unknown> = {
380
+ wp_seo_analyze_content: handleAnalyzeContent,
381
+ wp_seo_generate_metadata: handleGenerateMetadata,
382
+ wp_seo_bulk_update_metadata: handleBulkUpdateMetadata,
383
+ wp_seo_generate_schema: handleGenerateSchema,
384
+ wp_seo_validate_schema: handleValidateSchema,
385
+ wp_seo_suggest_internal_links: handleSuggestInternalLinks,
386
+ wp_seo_site_audit: handlePerformSiteAudit,
387
+ wp_seo_track_serp: handleTrackSERPPositions,
388
+ wp_seo_keyword_research: handleKeywordResearch,
389
+ wp_seo_test_integration: handleTestSEOIntegration,
390
+ wp_seo_get_live_data: handleGetLiveSEOData,
391
+ };
392
+
393
+ return (
394
+ handlers[toolName] ||
395
+ (() => {
396
+ throw new Error(`Unknown SEO tool: ${toolName}`);
397
+ })
398
+ );
399
+ }
400
+
401
+ /**
402
+ * Gets or creates a WordPress client for the specified site.
403
+ *
404
+ * @param site - Site identifier
405
+ * @returns WordPress client instance
406
+ * @private
407
+ */
408
+ private getSiteClient(site?: string): WordPressClient {
409
+ const siteId = site || "default";
410
+
411
+ if (!this.clients.has(siteId)) {
412
+ // Create new client for site (implementation depends on multi-site config)
413
+ const client = new WordPressClient();
414
+ this.clients.set(siteId, client);
415
+ }
416
+
417
+ return this.clients.get(siteId)!;
418
+ }
419
+
420
+ /**
421
+ * Get SEO-enhanced WordPress client for a specific site
422
+ */
423
+ private async getSEOClient(site?: string): Promise<SEOWordPressClient> {
424
+ const siteId = site || "default";
425
+
426
+ if (!this.seoClients.has(siteId)) {
427
+ // Get base client config and create SEO client
428
+ const baseClient = this.getSiteClient(site);
429
+ const seoClient = new SEOWordPressClient(baseClient.config);
430
+
431
+ // Initialize SEO capabilities
432
+ await seoClient.initializeSEO();
433
+
434
+ this.seoClients.set(siteId, seoClient);
435
+ }
436
+
437
+ return this.seoClients.get(siteId)!;
438
+ }
439
+
440
+ /**
441
+ * Retrieves cached result if available.
442
+ *
443
+ * @param key - Cache key
444
+ * @returns Cached result or null
445
+ * @private
446
+ */
447
+ private async getCachedResult(key: string): Promise<unknown | null> {
448
+ return this.cacheManager.get(key);
449
+ }
450
+
451
+ /**
452
+ * Caches a result with specified TTL.
453
+ *
454
+ * @param key - Cache key
455
+ * @param result - Result to cache
456
+ * @param ttl - Time to live in seconds
457
+ * @private
458
+ */
459
+ private async cacheResult(key: string, result: unknown, ttl: number): Promise<void> {
460
+ await this.cacheManager.set(key, result, ttl);
461
+ }
462
+
463
+ // Implementation methods
464
+ private async performAnalysis(client: WordPressClient, params: SEOToolParams): Promise<SEOAnalysisResult> {
465
+ // Fetch the post data
466
+ const post = await client.getPost(params.postId as number);
467
+
468
+ // Perform content analysis
469
+ return await this.contentAnalyzer.analyzePost(post as WordPressPost, params);
470
+ }
471
+
472
+ private async createMetadata(client: WordPressClient, params: SEOToolParams): Promise<SEOMetadata> {
473
+ // Fetch the post data
474
+ const post = await client.getPost(params.postId as number);
475
+
476
+ // Check cache first
477
+ const cacheKey = `seo:metadata:${params.site}:${params.postId as number}`;
478
+ const cached = await this.getCachedResult(cacheKey);
479
+ if (cached) {
480
+ this.logger.debug("Cache hit for metadata generation", { cacheKey });
481
+ return cached as SEOMetadata;
482
+ }
483
+
484
+ // Generate metadata
485
+ const metadata = await this.metaGenerator.generateMetadata(post as WordPressPost, params, {
486
+ includeKeywords: Boolean(params.focusKeywords?.length),
487
+ brandVoice: "professional", // Could be configurable
488
+ includeCallToAction: true,
489
+ preserveExisting: false,
490
+ });
491
+
492
+ // Cache for 2 hours
493
+ await this.cacheResult(cacheKey, metadata, 7200);
494
+
495
+ return metadata;
496
+ }
497
+
498
+ private async processBulkUpdate(client: WordPressClient, params: SEOToolParams): Promise<BulkOperationResult> {
499
+ // Initialize BulkOperations for the specific client if needed
500
+ const bulkOps = new BulkOperations(client, this.cacheManager, {
501
+ batchSize: 10,
502
+ maxRetries: 3,
503
+ enableProgress: true,
504
+ });
505
+
506
+ // Set up progress callback for logging
507
+ const progressCallback = (progress: {
508
+ processed: number;
509
+ total: number;
510
+ completed: number;
511
+ failed: number;
512
+ currentBatch: number;
513
+ totalBatches: number;
514
+ eta?: Date;
515
+ }) => {
516
+ this.logger.info("Bulk operation progress", {
517
+ progress: `${progress.processed}/${progress.total}`,
518
+ success: progress.completed,
519
+ failed: progress.failed,
520
+ currentBatch: `${progress.currentBatch}/${progress.totalBatches}`,
521
+ eta: progress.eta,
522
+ });
523
+ };
524
+
525
+ // Execute bulk metadata update
526
+ return await bulkOps.bulkUpdateMetadata(params, progressCallback);
527
+ }
528
+
529
+ private async createSchema(client: WordPressClient, params: SEOToolParams): Promise<SchemaMarkup> {
530
+ // Fetch the post data
531
+ const post = await client.getPost(params.postId as number);
532
+
533
+ if (!post) {
534
+ throw new Error(`Post ${params.postId as number} not found`);
535
+ }
536
+
537
+ // Generate schema markup
538
+ const options = {
539
+ includeAuthor: true,
540
+ includeOrganization: true,
541
+ includeBreadcrumbs: params.schemaType === "BreadcrumbList",
542
+ includeImages: true,
543
+ siteConfig: {
544
+ name: "WordPress Site", // This could be configurable
545
+ url: "https://example.com", // This could come from site settings
546
+ description: "WordPress site with SEO optimization",
547
+ },
548
+ customProperties: {
549
+ ...(params.focusKeywords && { keywords: params.focusKeywords }),
550
+ },
551
+ };
552
+
553
+ return await this.schemaGenerator.generateSchema(post as WordPressPost, params, options);
554
+ }
555
+
556
+ private async performSchemaValidation(params: SEOToolParams): Promise<unknown> {
557
+ if (!params.schema) {
558
+ throw new Error("Schema markup is required for validation");
559
+ }
560
+
561
+ // Basic validation using the SchemaGenerator's validation method
562
+ const validation = this.schemaGenerator.validateSchema(params.schema as SchemaMarkup);
563
+
564
+ return {
565
+ valid: validation.valid,
566
+ errors: validation.errors,
567
+ warnings: [], // Could add warnings for best practices
568
+ schemaType: (params.schema as SchemaMarkup)["@type"],
569
+ validatedAt: new Date().toISOString(),
570
+ };
571
+ }
572
+
573
+ private async findLinkingOpportunities(client: WordPressClient, params: SEOToolParams): Promise<unknown> {
574
+ // Fetch the source post
575
+ const post = await client.getPost(params.postId as number);
576
+
577
+ if (!post) {
578
+ throw new Error(`Post ${params.postId as number} not found`);
579
+ }
580
+
581
+ // Initialize internal linking suggester for the specific client
582
+ const linkingSuggester = new InternalLinkingSuggester(client, {
583
+ maxSuggestions: params.maxSuggestions || 10,
584
+ minRelevanceScore: params.minimumRelevance || 30,
585
+ maxLinksPerPost: 5,
586
+ useSemanticAnalysis: true,
587
+ enableContextualPlacement: true,
588
+ });
589
+
590
+ // Generate internal linking suggestions
591
+ const suggestions = await linkingSuggester.generateSuggestions(post as WordPressPost, params);
592
+
593
+ return {
594
+ postId: params.postId as number,
595
+ postTitle: post.title?.rendered || "Untitled",
596
+ suggestions,
597
+ totalSuggestions: suggestions.length,
598
+ averageRelevance:
599
+ suggestions.length > 0
600
+ ? (suggestions.reduce((sum, s) => sum + s.relevance, 0) / suggestions.length).toFixed(1)
601
+ : 0,
602
+ generatedAt: new Date().toISOString(),
603
+ };
604
+ }
605
+
606
+ /**
607
+ * Test SEO plugin integration and WordPress API connectivity
608
+ */
609
+ async testSEOIntegration(params: SEOToolParams): Promise<unknown> {
610
+ const siteLogger = LoggerFactory.tool("wp_seo_test_integration", params.site);
611
+
612
+ return await siteLogger.time("Test SEO integration", async () => {
613
+ try {
614
+ const seoClient = await this.getSEOClient(params.site);
615
+
616
+ // Test the integration
617
+ const integrationTest = await seoClient.testSEOIntegration();
618
+
619
+ // Get some sample SEO data
620
+ let sampleSEOData = null;
621
+ if (integrationTest.canReadMetadata && integrationTest.samplePostsWithSEO > 0) {
622
+ try {
623
+ const posts = await seoClient.getPosts({ per_page: 1, status: ["publish"] });
624
+ if (posts && posts.length > 0) {
625
+ sampleSEOData = await seoClient.getSEOMetadata(posts[0].id);
626
+ }
627
+ } catch (_error) {
628
+ // Sample data fetch failed, but that's okay
629
+ this.logger.debug("Failed to fetch sample SEO data", { _error: _error });
630
+ }
631
+ }
632
+
633
+ const result = {
634
+ integrationTest,
635
+ sampleSEOData,
636
+ recommendations: this.generateIntegrationRecommendations(integrationTest),
637
+ testedAt: new Date().toISOString(),
638
+ };
639
+
640
+ siteLogger.info("SEO integration test completed", {
641
+ plugin: integrationTest.pluginDetected,
642
+ canRead: integrationTest.canReadMetadata,
643
+ canWrite: integrationTest.canWriteMetadata,
644
+ postsWithSEO: integrationTest.samplePostsWithSEO,
645
+ });
646
+
647
+ return result;
648
+ } catch (_error) {
649
+ handleToolError(_error, "test SEO integration", {
650
+ site: params.site,
651
+ });
652
+ throw _error;
653
+ }
654
+ });
655
+ }
656
+
657
+ /**
658
+ * Get live SEO data for multiple posts
659
+ */
660
+ async getLiveSEOData(params: SEOToolParams & { maxPosts?: number }): Promise<unknown> {
661
+ const siteLogger = LoggerFactory.tool("wp_seo_get_live_data", params.site);
662
+
663
+ return await siteLogger.time("Get live SEO data", async () => {
664
+ try {
665
+ const seoClient = await this.getSEOClient(params.site);
666
+
667
+ // Get all posts with SEO data
668
+ const postsWithSEO = await seoClient.getAllPostsWithSEO({
669
+ maxPosts: params.maxPosts || 20,
670
+ includePages: true,
671
+ });
672
+
673
+ // Analyze the SEO data
674
+ const analysis = this.analyzeLiveSEOData(postsWithSEO);
675
+
676
+ const result = {
677
+ totalContent: postsWithSEO.length,
678
+ contentWithSEO: postsWithSEO.filter((p) => p.seoData).length,
679
+ analysis,
680
+ posts: postsWithSEO.map((post) => ({
681
+ id: post.id,
682
+ title: post.title?.rendered,
683
+ type: post.type,
684
+ url: post.link,
685
+ seoData: post.seoData
686
+ ? {
687
+ hasTitle: !!post.seoData.title,
688
+ hasDescription: !!post.seoData.description,
689
+ hasFocusKeyword: !!post.seoData.focusKeyword,
690
+ plugin: post.seoData.plugin,
691
+ lastModified: post.seoData.lastModified,
692
+ }
693
+ : null,
694
+ })),
695
+ retrievedAt: new Date().toISOString(),
696
+ };
697
+
698
+ siteLogger.info("Live SEO data retrieved", {
699
+ totalContent: result.totalContent,
700
+ withSEO: result.contentWithSEO,
701
+ plugin: analysis.detectedPlugin,
702
+ });
703
+
704
+ return result;
705
+ } catch (_error) {
706
+ handleToolError(_error, "get live SEO data", {
707
+ site: params.site,
708
+ maxPosts: params.maxPosts,
709
+ });
710
+ throw _error;
711
+ }
712
+ });
713
+ }
714
+
715
+ private async executeSiteAudit(client: WordPressClient, params: SEOToolParams): Promise<SiteAuditResult> {
716
+ // Initialize site auditor for the specific client
717
+ const siteAuditor = new SiteAuditor(client, {
718
+ includeTechnical: params.includeTechnical !== false,
719
+ includeContent: params.includeContent !== false,
720
+ includeArchitecture: params.includeArchitecture !== false,
721
+ includePerformance: params.includePerformance !== false,
722
+ includeAccessibility: params.includeAccessibility || false,
723
+ maxPagesForContentAudit: params.maxPages || 50,
724
+ minSeverityLevel: params.minSeverity || "medium",
725
+ includeRecommendations: params.includeRecommendations !== false,
726
+ });
727
+
728
+ // Perform comprehensive site audit
729
+ return await siteAuditor.performSiteAudit(params);
730
+ }
731
+
732
+ /**
733
+ * Generate integration recommendations based on test results
734
+ */
735
+ private generateIntegrationRecommendations(testResult: {
736
+ pluginDetected: string;
737
+ canReadMetadata: boolean;
738
+ canWriteMetadata: boolean;
739
+ samplePostsWithSEO: number;
740
+ errors?: string[];
741
+ }): string[] {
742
+ const recommendations: string[] = [];
743
+
744
+ if (testResult.pluginDetected === "none") {
745
+ recommendations.push(
746
+ "Consider installing a WordPress SEO plugin like Yoast SEO or RankMath for better SEO metadata management",
747
+ );
748
+ }
749
+
750
+ if (!testResult.canReadMetadata) {
751
+ recommendations.push(
752
+ "SEO metadata reading failed. Check WordPress REST API permissions and ensure the detected SEO plugin exposes metadata via REST API",
753
+ );
754
+ }
755
+
756
+ if (!testResult.canWriteMetadata) {
757
+ recommendations.push(
758
+ "SEO metadata writing failed. Verify user permissions for editing posts and pages via WordPress REST API",
759
+ );
760
+ }
761
+
762
+ if (testResult.samplePostsWithSEO === 0) {
763
+ recommendations.push(
764
+ "No SEO metadata found on sample posts. Consider adding SEO titles and descriptions to your content",
765
+ );
766
+ } else if (testResult.samplePostsWithSEO < 3) {
767
+ recommendations.push(
768
+ "Limited SEO metadata detected. Consider optimizing more posts with SEO titles, descriptions, and focus keywords",
769
+ );
770
+ }
771
+
772
+ if (testResult.errors && testResult.errors.length > 0) {
773
+ recommendations.push(
774
+ "Integration errors detected. Review error details and check WordPress configuration, plugin settings, and REST API accessibility",
775
+ );
776
+ }
777
+
778
+ if (recommendations.length === 0) {
779
+ recommendations.push("SEO integration is working well! All tests passed successfully.");
780
+ }
781
+
782
+ return recommendations;
783
+ }
784
+
785
+ /**
786
+ * Analyze live SEO data for insights
787
+ */
788
+ private analyzeLiveSEOData(
789
+ posts: Array<{
790
+ seoData?: { plugin: string; title?: string | null; description?: string | null; focusKeyword?: string | null };
791
+ }>,
792
+ ): Record<string, unknown> {
793
+ const analysis = {
794
+ detectedPlugin: "none",
795
+ totalPosts: posts.length,
796
+ postsWithSEO: 0,
797
+ postsWithTitles: 0,
798
+ postsWithDescriptions: 0,
799
+ postsWithFocusKeywords: 0,
800
+ averageMetadataCompleteness: 0,
801
+ recommendations: [] as string[],
802
+ };
803
+
804
+ const pluginCounts = { yoast: 0, rankmath: 0, seopress: 0, none: 0 };
805
+
806
+ for (const post of posts) {
807
+ if (post.seoData) {
808
+ analysis.postsWithSEO++;
809
+
810
+ // Count plugin usage
811
+ pluginCounts[post.seoData.plugin as keyof typeof pluginCounts]++;
812
+
813
+ // Count metadata presence
814
+ if (post.seoData.title) analysis.postsWithTitles++;
815
+ if (post.seoData.description) analysis.postsWithDescriptions++;
816
+ if (post.seoData.focusKeyword) analysis.postsWithFocusKeywords++;
817
+ }
818
+ }
819
+
820
+ // Determine most common plugin
821
+ const mostUsedPlugin = Object.entries(pluginCounts).reduce((a, b) =>
822
+ pluginCounts[a[0] as keyof typeof pluginCounts] > pluginCounts[b[0] as keyof typeof pluginCounts] ? a : b,
823
+ );
824
+ analysis.detectedPlugin = mostUsedPlugin[0];
825
+
826
+ // Calculate metadata completeness
827
+ if (analysis.postsWithSEO > 0) {
828
+ const titleCompleteness = (analysis.postsWithTitles / analysis.postsWithSEO) * 100;
829
+ const descriptionCompleteness = (analysis.postsWithDescriptions / analysis.postsWithSEO) * 100;
830
+ const keywordCompleteness = (analysis.postsWithFocusKeywords / analysis.postsWithSEO) * 100;
831
+
832
+ analysis.averageMetadataCompleteness = Math.round(
833
+ (titleCompleteness + descriptionCompleteness + keywordCompleteness) / 3,
834
+ );
835
+ }
836
+
837
+ // Generate recommendations
838
+ const seoPercentage = (analysis.postsWithSEO / analysis.totalPosts) * 100;
839
+
840
+ if (seoPercentage < 50) {
841
+ analysis.recommendations.push(
842
+ `Only ${Math.round(seoPercentage)}% of content has SEO metadata. Consider optimizing more posts.`,
843
+ );
844
+ }
845
+
846
+ if (analysis.averageMetadataCompleteness < 70) {
847
+ analysis.recommendations.push(
848
+ `Metadata completeness is ${analysis.averageMetadataCompleteness}%. Focus on adding missing titles, descriptions, and focus keywords.`,
849
+ );
850
+ }
851
+
852
+ const titlePercentage = analysis.postsWithSEO > 0 ? (analysis.postsWithTitles / analysis.postsWithSEO) * 100 : 0;
853
+ if (titlePercentage < 80) {
854
+ analysis.recommendations.push(
855
+ `${Math.round(100 - titlePercentage)}% of SEO-enabled posts lack custom titles. Add SEO titles for better search visibility.`,
856
+ );
857
+ }
858
+
859
+ const descPercentage =
860
+ analysis.postsWithSEO > 0 ? (analysis.postsWithDescriptions / analysis.postsWithSEO) * 100 : 0;
861
+ if (descPercentage < 80) {
862
+ analysis.recommendations.push(
863
+ `${Math.round(100 - descPercentage)}% of SEO-enabled posts lack meta descriptions. Add descriptions to improve click-through rates.`,
864
+ );
865
+ }
866
+
867
+ return analysis;
868
+ }
869
+ }
870
+
871
+ export default SEOTools;