PluginProbe
FluentBoards – Project Management, Task Management, Goal Tracking, Kanban Board, and, Team Collaboration / 2.2.0
FluentBoards – Project Management, Task Management, Goal Tracking, Kanban Board, and, Team Collaboration v2.2.0
2.2.0 2.1.0 2.0.15 2.0.12 2.0.10 2.0.4 2.0.1 2.0.0 1.95.3 1.95.2 1.95 1.91.6 trunk 1.11 1.12 1.13 1.20 1.21 1.22 1.23 1.30 1.31 1.32 1.35 1.40 All 43 releases
← All changes | app/Http/Controllers/MCPSettingsController.php +27 -140 1.95.3 → 2.2.0 View file →
@@ -2,8 +2,9 @@
2 2
3 3 namespace FluentBoards\App\Http\Controllers;
4 4
5 5 use FluentBoards\App\Modules\MCP\MCPInit;
6 +use FluentBoards\App\Services\MCPAccessService;
6 7 use FluentBoards\Framework\Http\Request\Request;
7 8
8 9 /**
9 10 * Settings endpoints for connecting Fluent Boards MCP tools to AI agents.
@@ -45,14 +46,14 @@
45 46 'abilities_api_loaded' => $abilitiesAvailable,
46 47 'endpoint_url' => MCPInit::getEndpointUrl(),
47 48 'tools_count' => $abilitiesAvailable ? MCPInit::getToolsCount() : 0,
48 49 'mcp_enabled' => fluent_boards_get_option('mcp_enabled', 'yes') === 'yes',
49 - 'app_passwords_url' => admin_url('profile.php#application-passwords-section'),
50 + 'mcp_member_access' => MCPAccessService::isMemberAccessEnabled(),
50 51 'plugins_url' => admin_url('plugins.php'),
51 52 'can_auto_install_adapter' => $canAutoInstall,
52 53 'toolkit_download_url' => 'https://github.com/WPManageNinja/fluent-toolkit',
53 54 'current_user_login' => $currentUser ? $currentUser->user_login : '',
54 - 'is_local_dev' => self::detectLocalDevEnvironment(),
55 + 'is_local_dev' => MCPAccessService::isLocalDev(),
55 56 ]);
56 57 }
57 58
58 59 public function toggle(Request $request)
@@ -70,8 +71,27 @@
70 71 : __('MCP tools disabled. The adapter will no longer report Fluent Boards abilities.', 'fluent-boards'),
71 72 ]);
72 73 }
73 74
75 + /**
76 + * All-or-none switch: when on, every board member can use MCP and create keys from Account.
77 + * Turning it off blocks members immediately; their keys are kept but stop working.
78 + */
79 + public function toggleMemberAccess(Request $request)
80 + {
81 + $value = $request->get('mcp_member_access');
82 + $enabled = is_string($value) ? in_array($value, ['yes', 'true', '1'], true) : (bool) $value;
83 +
84 + fluent_boards_update_option('mcp_member_access', $enabled ? 'yes' : 'no');
85 +
86 + return $this->sendSuccess([
87 + 'mcp_member_access' => $enabled,
88 + 'message' => $enabled
89 + ? __('Board members can now create their own MCP connections.', 'fluent-boards')
90 + : __('MCP is now limited to administrators. Member connections are blocked.', 'fluent-boards'),
91 + ]);
92 + }
93 +
74 94 public function installAdapter()
75 95 {
76 96 if (!current_user_can('install_plugins')) {
77 97 return $this->sendError([
@@ -81,9 +101,9 @@
81 101
82 102 $canAutoInstall = $this->canAutoInstallFluentKit();
83 103 if (!$canAutoInstall) {
84 104 return $this->sendError([
85 - 'message' => __('Please install FluentKit from GitHub, then reload this page to connect Fluent Boards with AI agents.', 'fluent-boards'),
105 + 'message' => __('Please install FluentHub from GitHub, then reload this page to connect Fluent Boards with AI agents.', 'fluent-boards'),
86 106 'toolkit_download_url' => 'https://github.com/WPManageNinja/fluent-toolkit',
87 107 ]);
88 108 }
89 109
@@ -102,15 +122,15 @@
102 122 $isInstalled = $this->isAdapterPresent() || $toolkitInstalled;
103 123 $isActive = (is_plugin_active(self::ADAPTER_PLUGIN_FILE) && $adapterRuntimeAvailable) || $toolkitAdapterAvailable;
104 124
105 125 if ($isInstalled && $isActive) {
106 - $message = __('FluentKit installed and activated. Reload the page to register Fluent Boards MCP tools.', 'fluent-boards');
126 + $message = __('FluentHub installed and activated. Reload the page to register Fluent Boards MCP tools.', 'fluent-boards');
107 127 } elseif ($toolkitInstalled && $toolkitActive) {
108 - $message = __('FluentKit is installed and active, but this version does not include the bundled MCP adapter yet. Please update FluentKit when the MCP-ready build is available, then reload this page.', 'fluent-boards');
128 + $message = __('FluentHub is installed and active, but this version does not include the bundled MCP adapter yet. Please update FluentHub when the MCP-ready build is available, then reload this page.', 'fluent-boards');
109 129 } elseif ($toolkitInstalled) {
110 - $message = __('FluentKit is installed but could not be activated automatically. Please activate FluentKit from the Plugins page, then reload this page.', 'fluent-boards');
130 + $message = __('FluentHub is installed but could not be activated automatically. Please activate FluentHub from the Plugins page, then reload this page.', 'fluent-boards');
111 131 } else {
112 - $message = __('Could not install FluentKit automatically. Please install FluentKit manually, then reload this page.', 'fluent-boards');
132 + $message = __('Could not install FluentHub automatically. Please install FluentHub manually, then reload this page.', 'fluent-boards');
113 133 }
114 134
115 135 return $this->sendSuccess([
116 136 'is_installed' => $isInstalled,
@@ -118,141 +138,8 @@
118 138 'toolkit_active' => $toolkitActive,
119 139 'toolkit_adapter_available' => $toolkitAdapterAvailable,
120 140 'message' => $message,
121 141 ]);
122 - }
123 -
124 - public function getConfigSnippet(Request $request)
125 - {
126 - $client = sanitize_key((string) $request->get('client', 'claude-code'));
127 - $endpoint = MCPInit::getEndpointUrl();
128 - $isLocalDev = $this->resolveLocalDevOverride($request);
129 -
130 - $basicPlaceholder = '<base64(your-username:application-password)>';
131 - $usernamePlaceholder = '<your-username>';
132 - $passwordPlaceholder = '<your-application-password>';
133 - $appPasswordsUrl = admin_url('profile.php#application-passwords-section');
134 -
135 - switch ($client) {
136 - case 'codex':
137 - $snippet = sprintf(
138 - "Settings > Connect to a custom MCP\n\nName: fluent-boards\nTransport: Streamable HTTP\n\nURL: %s\n\nHeader:\n Key: Authorization\n Value: Basic %s\n\nClick Save.",
139 - $endpoint,
140 - $basicPlaceholder
141 - );
142 - $instructions = sprintf(
143 - __('Open OpenAI Codex settings, connect to a custom MCP, choose Streamable HTTP, then use a WordPress Application Password from %s.', 'fluent-boards'),
144 - $appPasswordsUrl
145 - );
146 - break;
147 -
148 - case 'cursor':
149 - $snippet = wp_json_encode([
150 - 'mcpServers' => [
151 - 'fluent-boards' => [
152 - 'url' => $endpoint,
153 - 'type' => 'http',
154 - 'headers' => [
155 - 'Authorization' => 'Basic ' . $basicPlaceholder,
156 - ],
157 - ],
158 - ],
159 - ], JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES);
160 - $instructions = __('Paste this into Cursor Settings > MCP, then restart Cursor.', 'fluent-boards');
161 - break;
162 -
163 - case 'generic':
164 - $snippet = sprintf(
165 - "URL: %s\nAuth: Authorization: Basic %s\n\n# Quick test\ncurl -s -u '%s:%s' \\\n -X POST %s \\\n -H 'Content-Type: application/json' \\\n -d '{\"jsonrpc\":\"2.0\",\"id\":1,\"method\":\"tools/list\"}'",
166 - $endpoint,
167 - $basicPlaceholder,
168 - $usernamePlaceholder,
169 - $passwordPlaceholder,
170 - $endpoint
171 - );
172 - $instructions = __('Use the URL and Basic Auth header with any HTTP MCP client. The endpoint speaks MCP over JSON-RPC.', 'fluent-boards');
173 - break;
174 -
175 - case 'claude-desktop':
176 - $env = [
177 - 'WP_API_URL' => $endpoint,
178 - 'WP_API_USERNAME' => $usernamePlaceholder,
179 - 'WP_API_PASSWORD' => $passwordPlaceholder,
180 - 'OAUTH_ENABLED' => 'false',
181 - 'NODE_TLS_REJECT_UNAUTHORIZED' => '0',
182 - ];
183 -
184 - $snippet = wp_json_encode([
185 - 'mcpServers' => [
186 - 'fluent-boards' => [
187 - 'command' => 'npx',
188 - 'args' => ['-y', '@automattic/mcp-wordpress-remote@latest'],
189 - 'env' => $env,
190 - ],
191 - ],
192 - ], JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES);
193 -
194 - $instructions = __('Paste into the Claude Desktop config file, then restart Claude Desktop.', 'fluent-boards');
195 - break;
196 -
197 - case 'claude-code':
198 - default:
199 - $snippet = sprintf(
200 - "claude mcp add \\\n --transport http \\\n fluent-boards %s \\\n --header \"Authorization: Basic %s\"",
201 - $endpoint,
202 - $basicPlaceholder
203 - );
204 - $instructions = __('Fill in your username and application password above, then paste the command into your terminal.', 'fluent-boards');
205 - $client = 'claude-code';
206 - break;
207 - }
208 -
209 - return $this->sendSuccess([
210 - 'client' => $client,
211 - 'snippet' => $snippet,
212 - 'instructions' => $instructions,
213 - 'endpoint' => $endpoint,
214 - 'app_passwords_url' => $appPasswordsUrl,
215 - 'is_local_dev' => $isLocalDev,
216 - ]);
217 - }
218 -
219 - private function resolveLocalDevOverride(Request $request)
220 - {
221 - $forceLocalDev = $request->get('local_dev');
222 - if ($forceLocalDev === 'yes' || $forceLocalDev === '1' || $forceLocalDev === 'true') {
223 - return true;
224 - }
225 -
226 - if ($forceLocalDev === 'no' || $forceLocalDev === '0' || $forceLocalDev === 'false') {
227 - return false;
228 - }
229 -
230 - return self::detectLocalDevEnvironment();
231 - }
232 -
233 - private static function detectLocalDevEnvironment()
234 - {
235 - $host = wp_parse_url(home_url(), PHP_URL_HOST);
236 - $host = strtolower((string) $host);
237 -
238 - $isDev = false;
239 - foreach (['.test', '.lab', '.local', '.localhost', '.docker', '.dev'] as $tld) {
240 - if (substr($host, -strlen($tld)) === $tld) {
241 - $isDev = true;
242 - break;
243 - }
244 - }
245 -
246 - if (!$isDev && ($host === 'localhost' || $host === '127.0.0.1' || $host === '::1')) {
247 - $isDev = true;
248 - }
249 -
250 - if (!$isDev && filter_var($host, FILTER_VALIDATE_IP)) {
251 - $isDev = !filter_var($host, FILTER_VALIDATE_IP, FILTER_FLAG_NO_PRIV_RANGE | FILTER_FLAG_NO_RES_RANGE);
252 - }
253 -
254 - return (bool) apply_filters('fluent_boards/mcp_is_local_dev', $isDev, $host);
255 142 }
256 143
257 144 private function isAdapterPresent()
258 145 {