| 1 |
<?php |
| 2 |
|
| 3 |
namespace FluentBoards\App\Modules\MCP; |
| 4 |
|
| 5 |
/** |
| 6 |
* Bootstrap for Fluent Boards' Model Context Protocol integration. |
| 7 |
* |
| 8 |
* Mirrors FluentCRM's WordPress-native architecture: abilities are registered |
| 9 |
* through the WordPress Abilities API and exposed by the WP MCP Adapter at a |
| 10 |
* dedicated Fluent Boards MCP endpoint. |
| 11 |
*/ |
| 12 |
class MCPInit |
| 13 |
{ |
| 14 |
public function init() |
| 15 |
{ |
| 16 |
add_action('wp_abilities_api_categories_init', [$this, 'registerCategory']); |
| 17 |
add_action('wp_abilities_api_init', [$this, 'registerAbilities']); |
| 18 |
add_action('mcp_adapter_init', [$this, 'registerCustomServer']); |
| 19 |
} |
| 20 |
|
| 21 |
public function registerCategory() |
| 22 |
{ |
| 23 |
wp_register_ability_category('fluent-boards', [ |
| 24 |
'label' => __('FluentBoards', 'fluent-boards'), |
| 25 |
'description' => __('Board, stage, task, and comment abilities for Fluent Boards.', 'fluent-boards'), |
| 26 |
]); |
| 27 |
} |
| 28 |
|
| 29 |
public function registerAbilities() |
| 30 |
{ |
| 31 |
AbilitiesRegistrar::register(); |
| 32 |
|
| 33 |
/** |
| 34 |
* Fires after Fluent Boards has registered its core MCP abilities. |
| 35 |
* |
| 36 |
* Extensions can hook here and register additional abilities under the |
| 37 |
* same `fluent-boards/` namespace. |
| 38 |
* |
| 39 |
* @since 1.0.0 |
| 40 |
*/ |
| 41 |
do_action('fluent_boards/mcp_loaded'); |
| 42 |
} |
| 43 |
|
| 44 |
/** |
| 45 |
* Register the dedicated Fluent Boards MCP server. |
| 46 |
* |
| 47 |
* @param object $adapter WP MCP Adapter instance. |
| 48 |
*/ |
| 49 |
public function registerCustomServer($adapter) |
| 50 |
{ |
| 51 |
if (!$adapter || !is_object($adapter) || !method_exists($adapter, 'create_server')) { |
| 52 |
return; |
| 53 |
} |
| 54 |
|
| 55 |
$abilityNames = array_keys(AbilitiesRegistrar::getDefinitions()); |
| 56 |
$abilityNames = apply_filters('fluent_boards/mcp_ability_names', $abilityNames); |
| 57 |
|
| 58 |
$namespace = apply_filters('fluent_boards/mcp_server_namespace', 'fluent-boards'); |
| 59 |
$route = apply_filters('fluent_boards/mcp_server_route', 'mcp'); |
| 60 |
|
| 61 |
$adapter->create_server( |
| 62 |
'fluent-boards', |
| 63 |
$namespace, |
| 64 |
$route, |
| 65 |
__('Fluent Boards MCP Server', 'fluent-boards'), |
| 66 |
__('AI agent tools for Fluent Boards projects, stages, tasks, and comments.', 'fluent-boards'), |
| 67 |
defined('FLUENT_BOARDS_PLUGIN_VERSION') ? FLUENT_BOARDS_PLUGIN_VERSION : '1.0.0', |
| 68 |
['\WP\MCP\Transport\HttpTransport'], |
| 69 |
'\WP\MCP\Infrastructure\ErrorHandling\ErrorLogMcpErrorHandler', |
| 70 |
'\WP\MCP\Infrastructure\Observability\NullMcpObservabilityHandler', |
| 71 |
array_values(array_unique(array_filter((array) $abilityNames))) |
| 72 |
); |
| 73 |
} |
| 74 |
|
| 75 |
public static function getEndpointUrl() |
| 76 |
{ |
| 77 |
$namespace = apply_filters('fluent_boards/mcp_server_namespace', 'fluent-boards'); |
| 78 |
$route = apply_filters('fluent_boards/mcp_server_route', 'mcp'); |
| 79 |
|
| 80 |
return get_rest_url(null, trailingslashit($namespace) . $route); |
| 81 |
} |
| 82 |
|
| 83 |
public static function getToolsCount() |
| 84 |
{ |
| 85 |
$names = apply_filters('fluent_boards/mcp_ability_names', array_keys(AbilitiesRegistrar::getDefinitions())); |
| 86 |
|
| 87 |
if (!is_array($names)) { |
| 88 |
return count(AbilitiesRegistrar::getDefinitions()); |
| 89 |
} |
| 90 |
|
| 91 |
return count(array_unique($names)); |
| 92 |
} |
| 93 |
|
| 94 |
/** |
| 95 |
* Register FluentBoards in FluentKit's MCP products screen. |
| 96 |
* |
| 97 |
* @param array $products Existing MCP products. |
| 98 |
* @param array $adapter FluentKit adapter status. |
| 99 |
* @return array |
| 100 |
*/ |
| 101 |
public static function registerToolkitProduct($products, $adapter = []) |
| 102 |
{ |
| 103 |
$enabled = fluent_boards_get_option('mcp_enabled', 'yes') === 'yes'; |
| 104 |
$adapterAvailable = !empty($adapter['available']); |
| 105 |
|
| 106 |
if (!$adapterAvailable) { |
| 107 |
$status = 'adapter_required'; |
| 108 |
} elseif (!$enabled) { |
| 109 |
$status = 'disabled'; |
| 110 |
} else { |
| 111 |
$status = 'ready'; |
| 112 |
} |
| 113 |
|
| 114 |
$products[] = [ |
| 115 |
'slug' => 'fluent-boards', |
| 116 |
'title' => __('FluentBoards', 'fluent-boards'), |
| 117 |
'endpoint_url' => self::getEndpointUrl(), |
| 118 |
'tools_count' => self::getToolsCount(), |
| 119 |
'status' => $status, |
| 120 |
'enabled' => $enabled ? 'yes' : 'no', |
| 121 |
]; |
| 122 |
|
| 123 |
return $products; |
| 124 |
} |
| 125 |
} |
| 126 |
|