/**
* Feature Discovery System
*
* Discovers and maps WordPress features using the WordPress Feature API
* This aligns with Automattic's approach of semantic feature abstraction
*/
export class FeatureDiscovery {
constructor(wpClient) {
this.wpClient = wpClient;
this.discoveredFeatures = new Map();
}
/**
* Discover features from WordPress Feature API
* This would connect to a WordPress site with the Feature API plugin
*/
async discoverFeatures() {
try {
// In a real implementation, this would query the WordPress Feature API
// For now, we'll simulate the discovery based on Automattic's pattern
const features = await this.queryWordPressFeatures();
// Process and categorize features
for (const feature of features) {
this.processFeature(feature);
}
return this.discoveredFeatures;
} catch (error) {
console.error('Feature discovery failed:', error);
// Fall back to static features
return this.getStaticFeatures();
}
}
/**
* Query WordPress for available features
* In production, this would use WP_Feature_Query
*/
async queryWordPressFeatures() {
// This would make a request to WordPress Feature API endpoint
// Example: /wp-json/wp-features/v1/features
// Simulated response based on Automattic's structure
return [
{
id: 'content/publish-with-seo',
name: 'Publish with SEO',
description: 'Create and publish content with automatic SEO optimization',
type: 'tool',
category: 'content',
rest_alias: '/wp/v2/posts',
is_eligible: 'can_publish_posts',
schema: {
input: {
title: { type: 'string', required: true },
content: { type: 'string', required: true },
seo_focus_keyword: { type: 'string' },
meta_description: { type: 'string' },
},
},
},
{
id: 'content/schedule-series',
name: 'Schedule Content Series',
description: 'Schedule multiple related posts to publish over time',
type: 'tool',
category: 'content',
is_eligible: 'can_publish_posts',
schema: {
input: {
series_title: { type: 'string', required: true },
posts: { type: 'array', items: { type: 'object' } },
schedule: { type: 'object' },
},
},
},
{
id: 'media/smart-gallery',
name: 'Smart Gallery Creation',
description: 'Create optimized image galleries with automatic captions',
type: 'tool',
category: 'media',
is_eligible: 'upload_files',
schema: {
input: {
images: { type: 'array', items: { type: 'string' } },
layout: { type: 'string', enum: ['grid', 'carousel', 'masonry'] },
auto_caption: { type: 'boolean', default: true },
},
},
},
{
id: 'analytics/content-insights',
name: 'Content Performance Insights',
description: 'Get AI-ready insights about content performance',
type: 'resource',
category: 'analytics',
rest_alias: '/wp/v2/analytics/posts',
schema: {
output: {
top_posts: { type: 'array' },
engagement_trends: { type: 'object' },
recommendations: { type: 'array' },
},
},
},
];
}
/**
* Process a discovered feature into our system
*/
processFeature(feature) {
const semanticFeature = {
name: feature.id,
title: feature.name,
description: feature.description,
category: feature.category,
type: feature.type,
// Map eligibility to our personality system
personalities: this.mapEligibilityToPersonalities(feature.is_eligible),
// Convert WordPress schema to our format
inputSchema: this.convertSchema(feature.schema?.input),
// Create semantic execution wrapper
async execute(params, context) {
return this.executeSemanticFeature(feature, params, context);
},
};
this.discoveredFeatures.set(feature.id, semanticFeature);
}
/**
* Map WordPress capabilities to our personality system
*/
mapEligibilityToPersonalities(eligibility) {
if (!eligibility) return ['contributor', 'author', 'administrator'];
const capabilityMap = {
can_publish_posts: ['author', 'administrator'],
edit_others_posts: ['administrator'],
manage_options: ['administrator'],
upload_files: ['author', 'administrator'],
edit_posts: ['contributor', 'author', 'administrator'],
};
return capabilityMap[eligibility] || ['administrator'];
}
/**
* Convert WordPress Feature API schema to MCP schema
*/
convertSchema(wpSchema) {
if (!wpSchema) {
return {
type: 'object',
properties: {},
additionalProperties: true,
};
}
// Transform WordPress schema format to JSON Schema
const properties = {};
const required = [];
for (const [key, value] of Object.entries(wpSchema)) {
properties[key] = {
type: value.type,
description: value.description || `${key} parameter`,
};
if (value.required) {
required.push(key);
}
if (value.enum) {
properties[key].enum = value.enum;
}
if (value.default !== undefined) {
properties[key].default = value.default;
}
}
return {
type: 'object',
properties,
required: required.length > 0 ? required : undefined,
};
}
/**
* Execute a semantic feature with WordPress business logic
*/
async executeSemanticFeature(feature, params, context) {
const { wpClient } = context;
// Handle different feature types
switch (feature.type) {
case 'tool':
return this.executeToolFeature(feature, params, wpClient);
case 'resource':
return this.executeResourceFeature(feature, params, wpClient);
case 'prompt':
return this.executePromptFeature(feature, params, wpClient);
default:
throw new Error(`Unknown feature type: ${feature.type}`);
}
}
/**
* Execute tool-type features (actions)
*/
async executeToolFeature(feature, params, wpClient) {
// Example: content/publish-with-seo
if (feature.id === 'content/publish-with-seo') {
// Orchestrate multiple operations
const post = await wpClient.createPost({
title: params.title,
content: params.content,
status: 'publish',
});
// Add SEO metadata (would use Yoast or similar)
if (params.seo_focus_keyword || params.meta_description) {
await this.addSeoMetadata(
post.id,
{
focus_keyword: params.seo_focus_keyword,
meta_description: params.meta_description,
},
wpClient
);
}
return {
success: true,
postId: post.id,
url: post.link,
seo_optimized: true,
message: `Published with SEO optimization: "${params.title}"`,
};
}
// Default execution using rest_alias if available
if (feature.rest_alias) {
return wpClient.request(feature.rest_alias, {
method: 'POST',
body: JSON.stringify(params),
});
}
throw new Error(`No execution handler for feature: ${feature.id}`);
}
/**
* Execute resource-type features (data providers)
*/
async executeResourceFeature(feature, params, wpClient) {
if (feature.rest_alias) {
return wpClient.request(feature.rest_alias);
}
throw new Error(`No resource handler for feature: ${feature.id}`);
}
/**
* Execute prompt-type features (templates)
*/
async executePromptFeature(feature, params, wpClient) {
// Prompts would return pre-configured templates or workflows
return {
template: feature.template,
variables: params,
instructions: feature.instructions,
};
}
/**
* Add SEO metadata to a post
*/
async addSeoMetadata(postId, seoData, wpClient) {
// This would integrate with SEO plugins like Yoast
// For now, add as post meta
return wpClient.updatePost(postId, {
meta: {
_yoast_wpseo_focuskw: seoData.focus_keyword,
_yoast_wpseo_metadesc: seoData.meta_description,
},
});
}
/**
* Get static features as fallback
*/
getStaticFeatures() {
// Return a basic set of features if discovery fails
return new Map([
[
'content/create-draft',
{
name: 'content/create-draft',
title: 'Create Draft',
description: 'Create a draft post',
personalities: ['contributor', 'author', 'administrator'],
async execute(params, context) {
return context.wpClient.createPost({
...params,
status: 'draft',
});
},
},
],
]);
}
}