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.
- package/README.md +1 -1
- package/dist/cache/CacheInvalidation.d.ts +25 -6
- package/dist/cache/CacheInvalidation.d.ts.map +1 -1
- package/dist/cache/CacheInvalidation.js +168 -16
- package/dist/cache/CacheInvalidation.js.map +1 -1
- package/dist/cache/HttpCacheWrapper.d.ts.map +1 -1
- package/dist/cache/HttpCacheWrapper.js +3 -4
- package/dist/cache/HttpCacheWrapper.js.map +1 -1
- package/dist/cache/SEOCacheManager.d.ts +150 -0
- package/dist/cache/SEOCacheManager.d.ts.map +1 -0
- package/dist/cache/SEOCacheManager.js +275 -0
- package/dist/cache/SEOCacheManager.js.map +1 -0
- package/dist/client/SEOWordPressClient.d.ts +164 -0
- package/dist/client/SEOWordPressClient.d.ts.map +1 -0
- package/dist/client/SEOWordPressClient.js +674 -0
- package/dist/client/SEOWordPressClient.js.map +1 -0
- package/dist/client/api.d.ts.map +1 -1
- package/dist/client/api.js +50 -20
- package/dist/client/api.js.map +1 -1
- package/dist/client/auth.js +19 -19
- package/dist/client/auth.js.map +1 -1
- package/dist/client/index.d.ts +11 -0
- package/dist/client/index.d.ts.map +1 -0
- package/dist/client/index.js +14 -0
- package/dist/client/index.js.map +1 -0
- package/dist/client/managers/AuthManager.d.ts +39 -0
- package/dist/client/managers/AuthManager.d.ts.map +1 -0
- package/dist/client/managers/AuthManager.js +142 -0
- package/dist/client/managers/AuthManager.js.map +1 -0
- package/dist/client/managers/AuthenticationManager.d.ts.map +1 -1
- package/dist/client/managers/AuthenticationManager.js +10 -9
- package/dist/client/managers/AuthenticationManager.js.map +1 -1
- package/dist/client/managers/BaseManager.d.ts.map +1 -1
- package/dist/client/managers/BaseManager.js +12 -0
- package/dist/client/managers/BaseManager.js.map +1 -1
- package/dist/client/managers/ComposedAuthenticationManager.d.ts +94 -0
- package/dist/client/managers/ComposedAuthenticationManager.d.ts.map +1 -0
- package/dist/client/managers/ComposedAuthenticationManager.js +340 -0
- package/dist/client/managers/ComposedAuthenticationManager.js.map +1 -0
- package/dist/client/managers/ComposedManagerFactory.d.ts +104 -0
- package/dist/client/managers/ComposedManagerFactory.d.ts.map +1 -0
- package/dist/client/managers/ComposedManagerFactory.js +180 -0
- package/dist/client/managers/ComposedManagerFactory.js.map +1 -0
- package/dist/client/managers/ComposedRequestManager.d.ts +82 -0
- package/dist/client/managers/ComposedRequestManager.d.ts.map +1 -0
- package/dist/client/managers/ComposedRequestManager.js +260 -0
- package/dist/client/managers/ComposedRequestManager.js.map +1 -0
- package/dist/client/managers/JWTAuthImplementation.d.ts +86 -0
- package/dist/client/managers/JWTAuthImplementation.d.ts.map +1 -0
- package/dist/client/managers/JWTAuthImplementation.js +240 -0
- package/dist/client/managers/JWTAuthImplementation.js.map +1 -0
- package/dist/client/managers/ManagersIndex.d.ts +6 -0
- package/dist/client/managers/ManagersIndex.d.ts.map +1 -0
- package/dist/client/managers/ManagersIndex.js +6 -0
- package/dist/client/managers/ManagersIndex.js.map +1 -0
- package/dist/client/managers/RequestManager.d.ts.map +1 -1
- package/dist/client/managers/RequestManager.js +5 -3
- package/dist/client/managers/RequestManager.js.map +1 -1
- package/dist/client/managers/composed/MigrationAdapter.d.ts +80 -0
- package/dist/client/managers/composed/MigrationAdapter.d.ts.map +1 -0
- package/dist/client/managers/composed/MigrationAdapter.js +214 -0
- package/dist/client/managers/composed/MigrationAdapter.js.map +1 -0
- package/dist/client/managers/composed/index.d.ts +23 -0
- package/dist/client/managers/composed/index.d.ts.map +1 -0
- package/dist/client/managers/composed/index.js +26 -0
- package/dist/client/managers/composed/index.js.map +1 -0
- package/dist/client/managers/implementations/ConfigurationProviderImpl.d.ts +27 -0
- package/dist/client/managers/implementations/ConfigurationProviderImpl.d.ts.map +1 -0
- package/dist/client/managers/implementations/ConfigurationProviderImpl.js +41 -0
- package/dist/client/managers/implementations/ConfigurationProviderImpl.js.map +1 -0
- package/dist/client/managers/implementations/ErrorHandlerImpl.d.ts +31 -0
- package/dist/client/managers/implementations/ErrorHandlerImpl.d.ts.map +1 -0
- package/dist/client/managers/implementations/ErrorHandlerImpl.js +73 -0
- package/dist/client/managers/implementations/ErrorHandlerImpl.js.map +1 -0
- package/dist/client/managers/implementations/ParameterValidatorImpl.d.ts +47 -0
- package/dist/client/managers/implementations/ParameterValidatorImpl.d.ts.map +1 -0
- package/dist/client/managers/implementations/ParameterValidatorImpl.js +141 -0
- package/dist/client/managers/implementations/ParameterValidatorImpl.js.map +1 -0
- package/dist/client/managers/interfaces/ManagerInterfaces.d.ts +147 -0
- package/dist/client/managers/interfaces/ManagerInterfaces.d.ts.map +1 -0
- package/dist/client/managers/interfaces/ManagerInterfaces.js +6 -0
- package/dist/client/managers/interfaces/ManagerInterfaces.js.map +1 -0
- package/dist/config/Config.d.ts +30 -0
- package/dist/config/Config.d.ts.map +1 -1
- package/dist/config/Config.js +30 -0
- package/dist/config/Config.js.map +1 -1
- package/dist/config/ConfigurationSchema.d.ts +75 -198
- package/dist/config/ConfigurationSchema.d.ts.map +1 -1
- package/dist/config/ConfigurationSchema.js +17 -17
- package/dist/config/ConfigurationSchema.js.map +1 -1
- package/dist/config/ServerConfiguration.d.ts +2 -2
- package/dist/config/ServerConfiguration.d.ts.map +1 -1
- package/dist/config/ServerConfiguration.js +15 -13
- package/dist/config/ServerConfiguration.js.map +1 -1
- package/dist/config/index.d.ts +8 -0
- package/dist/config/index.d.ts.map +1 -0
- package/dist/config/index.js +11 -0
- package/dist/config/index.js.map +1 -0
- package/dist/docs/DocumentationGenerator.js +2 -2
- package/dist/docs/DocumentationGenerator.js.map +1 -1
- package/dist/dxt-entry.js +3 -3
- package/dist/dxt-entry.js.map +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +38 -37
- package/dist/index.js.map +1 -1
- package/dist/performance/MetricsCollector.d.ts.map +1 -1
- package/dist/performance/MetricsCollector.js +5 -4
- package/dist/performance/MetricsCollector.js.map +1 -1
- package/dist/security/AISecurityScanner.js +7 -7
- package/dist/security/AISecurityScanner.js.map +1 -1
- package/dist/security/AutomatedRemediation.d.ts.map +1 -1
- package/dist/security/AutomatedRemediation.js +11 -11
- package/dist/security/AutomatedRemediation.js.map +1 -1
- package/dist/security/InputValidator.d.ts +50 -126
- package/dist/security/InputValidator.d.ts.map +1 -1
- package/dist/security/InputValidator.js +9 -9
- package/dist/security/InputValidator.js.map +1 -1
- package/dist/security/SecurityCIPipeline.d.ts +47 -5
- package/dist/security/SecurityCIPipeline.d.ts.map +1 -1
- package/dist/security/SecurityCIPipeline.js +390 -49
- package/dist/security/SecurityCIPipeline.js.map +1 -1
- package/dist/security/SecurityConfigManager.js +10 -10
- package/dist/security/SecurityConfigManager.js.map +1 -1
- package/dist/security/SecurityMonitoring.js +4 -4
- package/dist/security/SecurityMonitoring.js.map +1 -1
- package/dist/security/SecurityReviewer.d.ts.map +1 -1
- package/dist/security/SecurityReviewer.js +13 -6
- package/dist/security/SecurityReviewer.js.map +1 -1
- package/dist/security/index.js +3 -3
- package/dist/security/index.js.map +1 -1
- package/dist/server/ConnectionTester.js +5 -5
- package/dist/server/ConnectionTester.js.map +1 -1
- package/dist/server/ToolRegistry.d.ts +2 -2
- package/dist/server/ToolRegistry.d.ts.map +1 -1
- package/dist/server/ToolRegistry.js +7 -6
- package/dist/server/ToolRegistry.js.map +1 -1
- package/dist/server/index.d.ts +7 -0
- package/dist/server/index.d.ts.map +1 -0
- package/dist/server/index.js +9 -0
- package/dist/server/index.js.map +1 -0
- package/dist/tools/BaseToolManager.d.ts.map +1 -1
- package/dist/tools/BaseToolManager.js +11 -11
- package/dist/tools/BaseToolManager.js.map +1 -1
- package/dist/tools/auth.d.ts.map +1 -1
- package/dist/tools/auth.js +7 -7
- package/dist/tools/auth.js.map +1 -1
- package/dist/tools/comments.d.ts.map +1 -1
- package/dist/tools/comments.js +14 -14
- package/dist/tools/comments.js.map +1 -1
- package/dist/tools/index.d.ts +1 -0
- package/dist/tools/index.d.ts.map +1 -1
- package/dist/tools/index.js +1 -0
- package/dist/tools/index.js.map +1 -1
- package/dist/tools/media.d.ts.map +1 -1
- package/dist/tools/media.js +14 -11
- package/dist/tools/media.js.map +1 -1
- package/dist/tools/pages.d.ts.map +1 -1
- package/dist/tools/pages.js +12 -12
- package/dist/tools/pages.js.map +1 -1
- package/dist/tools/performance.d.ts.map +1 -1
- package/dist/tools/performance.js +13 -11
- package/dist/tools/performance.js.map +1 -1
- package/dist/tools/posts/PostHandlers.d.ts.map +1 -1
- package/dist/tools/posts/PostHandlers.js +16 -16
- package/dist/tools/posts/PostHandlers.js.map +1 -1
- package/dist/tools/posts/PostToolDefinitions.d.ts.map +1 -1
- package/dist/tools/posts/index.d.ts.map +1 -1
- package/dist/tools/seo/BulkOperations.d.ts +113 -0
- package/dist/tools/seo/BulkOperations.d.ts.map +1 -0
- package/dist/tools/seo/BulkOperations.js +398 -0
- package/dist/tools/seo/BulkOperations.js.map +1 -0
- package/dist/tools/seo/SEOHandlers.d.ts +55 -0
- package/dist/tools/seo/SEOHandlers.d.ts.map +1 -0
- package/dist/tools/seo/SEOHandlers.js +255 -0
- package/dist/tools/seo/SEOHandlers.js.map +1 -0
- package/dist/tools/seo/SEOToolDefinitions.d.ts +59 -0
- package/dist/tools/seo/SEOToolDefinitions.d.ts.map +1 -0
- package/dist/tools/seo/SEOToolDefinitions.js +385 -0
- package/dist/tools/seo/SEOToolDefinitions.js.map +1 -0
- package/dist/tools/seo/SEOTools.d.ts +203 -0
- package/dist/tools/seo/SEOTools.d.ts.map +1 -0
- package/dist/tools/seo/SEOTools.js +708 -0
- package/dist/tools/seo/SEOTools.js.map +1 -0
- package/dist/tools/seo/analyzers/ContentAnalyzer.d.ts +94 -0
- package/dist/tools/seo/analyzers/ContentAnalyzer.d.ts.map +1 -0
- package/dist/tools/seo/analyzers/ContentAnalyzer.js +402 -0
- package/dist/tools/seo/analyzers/ContentAnalyzer.js.map +1 -0
- package/dist/tools/seo/auditors/SiteAuditor.d.ts +121 -0
- package/dist/tools/seo/auditors/SiteAuditor.d.ts.map +1 -0
- package/dist/tools/seo/auditors/SiteAuditor.js +600 -0
- package/dist/tools/seo/auditors/SiteAuditor.js.map +1 -0
- package/dist/tools/seo/generators/MetaGenerator.d.ts +128 -0
- package/dist/tools/seo/generators/MetaGenerator.d.ts.map +1 -0
- package/dist/tools/seo/generators/MetaGenerator.js +547 -0
- package/dist/tools/seo/generators/MetaGenerator.js.map +1 -0
- package/dist/tools/seo/generators/SchemaGenerator.d.ts +204 -0
- package/dist/tools/seo/generators/SchemaGenerator.d.ts.map +1 -0
- package/dist/tools/seo/generators/SchemaGenerator.js +670 -0
- package/dist/tools/seo/generators/SchemaGenerator.js.map +1 -0
- package/dist/tools/seo/index.d.ts +17 -0
- package/dist/tools/seo/index.d.ts.map +1 -0
- package/dist/tools/seo/index.js +18 -0
- package/dist/tools/seo/index.js.map +1 -0
- package/dist/tools/seo/optimizers/InternalLinkingSuggester.d.ts +186 -0
- package/dist/tools/seo/optimizers/InternalLinkingSuggester.d.ts.map +1 -0
- package/dist/tools/seo/optimizers/InternalLinkingSuggester.js +683 -0
- package/dist/tools/seo/optimizers/InternalLinkingSuggester.js.map +1 -0
- package/dist/tools/site.d.ts.map +1 -1
- package/dist/tools/site.js +12 -12
- package/dist/tools/site.js.map +1 -1
- package/dist/tools/taxonomies.d.ts.map +1 -1
- package/dist/tools/taxonomies.js +20 -20
- package/dist/tools/taxonomies.js.map +1 -1
- package/dist/tools/users.d.ts.map +1 -1
- package/dist/tools/users.js +12 -12
- package/dist/tools/users.js.map +1 -1
- package/dist/types/client.d.ts +8 -6
- package/dist/types/client.d.ts.map +1 -1
- package/dist/types/client.js.map +1 -1
- package/dist/types/seo.d.ts +473 -0
- package/dist/types/seo.d.ts.map +1 -0
- package/dist/types/seo.js +94 -0
- package/dist/types/seo.js.map +1 -0
- package/dist/utils/enhancedError.js +1 -1
- package/dist/utils/enhancedError.js.map +1 -1
- package/dist/utils/error.d.ts.map +1 -1
- package/dist/utils/error.js +0 -1
- package/dist/utils/error.js.map +1 -1
- package/dist/utils/index.d.ts +12 -0
- package/dist/utils/index.d.ts.map +1 -0
- package/dist/utils/index.js +18 -0
- package/dist/utils/index.js.map +1 -0
- package/dist/utils/logger.js +3 -3
- package/dist/utils/logger.js.map +1 -1
- package/dist/utils/toolWrapper.d.ts +2 -2
- package/dist/utils/toolWrapper.js +8 -8
- package/dist/utils/toolWrapper.js.map +1 -1
- package/dist/utils/validation/core.d.ts.map +1 -1
- package/dist/utils/validation/core.js.map +1 -1
- package/dist/utils/validation/index.d.ts.map +1 -1
- package/dist/utils/validation/index.js.map +1 -1
- package/dist/utils/validation/network.js +3 -3
- package/dist/utils/validation/network.js.map +1 -1
- package/dist/utils/validation/rateLimit.js.map +1 -1
- package/dist/utils/validation/security.js.map +1 -1
- package/dist/utils/validation/wordpress.js.map +1 -1
- package/dist/utils/version.d.ts +144 -0
- package/dist/utils/version.d.ts.map +1 -0
- package/dist/utils/version.js +318 -0
- package/dist/utils/version.js.map +1 -0
- package/package.json +24 -58
- package/src/cache/CacheInvalidation.ts +183 -20
- package/src/cache/HttpCacheWrapper.ts +8 -5
- package/src/cache/SEOCacheManager.ts +330 -0
- package/src/cache/__tests__/CacheInvalidation.test.ts +6 -11
- package/src/cache/__tests__/CachedWordPressClient.test.ts +37 -62
- package/src/client/SEOWordPressClient.ts +876 -0
- package/src/client/api.ts +50 -21
- package/src/client/auth.ts +19 -19
- package/src/client/index.ts +16 -0
- package/src/client/managers/AuthManager.ts +175 -0
- package/src/client/managers/AuthenticationManager.ts +16 -14
- package/src/client/managers/BaseManager.ts +24 -5
- package/src/client/managers/ComposedAuthenticationManager.ts +409 -0
- package/src/client/managers/ComposedManagerFactory.ts +231 -0
- package/src/client/managers/ComposedRequestManager.ts +336 -0
- package/src/client/managers/JWTAuthImplementation.ts +326 -0
- package/src/client/managers/ManagersIndex.ts +6 -0
- package/src/client/managers/RequestManager.ts +9 -7
- package/src/client/managers/composed/MigrationAdapter.ts +263 -0
- package/src/client/managers/composed/index.ts +47 -0
- package/src/client/managers/implementations/ConfigurationProviderImpl.ts +52 -0
- package/src/client/managers/implementations/ErrorHandlerImpl.ts +102 -0
- package/src/client/managers/implementations/ParameterValidatorImpl.ts +221 -0
- package/src/client/managers/interfaces/ManagerInterfaces.ts +171 -0
- package/src/config/Config.ts +63 -0
- package/src/config/ConfigurationSchema.ts +17 -17
- package/src/config/ServerConfiguration.ts +18 -16
- package/src/config/index.ts +13 -0
- package/src/docs/DocumentationGenerator.ts +2 -2
- package/src/dxt-entry.ts +3 -3
- package/src/index.ts +43 -43
- package/src/performance/MetricsCollector.ts +15 -11
- package/src/security/AISecurityScanner.ts +7 -7
- package/src/security/AutomatedRemediation.ts +13 -11
- package/src/security/InputValidator.ts +10 -9
- package/src/security/SecurityCIPipeline.ts +494 -56
- package/src/security/SecurityConfigManager.ts +10 -10
- package/src/security/SecurityMonitoring.ts +5 -5
- package/src/security/SecurityReviewer.ts +13 -6
- package/src/security/index.ts +3 -3
- package/src/server/ConnectionTester.ts +5 -5
- package/src/server/ToolRegistry.ts +9 -8
- package/src/server/index.ts +10 -0
- package/src/tools/BaseToolManager.ts +55 -83
- package/src/tools/auth.ts +21 -12
- package/src/tools/comments.ts +23 -19
- package/src/tools/index.ts +1 -0
- package/src/tools/media.ts +23 -20
- package/src/tools/pages.ts +20 -13
- package/src/tools/performance.ts +101 -32
- package/src/tools/posts/PostHandlers.ts +23 -23
- package/src/tools/posts/PostToolDefinitions.ts +1 -1
- package/src/tools/posts/index.ts +2 -2
- package/src/tools/seo/BulkOperations.ts +557 -0
- package/src/tools/seo/SEOHandlers.ts +296 -0
- package/src/tools/seo/SEOToolDefinitions.ts +402 -0
- package/src/tools/seo/SEOTools.ts +871 -0
- package/src/tools/seo/analyzers/ContentAnalyzer.ts +493 -0
- package/src/tools/seo/auditors/SiteAuditor.ts +787 -0
- package/src/tools/seo/generators/MetaGenerator.ts +694 -0
- package/src/tools/seo/generators/SchemaGenerator.ts +955 -0
- package/src/tools/seo/index.ts +47 -0
- package/src/tools/seo/optimizers/InternalLinkingSuggester.ts +934 -0
- package/src/tools/site.ts +27 -26
- package/src/tools/taxonomies.ts +29 -25
- package/src/tools/users.ts +20 -13
- package/src/types/client.ts +8 -6
- package/src/types/seo.ts +546 -0
- package/src/utils/enhancedError.ts +1 -1
- package/src/utils/error.ts +1 -2
- package/src/utils/index.ts +23 -0
- package/src/utils/logger.ts +3 -3
- package/src/utils/toolWrapper.ts +10 -10
- package/src/utils/validation/core.ts +2 -2
- package/src/utils/validation/index.ts +2 -2
- package/src/utils/validation/network.ts +5 -5
- package/src/utils/validation/rateLimit.ts +1 -1
- package/src/utils/validation/security.ts +1 -1
- package/src/utils/validation/wordpress.ts +1 -1
- package/src/utils/version.ts +402 -0
- 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;
|