This post was created by an AI agent using MCP!
", - "status": "draft", - "category": "AI Content" - } - } - }' +# Create a draft post using WP-CLI +echo '{"jsonrpc":"2.0","id":1,"method":"tools/call","params":{"name":"my-plugin-create-post","arguments":{"title":"My First MCP Post","content":"This post was created using MCP!","status":"draft"}}}' | wp mcp-adapter serve --user=admin --server=mcp-adapter-default-server ``` -## Example 2: Resource - Site Statistics +## Example 2: Resource - Site Configuration -Resources provide data access without complex parameters. Here's a site statistics resource: +Resources provide access to data. They require a `uri` in the ability meta: ```php 'Site Statistics', - 'description' => 'Provides comprehensive statistics about the WordPress site', - 'input_schema' => [ - 'type' => 'object', - 'properties' => [ - 'detailed' => [ - 'type' => 'boolean', - 'description' => 'Include detailed breakdown by post type', - 'default' => false - ] - ] - ], - 'output_schema' => [ - 'type' => 'object', - 'properties' => [ - 'site_info' => [ - 'type' => 'object', - 'properties' => [ - 'name' => ['type' => 'string'], - 'url' => ['type' => 'string'], - 'description' => ['type' => 'string'], - 'wordpress_version' => ['type' => 'string'] - ] - ], - 'content_stats' => [ - 'type' => 'object', - 'properties' => [ - 'total_posts' => ['type' => 'integer'], - 'total_pages' => ['type' => 'integer'], - 'total_comments' => ['type' => 'integer'], - 'post_types' => [ - 'type' => 'object', - 'description' => 'Detailed breakdown by post type (if detailed=true)' - ] - ] - ], - 'user_stats' => [ - 'type' => 'object', - 'properties' => [ - 'total_users' => ['type' => 'integer'], - 'user_roles' => ['type' => 'object'] - ] - ] - ] - ], - 'execute_callback' => function( $input ) { - $detailed = $input['detailed'] ?? false; - - // Site information - $site_info = [ - 'name' => get_bloginfo( 'name' ), - 'url' => get_site_url(), - 'description' => get_bloginfo( 'description' ), - 'wordpress_version' => get_bloginfo( 'version' ) - ]; - - // Content statistics - $content_stats = [ - 'total_posts' => wp_count_posts( 'post' )->publish, - 'total_pages' => wp_count_posts( 'page' )->publish, - 'total_comments' => wp_count_comments()['approved'] - ]; - - // Detailed post type breakdown if requested - if ( $detailed ) { - $post_types = get_post_types( ['public' => true], 'objects' ); - $post_type_counts = []; - foreach ( $post_types as $post_type ) { - $counts = wp_count_posts( $post_type->name ); - $post_type_counts[ $post_type->name ] = [ - 'label' => $post_type->label, - 'published' => $counts->publish ?? 0, - 'draft' => $counts->draft ?? 0, - 'total' => array_sum( (array) $counts ) - ]; - } - $content_stats['post_types'] = $post_type_counts; - } - - // User statistics - $user_count = count_users(); - $user_stats = [ - 'total_users' => $user_count['total_users'], - 'user_roles' => $user_count['avail_roles'] - ]; - +add_action( 'wp_abilities_api_init', function() { + wp_register_ability( 'my-plugin/site-config', [ + 'label' => 'Site Configuration', + 'description' => 'WordPress site configuration and settings', + 'execute_callback' => function() { return [ - 'site_info' => $site_info, - 'content_stats' => $content_stats, - 'user_stats' => $user_stats + 'site_name' => get_bloginfo( 'name' ), + 'site_url' => get_site_url(), + 'admin_email' => get_option( 'admin_email' ), + 'timezone' => get_option( 'timezone_string' ), + 'date_format' => get_option( 'date_format' ), + 'wordpress_version' => get_bloginfo( 'version' ) ]; }, 'permission_callback' => function() { return current_user_can( 'manage_options' ); - } + }, + 'meta' => [ + 'uri' => 'wordpress://site/config', // Required for resources + 'annotations' => [ + 'readOnlyHint' => true, + 'idempotentHint' => true, + 'audience' => ['user', 'assistant'], + 'priority' => 0.8 + ], + 'mcp' => [ + 'public' => true, // Expose this ability via MCP + 'type' => 'resource' // Mark as resource for auto-discovery + ] + ] ]); }); - -// Set up the MCP server with the resource -add_action( 'mcp_adapter_init', function( $adapter ) { - $adapter->create_server( - 'site-info-server', - 'my-plugin', - 'info', - 'Site Information Server', - 'Provides site statistics and information', - '1.0.0', - [ \WP\MCP\Transport\Http\RestTransport::class ], - \WP\MCP\Infrastructure\ErrorHandling\ErrorLogMcpErrorHandler::class, - [], // No tools - [ 'my-plugin/site-stats' ], // Expose as resource - [] // No prompts - ); -}); ``` +The ability is automatically available via the default MCP server. + ### Testing the Resource ```bash -# Get basic site statistics -curl -X POST "https://yoursite.com/wp-json/my-plugin/info" \ - -H "Content-Type: application/json" \ - -d '{ - "method": "resources/read", - "params": { - "uri": "my-plugin--site-stats" - } - }' +# Read the site configuration resource +echo '{"jsonrpc":"2.0","id":1,"method":"resources/read","params":{"uri":"wordpress://site/config"}}' | wp mcp-adapter serve --user=admin --server=mcp-adapter-default-server -# Get detailed statistics -curl -X POST "https://yoursite.com/wp-json/my-plugin/info" \ - -H "Content-Type: application/json" \ - -d '{ - "method": "resources/read", - "params": { - "uri": "my-plugin--site-stats?detailed=true" - } - }' +# List all available resources +echo '{"jsonrpc":"2.0","id":1,"method":"resources/list","params":{}}' | wp mcp-adapter serve --user=admin --server=mcp-adapter-default-server ``` -## Example 3: Prompt - SEO Recommendations +## Example 3: Prompt - Code Review -Prompts provide structured guidance for AI agents. Here's an SEO recommendation prompt: +Prompts generate structured messages for language models: ```php 'SEO Recommendations', - 'description' => 'Generates SEO recommendations based on site analysis', - 'input_schema' => [ - 'type' => 'object', - 'properties' => [ - 'post_id' => [ - 'type' => 'integer', - 'description' => 'Specific post ID to analyze (optional)' - ], - 'focus_area' => [ - 'type' => 'string', - 'description' => 'Specific SEO area to focus on', - 'enum' => ['content', 'technical', 'keywords', 'all'], - 'default' => 'all' - ] - ] - ], - 'output_schema' => [ - 'type' => 'object', - 'properties' => [ - 'analysis_summary' => ['type' => 'string'], - 'recommendations' => [ - 'type' => 'array', - 'items' => [ - 'type' => 'object', - 'properties' => [ - 'category' => ['type' => 'string'], - 'priority' => ['type' => 'string'], - 'recommendation' => ['type' => 'string'], - 'implementation' => ['type' => 'string'] - ] - ] - ], - 'next_steps' => ['type' => 'string'] - ] - ], +add_action( 'wp_abilities_api_init', function() { + wp_register_ability( 'my-plugin/code-review', [ + 'label' => 'Code Review Prompt', + 'description' => 'Generate a code review prompt with specific focus areas', 'execute_callback' => function( $input ) { - $focus_area = $input['focus_area'] ?? 'all'; - $post_id = $input['post_id'] ?? null; - - $recommendations = []; - $analysis_context = ''; - - if ( $post_id ) { - $post = get_post( $post_id ); - if ( ! $post ) { - throw new Exception( 'Post not found' ); - } - $analysis_context = "Analysis for post: \"{$post->post_title}\""; - - // Post-specific recommendations - if ( in_array( $focus_area, ['content', 'all'] ) ) { - $content_length = str_word_count( strip_tags( $post->post_content ) ); - if ( $content_length < 300 ) { - $recommendations[] = [ - 'category' => 'Content', - 'priority' => 'High', - 'recommendation' => 'Increase content length to at least 300 words', - 'implementation' => 'Add more detailed information, examples, or explanations to reach the recommended word count' - ]; - } - - if ( empty( get_post_meta( $post_id, '_yoast_wpseo_metadesc', true ) ) ) { - $recommendations[] = [ - 'category' => 'Content', - 'priority' => 'Medium', - 'recommendation' => 'Add a meta description', - 'implementation' => 'Write a compelling 150-160 character meta description that summarizes the post content' - ]; - } - } - } else { - $analysis_context = 'Site-wide SEO analysis'; - - // Site-wide recommendations - if ( in_array( $focus_area, ['technical', 'all'] ) ) { - // Check if sitemap exists - $sitemap_exists = wp_remote_get( get_site_url() . '/sitemap.xml' ); - if ( is_wp_error( $sitemap_exists ) || wp_remote_retrieve_response_code( $sitemap_exists ) !== 200 ) { - $recommendations[] = [ - 'category' => 'Technical', - 'priority' => 'High', - 'recommendation' => 'Create an XML sitemap', - 'implementation' => 'Install an SEO plugin like Yoast or RankMath to generate sitemaps automatically' - ]; - } - - // Check for HTTPS - if ( ! is_ssl() ) { - $recommendations[] = [ - 'category' => 'Technical', - 'priority' => 'High', - 'recommendation' => 'Enable HTTPS/SSL', - 'implementation' => 'Contact your hosting provider to install an SSL certificate and configure WordPress to use HTTPS' - ]; - } - } - - if ( in_array( $focus_area, ['content', 'all'] ) ) { - // Check for recent content - $recent_posts = get_posts( [ - 'numberposts' => 1, - 'post_status' => 'publish', - 'date_query' => [ - 'after' => '30 days ago' - ] - ] ); - - if ( empty( $recent_posts ) ) { - $recommendations[] = [ - 'category' => 'Content', - 'priority' => 'Medium', - 'recommendation' => 'Publish fresh content regularly', - 'implementation' => 'Create a content calendar and aim to publish at least one new post per month' - ]; - } - } - } - - // Default recommendations if none found - if ( empty( $recommendations ) ) { - $recommendations[] = [ - 'category' => 'General', - 'priority' => 'Low', - 'recommendation' => 'SEO analysis shows good optimization', - 'implementation' => 'Continue monitoring and maintaining current SEO practices' - ]; - } - - $summary = $analysis_context . ". Found " . count( $recommendations ) . " optimization opportunities."; - $next_steps = "Implement high-priority recommendations first, then work through medium and low priority items. Monitor search rankings and traffic after changes."; + $code = $input['code'] ?? ''; + $focus = $input['focus'] ?? ['security', 'performance']; return [ - 'analysis_summary' => $summary, - 'recommendations' => $recommendations, - 'next_steps' => $next_steps + 'messages' => [ + [ + 'role' => 'user', + 'content' => [ + 'type' => 'text', + 'text' => "Please review this code focusing on: " . implode(', ', $focus) . "\n\n```\n" . $code . "\n```", + 'annotations' => [ + 'audience' => ['assistant'], + 'priority' => 0.9 + ] + ] + ] + ] ]; }, 'permission_callback' => function() { return current_user_can( 'edit_posts' ); - } + }, + 'meta' => [ + 'arguments' => [ + [ + 'name' => 'code', + 'description' => 'Code to review', + 'required' => true + ], + [ + 'name' => 'focus', + 'description' => 'Areas to focus on during review', + 'required' => false + ] + ], + 'annotations' => [ + 'readOnlyHint' => true, + 'idempotentHint' => true + ], + 'mcp' => [ + 'public' => true, // Expose this ability via MCP + 'type' => 'prompt' // Mark as prompt for auto-discovery + ] + ] ]); }); - -// Set up the MCP server with the prompt -add_action( 'mcp_adapter_init', function( $adapter ) { - $adapter->create_server( - 'seo-advisor', - 'my-plugin', - 'seo', - 'SEO Advisory Server', - 'Provides SEO analysis and recommendations', - '1.0.0', - [ \WP\MCP\Transport\Http\RestTransport::class ], - \WP\MCP\Infrastructure\ErrorHandling\ErrorLogMcpErrorHandler::class, - [], // No tools - [], // No resources - [ 'my-plugin/seo-recommendations' ] // Expose as prompt - ); -}); ``` +The ability is automatically available via the default MCP server. + ### Testing the Prompt ```bash -# Get site-wide SEO recommendations -curl -X POST "https://yoursite.com/wp-json/my-plugin/seo" \ - -H "Content-Type: application/json" \ - -d '{ - "method": "prompts/get", - "params": { - "name": "my-plugin--seo-recommendations" - } - }' +# Get a code review prompt +echo '{"jsonrpc":"2.0","id":1,"method":"prompts/get","params":{"name":"my-plugin-code-review","arguments":{"code":"function hello() { console.log(\"world\"); }","focus":["security","performance"]}}}' | wp mcp-adapter serve --user=admin --server=mcp-adapter-default-server -# Get recommendations for a specific post -curl -X POST "https://yoursite.com/wp-json/my-plugin/seo" \ - -H "Content-Type: application/json" \ - -d '{ - "method": "prompts/get", - "params": { - "name": "my-plugin--seo-recommendations", - "arguments": { - "post_id": 123 - } - } - }' - -# Focus on technical SEO only -curl -X POST "https://yoursite.com/wp-json/my-plugin/seo" \ - -H "Content-Type: application/json" \ - -d '{ - "method": "prompts/get", - "params": { - "name": "my-plugin--seo-recommendations", - "arguments": { - "focus_area": "technical" - } - } - }' +# List all available prompts +echo '{"jsonrpc":"2.0","id":1,"method":"prompts/list","params":{}}' | wp mcp-adapter serve --user=admin --server=mcp-adapter-default-server ``` -## Combining Multiple Components - -You can create a single server that exposes the same ability in different ways: - -```php -add_action( 'mcp_adapter_init', function( $adapter ) { - $adapter->create_server( - 'complete-server', - 'my-plugin', - 'complete', - 'Complete MCP Server', - 'Demonstrates tools, resources, and prompts together', - '1.0.0', - [ \WP\MCP\Transport\Http\RestTransport::class ], - \WP\MCP\Infrastructure\ErrorHandling\ErrorLogMcpErrorHandler::class, - [ 'my-plugin/create-post' ], // Tools - [ 'my-plugin/site-stats' ], // Resources - [ 'my-plugin/seo-recommendations' ] // Prompts - ); -}); -``` +## Key Points -## Error Handling Examples +### Default Server +The MCP Adapter automatically creates a default server that exposes all registered abilities: +- **Endpoint**: `/wp-json/mcp/mcp-adapter-default-server` +- **Server ID**: `mcp-adapter-default-server` +- **Automatic Registration**: All abilities become available immediately -Add proper error handling to your abilities: +### Component Types +- **Tools**: Execute actions (like `tools/call`) +- **Resources**: Provide data access (like `resources/read`) - require `meta.uri` +- **Prompts**: Generate messages (like `prompts/get`) - return `messages` array -```php -'execute_callback' => function( $input ) { - try { - // Validate input - if ( empty( $input['title'] ) ) { - throw new InvalidArgumentException( 'Title is required' ); - } - - // Perform operation - $result = wp_insert_post( $post_data ); - - if ( is_wp_error( $result ) ) { - throw new Exception( 'WordPress error: ' . $result->get_error_message() ); - } - - return $result; - - } catch ( InvalidArgumentException $e ) { - // Client error - invalid input - throw $e; - } catch ( Exception $e ) { - // Server error - log and re-throw - error_log( 'MCP Error in ' . __FUNCTION__ . ': ' . $e->getMessage() ); - throw new Exception( 'Operation failed. Please try again.' ); - } -} -``` +### Annotations +All MCP components may include metadata in `meta.annotations`, which hint at how clients should treat them. +For full details on annotations, their semantics, and usage guidelines, see the Annotations section of the MCP schema spec: https://modelcontextprotocol.io/specification/2025-06-18/schema#annotations -## Observability and Monitoring +### Testing +Use WP-CLI with the default server: +```bash +# List all available tools +echo '{"jsonrpc":"2.0","id":1,"method":"tools/list","params":{}}' | wp mcp-adapter serve --user=admin --server=mcp-adapter-default-server -The MCP Adapter automatically tracks metrics for all operations. You can customize observability by providing a custom handler: +# List all available resources +echo '{"jsonrpc":"2.0","id":1,"method":"resources/list","params":{}}' | wp mcp-adapter serve --user=admin --server=mcp-adapter-default-server -```php -add_action( 'mcp_adapter_init', function( $adapter ) { - $adapter->create_server( - 'monitored-server', - 'my-plugin', - 'monitored', - 'Monitored MCP Server', - 'Server with custom observability', - '1.0.0', - [ \WP\MCP\Transport\Http\RestTransport::class ], - \WP\MCP\Infrastructure\ErrorHandling\ErrorLogMcpErrorHandler::class, - [ 'my-plugin/create-post' ], - [], - [], - \WP\MCP\Infrastructure\Observability\ErrorLogMcpObservabilityHandler::class // Custom observability - ); -}); +# List all available prompts +echo '{"jsonrpc":"2.0","id":1,"method":"prompts/list","params":{}}' | wp mcp-adapter serve --user=admin --server=mcp-adapter-default-server ``` -Metrics include request counts, execution timing, error rates, and permission events. For production environments, consider implementing a custom observability handler that integrates with your monitoring systems. - ## Next Steps -- **Learn about [Advanced Abilities](../guides/creating-abilities.md)** for more complex implementations -- **Explore [Custom Transports](../guides/custom-transports.md)** for specialized communication needs -- **Check out [Creating Abilities](../guides/creating-abilities.md)** with full implementation guide -- **Read the [Architecture Guide](../architecture/overview.md)** to understand the system design +- **[Creating Abilities](../guides/creating-abilities.md)** - Complete implementation guide +- **[Error Handling](../guides/error-handling.md)** - Custom logging and monitoring +- **[Architecture Overview](../architecture/overview.md)** - System design -These basic examples should give you a solid foundation for building your own MCP integrations! +These examples provide a foundation for building MCP integrations with WordPress abilities. diff --git a/docs/getting-started/installation.md b/docs/getting-started/installation.md index 18d9d2f..56d52bb 100644 --- a/docs/getting-started/installation.md +++ b/docs/getting-started/installation.md @@ -1,53 +1,47 @@ # Installation Guide -This guide covers different installation methods for the MCP Adapter, from simple manual installation to advanced -Composer-based workflows. +This guide covers different installation methods for the MCP Adapter. ## Installation Methods -### Method 1: Manual Installation (Recommended for Most Users) +### Method 1: Composer Package (Recommended) -The MCP Adapter works perfectly without Composer by using the included Jetpack autoloader. This is the simplest method -and works in any WordPress environment. +The MCP Adapter is designed to be installed as a Composer package. This is the primary and recommended installation method: -#### Download and Setup +```bash +composer require wordpress/abilities-api wordpress/mcp-adapter +``` -1. **Download the library** to your WordPress installation: - ```bash - # Navigate to your WordPress wp-content directory - cd /path/to/your/wordpress/wp-content/lib/ - - # Clone or download the MCP adapter - git clone https://github.com/your-org/mcp-adapter.git - ``` +#### Using Jetpack Autoloader (Highly Recommended) -2. **Load the autoloader** in your plugin or theme: - ```php - load_mcp_adapter(); + // Check if MCP Adapter is available + if ( ! class_exists( 'WP\MCP\Core\McpAdapter' ) ) { + add_action( 'admin_notices', [ $this, 'missing_mcp_adapter_notice' ] ); + return; + } - // Ensure WordPress Abilities API is available + // Check if Abilities API is available if ( ! function_exists( 'wp_register_ability' ) ) { add_action( 'admin_notices', [ $this, 'missing_abilities_api_notice' ] ); return; } - // Initialize your MCP functionality + // Register your abilities and MCP server $this->register_abilities(); $this->setup_mcp_server(); } - private function load_mcp_adapter() { - if ( ! class_exists( 'WP\MCP\Core\McpAdapter' ) ) { - $autoloader_path = ABSPATH . 'wp-content/lib/mcp-adapter/vendor/autoload_packages.php'; - if ( is_file( $autoloader_path ) ) { - require_once $autoloader_path; - } - } - } - private function register_abilities() { - // Your ability registration code here + add_action( 'wp_abilities_api_init', function() { + wp_register_ability( 'my-plugin/get-posts', [ + 'label' => 'Get Posts', + 'description' => 'Retrieve WordPress posts', + 'input_schema' => [ + 'type' => 'object', + 'properties' => [ + 'numberposts' => [ + 'type' => 'integer', + 'default' => 5, + 'minimum' => 1, + 'maximum' => 100 + ] + ] + ], + 'execute_callback' => function( $input ) { + return get_posts( [ 'numberposts' => $input['numberposts'] ?? 5 ] ); + }, + 'permission_callback' => function() { + return current_user_can( 'read' ); + } + ]); + }); } private function setup_mcp_server() { @@ -101,7 +111,23 @@ class MyMcpPlugin { } public function create_mcp_server( $adapter ) { - // Your server creation code here + $adapter->create_server( + 'my-plugin-server', + 'my-plugin', + 'mcp', + 'My Plugin MCP Server', + 'Custom MCP server for my plugin', + '1.0.0', + [ \WP\MCP\Transport\HttpTransport::class ], + \WP\MCP\Infrastructure\ErrorHandling\ErrorLogMcpErrorHandler::class, + [ 'my-plugin/get-posts' ] + ); + } + + public function missing_mcp_adapter_notice() { + echo ''; + echo 'My MCP Plugin requires the MCP Adapter plugin to be active.'; + echo '
MCP Adapter plugin must be active.
'; - echo 'WordPress Abilities API is required for MCP functionality.'; - echo '
WordPress Abilities API is required.
'; - echo 'MCP requires permalinks to be set to something other than "Plain". '; - echo 'Update permalinks'; - echo '
+ +
+