PluginProbe
Fluent Support – Helpdesk & Customer Support Ticket System / 2.3.0
Fluent Support – Helpdesk & Customer Support Ticket System v2.3.0
2.3.2 2.3.1 2.3.0 2.2.1 2.2.0 trunk 1.10.0 1.10.1 1.10.2 1.10.3 1.10.4 1.10.5 1.4.0 1.4.1 1.4.2 1.4.5 1.4.6 1.4.7 1.5.0 1.5.1 1.5.2 1.5.3 1.5.4 1.5.5 1.5.6 All 67 releases
fluent-support / app / Modules / MCP / MCPInit.php

MCPInit.php in Fluent Support – Helpdesk & Customer Support Ticket System 2.3.0, at app/Modules/MCP/MCPInit.php

231 lines 8.4 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 namespace FluentSupport\App\Modules\MCP;
4
5 use FluentSupport\App\Modules\MCP\Support\PermissionGate;
6 use FluentSupport\App\Modules\MCP\Tools\ManagementTools;
7
8 /**
9 * Bootstrap for Fluent Support's Model Context Protocol (MCP) integration.
10 *
11 * Wires the WordPress Abilities API (core 6.9+) + the WP MCP Adapter (provided
12 * by FluentHub or the standalone mcp-adapter plugin). Fluent Support bundles
13 * nothing; it consumes whatever is loaded. If neither is available an admin
14 * notice is shown when MCP is enabled.
15 *
16 * The whole surface is gated behind the `_mcp_settings` option (default off).
17 * Even when on, the endpoint stays behind WP auth + a Fluent Support agent
18 * permission (transport gate) + per-ability permission_callback checks.
19 *
20 * Entry point: MCPInit::boot(), called from app/Hooks/actions.php.
21 */
22 class MCPInit
23 {
24 const SERVER_ID = 'fluent-support';
25
26 /**
27 * Bootstrap entry point, called once from app/Hooks/actions.php.
28 *
29 * Toolkit discovery runs UNCONDITIONALLY so FluentHub can list Fluent
30 * Support on its MCP page even while disabled. The actual server
31 * registration only happens when MCP is enabled — zero overhead by default.
32 */
33 public static function boot()
34 {
35 // Toolkit discovery runs unconditionally — FluentHub needs this to list
36 // Fluent Support on its MCP page even on WP < 6.9 or when MCP is off.
37 self::registerWithToolkit();
38
39 // Server init requires both the feature flag and the WP 6.9 Abilities API.
40 if (PermissionGate::isEnabled() && function_exists('wp_register_ability')) {
41 (new self())->init();
42 }
43 }
44
45 public function init()
46 {
47 // Abilities API hooks (fire only on WP 6.9+).
48 add_action('wp_abilities_api_categories_init', [$this, 'registerCategory']);
49 add_action('wp_abilities_api_init', [$this, 'registerAbilities']);
50
51 // Dedicated server registration (fires only when an adapter is loaded).
52 add_action('mcp_adapter_init', [$this, 'registerCustomServer']);
53
54 // Warn the operator if MCP is on but no adapter is installed.
55 add_action('admin_notices', [$this, 'maybeShowAdapterNotice']);
56
57 // Keep get-support-context fresh: invalidate its cache when anything it reports changes.
58 $invalidate = [ManagementTools::class, 'invalidateSupportContextCache'];
59 foreach ([
60 'fluent_support/ticket_created',
61 'fluent_support/ticket_closed',
62 'fluent_support/agent_assigned_to_ticket',
63 'fluent_support/mailbox_saved',
64 'fluent_support/agent_created',
65 ] as $hook) {
66 add_action($hook, $invalidate);
67 }
68 }
69
70 public function registerCategory()
71 {
72 wp_register_ability_category(self::SERVER_ID, [
73 'label' => __('Fluent Support', 'fluent-support'),
74 'description' => __('Helpdesk ticket management abilities for Fluent Support.', 'fluent-support'),
75 ]);
76 }
77
78 public function registerAbilities()
79 {
80 AbilitiesRegistrar::register();
81
82 /**
83 * Fires after Fluent Support registers its core MCP abilities. Pro
84 * hooks this to register additional abilities under the same namespace.
85 *
86 * @since 1.0.0
87 */
88 do_action('fluent_support/mcp_loaded');
89 }
90
91 /**
92 * Register the dedicated Fluent Support MCP server.
93 * Endpoint: /wp-json/fluent-support/mcp
94 *
95 * @param object $adapter The \WP\MCP\Core\McpAdapter instance.
96 */
97 public function registerCustomServer($adapter)
98 {
99 if (!$adapter || !is_object($adapter) || !method_exists($adapter, 'create_server')) {
100 return;
101 }
102
103 $abilityNames = array_keys(AbilitiesRegistrar::getDefinitions());
104
105 /**
106 * Filter the ability names exposed by the Fluent Support MCP server.
107 * Pro and extensions push their ability names here.
108 *
109 * @since 1.0.0
110 *
111 * @param array $abilityNames Fully-qualified ability names.
112 */
113 $abilityNames = apply_filters('fluent_support/mcp_ability_names', $abilityNames);
114 $abilityNames = array_values(array_unique(array_filter(
115 (array) $abilityNames,
116 fn($n) => is_string($n) && preg_match('/^[a-z0-9\-\/]+$/', $n)
117 )));
118
119 $namespace = sanitize_key(apply_filters('fluent_support/mcp_server_namespace', self::SERVER_ID)) ?: self::SERVER_ID;
120 $route = sanitize_key(apply_filters('fluent_support/mcp_server_route', 'mcp')) ?: 'mcp';
121
122 $adapter->create_server(
123 self::SERVER_ID,
124 $namespace,
125 $route,
126 __('Fluent Support MCP Server', 'fluent-support'),
127 __('AI agent tools for helpdesk ticket management.', 'fluent-support'),
128 defined('FLUENT_SUPPORT_VERSION') ? FLUENT_SUPPORT_VERSION : '1.0.0',
129 ['\WP\MCP\Transport\HttpTransport'],
130 '\WP\MCP\Infrastructure\ErrorHandling\ErrorLogMcpErrorHandler',
131 '\WP\MCP\Infrastructure\Observability\NullMcpObservabilityHandler',
132 $abilityNames,
133 [],
134 [],
135 [PermissionGate::class, 'transport']
136 );
137 }
138
139 /**
140 * Announce Fluent Support to FluentHub's MCP page via the toolkit filters.
141 * Runs UNCONDITIONALLY (even when MCP is off) so the operator can find
142 * the card and toggle it on; the toggle handler maps that switch onto our
143 * _mcp_settings option. Both filters are cheap no-ops unless the Toolkit
144 * applies them, so there's no cost when FluentHub is absent.
145 */
146 public static function registerWithToolkit()
147 {
148 add_filter('fluent_kit/mcp_products', function ($products) {
149 if (!is_array($products)) {
150 $products = [];
151 }
152
153 $products[] = [
154 'slug' => self::SERVER_ID,
155 'name' => __('Fluent Support', 'fluent-support'),
156 'mcp_enabled' => PermissionGate::isEnabled(),
157 'tools_count' => self::toolsCount(),
158 'endpoint_url' => self::getEndpointUrl(),
159 'status' => self::toolkitStatus(),
160 ];
161
162 return $products;
163 });
164
165 add_filter('fluent_kit/mcp_toggle_handlers', function ($handlers) {
166 if (!is_array($handlers)) {
167 $handlers = [];
168 }
169
170 $handlers[self::SERVER_ID] = [
171 'get_enabled' => [PermissionGate::class, 'isEnabled'],
172 'set_enabled' => function ($enabled) {
173 return PermissionGate::setEnabled($enabled);
174 },
175 ];
176
177 return $handlers;
178 });
179 }
180
181 /** Count of abilities the server exposes, including any pushed by Pro. */
182 public static function toolsCount()
183 {
184 $names = array_keys(AbilitiesRegistrar::getDefinitions());
185 $names = apply_filters('fluent_support/mcp_ability_names', $names);
186
187 return is_array($names) ? count(array_unique($names)) : 0;
188 }
189
190 /** Status key the Toolkit renders on the Fluent Support card. */
191 public static function toolkitStatus()
192 {
193 if (!self::adapterAvailable()) {
194 return 'adapter_required';
195 }
196
197 return PermissionGate::isEnabled() ? 'ready' : 'disabled';
198 }
199
200 /** Stable endpoint URL shown in connection snippets. */
201 public static function getEndpointUrl()
202 {
203 $namespace = sanitize_key(apply_filters('fluent_support/mcp_server_namespace', self::SERVER_ID)) ?: self::SERVER_ID;
204 $route = sanitize_key(apply_filters('fluent_support/mcp_server_route', 'mcp')) ?: 'mcp';
205
206 return get_rest_url(null, trailingslashit($namespace) . $route);
207 }
208
209 /** True when a WP MCP adapter + the Abilities API are both present. */
210 public static function adapterAvailable()
211 {
212 return defined('WP_MCP_VERSION')
213 && class_exists('\WP\MCP\Core\McpAdapter')
214 && function_exists('wp_register_ability');
215 }
216
217 public function maybeShowAdapterNotice()
218 {
219 if (self::adapterAvailable() || !current_user_can('manage_options')) {
220 return;
221 }
222
223 echo '<div class="notice notice-warning"><p>';
224 echo esc_html__(
225 'Fluent Support MCP is enabled but no MCP adapter was found. Install FluentHub (recommended) or the MCP Adapter plugin on WordPress 6.9+.',
226 'fluent-support'
227 );
228 echo '</p></div>';
229 }
230 }
231