PluginProbe
Kit (formerly ConvertKit) – Email Newsletter, Email Marketing, Membership, Subscribers and Landing Pages / 3.4.3
Kit (formerly ConvertKit) – Email Newsletter, Email Marketing, Membership, Subscribers and Landing Pages v3.4.3
3.4.3 3.4.2 3.4.1 3.4.0 3.3.9 3.3.8 3.3.7 3.3.6 3.3.5 3.3.4 3.3.3 3.3.2 3.3.1 2.2.0 2.2.1 2.2.2 2.2.3 2.2.4 2.2.5 2.2.6 2.2.7 2.2.8 2.2.9 2.3.0 2.3.1 All 196 releases
convertkit / admin / section / class-convertkit-admin-section-mcp.php

class-convertkit-admin-section-mcp.php in Kit (formerly ConvertKit) – Email Newsletter, Email Marketing, Membership, Subscribers and Landing Pages 3.4.3, at admin/section/class-convertkit-admin-section-mcp.php

855 lines 25.7 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * ConvertKit Settings MCP Settings class.
4 *
5 * @package ConvertKit
6 * @author ConvertKit
7 */
8
9 /**
10 * Registers MCP Settings that can be edited at Settings > Kit > MCP.
11 *
12 * @package ConvertKit
13 * @author ConvertKit
14 */
15 class ConvertKit_Admin_Section_MCP extends ConvertKit_Admin_Section_Base {
16
17 /**
18 * The authorization header to display on screen.
19 *
20 * @since 3.4.0
21 *
22 * @var bool|string
23 */
24 private $authorization_header = false;
25
26 /**
27 * Whether the Kit account is on a paid plan, once queried.
28 *
29 * @since 3.4.1
30 *
31 * @var bool|null
32 */
33 private $is_paid_plan = null;
34
35 /**
36 * Constructor.
37 *
38 * @since 3.4.0
39 */
40 public function __construct() {
41
42 // Define the class that reads/writes settings.
43 $this->settings = new ConvertKit_Settings_MCP();
44
45 // Define the settings key.
46 $this->settings_key = $this->settings::SETTINGS_NAME;
47
48 // Define the programmatic name, Title and Tab Text.
49 $this->name = 'mcp';
50 $this->title = __( 'MCP', 'convertkit' );
51 $this->tab_text = __( 'MCP', 'convertkit' );
52
53 // Identify that this is beta functionality.
54 $this->is_beta = true;
55
56 // Define settings sections.
57 $this->settings_sections = array(
58 'general' => array(
59 'title' => $this->title,
60 'callback' => array( $this, 'print_section_info' ),
61 'wrap' => true,
62 ),
63 'connect' => array(
64 'title' => __( 'Connect an AI client', 'convertkit' ),
65 'callback' => array( $this, 'print_section_info_connect' ),
66 'wrap' => true,
67 ),
68 );
69
70 $this->maybe_generate_authentication_header();
71 $this->maybe_revoke_application_password();
72
73 // Register and maybe output notices for this settings screen, and the Intercom messenger.
74 if ( $this->on_settings_screen( $this->name ) ) {
75 add_action( 'convertkit_settings_base_render_before', array( $this, 'maybe_output_notices' ) );
76 }
77
78 // Enqueue scripts and CSS.
79 add_action( 'convertkit_admin_settings_enqueue_scripts', array( $this, 'enqueue_scripts' ) );
80
81 parent::__construct();
82
83 }
84
85 /**
86 * Generates the authentication header to display on screen, if the user
87 * has just created an Application Password.
88 *
89 * @since 3.4.0
90 */
91 private function maybe_generate_authentication_header() {
92
93 // Bail if we're not on the settings screen.
94 if ( ! $this->on_settings_screen( $this->name ) ) {
95 return;
96 }
97
98 // Bail if nonce verification fails.
99 if ( ! isset( $_REQUEST['_convertkit_settings_mcp_create_application_password'] ) ) {
100 return;
101 }
102 if ( ! wp_verify_nonce( sanitize_key( $_REQUEST['_convertkit_settings_mcp_create_application_password'] ), 'convertkit-mcp-create-application-password' ) ) {
103 return;
104 }
105
106 // Bail if the user login and password are not included in the request.
107 if ( ! isset( $_REQUEST['user_login'] ) || ! isset( $_REQUEST['password'] ) ) {
108 return;
109 }
110
111 // Build the authorization header to display on screen.
112 $user_login = sanitize_text_field( wp_unslash( $_REQUEST['user_login'] ) );
113 $password = sanitize_text_field( wp_unslash( $_REQUEST['password'] ) );
114 $this->authorization_header = base64_encode( $user_login . ':' . $password ); // phpcs:ignore WordPress.PHP.DiscouragedPHPFunctions.obfuscation_base64_encode
115
116 }
117
118 /**
119 * Revokes the Application Password, if the user clicked the Revoke Application Password button.
120 *
121 * @since 3.4.0
122 */
123 private function maybe_revoke_application_password() {
124
125 // Bail if we're not on the settings screen.
126 if ( ! $this->on_settings_screen( $this->name ) ) {
127 return;
128 }
129
130 // Bail if nonce verification fails.
131 if ( ! isset( $_REQUEST['_convertkit_settings_mcp_revoke_application_password'] ) ) {
132 return;
133 }
134 if ( ! wp_verify_nonce( sanitize_key( $_REQUEST['_convertkit_settings_mcp_revoke_application_password'] ), 'convertkit-mcp-revoke-application-password' ) ) {
135 return;
136 }
137
138 // Get the Application Password UUID.
139 $application_password_uuid = $this->get_application_password_uuid();
140
141 // Bail if no Application Password UUID exists.
142 if ( ! $application_password_uuid ) {
143 return;
144 }
145
146 // Revoke the Application Password.
147 $result = WP_Application_Passwords::delete_application_password( get_current_user_id(), $application_password_uuid );
148 if ( is_wp_error( $result ) ) {
149 $this->output_error( $result->get_error_message() );
150 return;
151 }
152
153 // Reload the settings screen.
154 wp_safe_redirect( $this->get_settings_url() );
155 exit();
156
157 }
158
159 /**
160 * Enqueues scripts for the Settings > MCP screen.
161 *
162 * @since 3.4.0
163 *
164 * @param string $section Settings section / tab (general|tools|restrict-content|broadcasts|mcp).
165 */
166 public function enqueue_scripts( $section ) {
167
168 // Bail if we're not on the MCP section.
169 if ( $section !== $this->name ) {
170 return;
171 }
172
173 // Enqueue JS.
174 wp_enqueue_script( 'convertkit-admin-settings-conditional-display', CONVERTKIT_PLUGIN_URL . 'resources/backend/js/settings-conditional-display.js', array( 'jquery' ), CONVERTKIT_PLUGIN_VERSION, true );
175 wp_enqueue_script( 'convertkit-admin-ui', CONVERTKIT_PLUGIN_URL . 'resources/backend/js/ui.js', array(), CONVERTKIT_PLUGIN_VERSION, true );
176
177 // Localize the strings displayed when copying a code block to the clipboard.
178 wp_localize_script(
179 'convertkit-admin-ui',
180 'convertkit_ui',
181 array(
182 'copy' => __( 'Copy', 'convertkit' ),
183 'copied' => __( 'Copied', 'convertkit' ),
184 'failed' => __( 'Press Ctrl/Cmd + C to copy', 'convertkit' ),
185 )
186 );
187
188 }
189
190 /**
191 * Registers settings fields for this section.
192 *
193 * @since 3.4.0
194 */
195 public function register_fields() {
196
197 // Enable.
198 add_settings_field(
199 'enabled',
200 __( 'Enable MCP Server', 'convertkit' ),
201 array( $this, 'enabled_callback' ),
202 $this->settings_key,
203 $this->name,
204 array(
205 'name' => 'enabled',
206 'label_for' => 'enabled',
207 'label' => __( 'When enabled, allows AI clients to connect to the Kit Plugin using MCP.', 'convertkit' ),
208 'description' => sprintf(
209 '%s<br /><code>%s</code>',
210 __( 'MCP server URL:', 'convertkit' ),
211 esc_url( ConvertKit_MCP::get_server_url() )
212 ),
213 )
214 );
215
216 }
217
218 /**
219 * Prints help info for this section
220 *
221 * @since 3.4.0
222 */
223 public function print_section_info() {
224
225 ?>
226 <span class="convertkit-beta-label"><?php esc_html_e( 'Beta', 'convertkit' ); ?></span>
227 <p class="description"><?php esc_html_e( 'Defines whether AI clients can connect to the Kit Plugin using MCP, and provides instructions for connecting.', 'convertkit' ); ?></p>
228 <?php
229
230 }
231
232 /**
233 * Returns the URL for the ConvertKit documentation for this setting section.
234 *
235 * @since 3.4.0
236 *
237 * @return string Documentation URL.
238 */
239 public function documentation_url() {
240
241 return 'https://help.kit.com/en/articles/16729038-using-the-kit-plugin-s-mcp-server-on-your-wordpress-website';
242
243 }
244
245 /**
246 * Renders the upgrade CTA when the connected Kit account is on
247 * the free plan.
248 *
249 * @since 3.4.0
250 */
251 public function output_upgrade_required_message() {
252
253 ?>
254 <p>
255 <?php esc_html_e( 'The Kit WordPress MCP is available on paid Kit plans. Upgrade your Kit account to connect AI clients to your WordPress site.', 'convertkit' ); ?>
256 </p>
257 <p>
258 <a href="https://app.kit.com/account_settings/billing" class="button button-primary" target="_blank">
259 <?php esc_html_e( 'Upgrade Kit Account', 'convertkit' ); ?>
260 </a>
261 </p>
262 <?php
263
264 }
265
266 /**
267 * Renders the input for the Enable setting.
268 *
269 * @since 3.4.0
270 *
271 * @param array $args Setting field arguments (name,description).
272 */
273 public function enabled_callback( $args ) {
274
275 // If the user doesn't have a paid plan, show the upgrade required message.
276 if ( ! $this->is_paid_plan() ) {
277 // Disable saving settings.
278 $this->save_disabled = true;
279
280 $this->output_upgrade_required_message();
281 return;
282 }
283
284 // Output field.
285 $this->output_checkbox_field(
286 $args['name'],
287 'on',
288 $this->settings->enabled(),
289 $args['label'],
290 $args['description'],
291 array( 'convertkit-conditional-display' )
292 );
293
294 }
295
296 /**
297 * Renders the connection instructions, comprising of the steps the user needs
298 * to complete to connect an AI client to this site's MCP server.
299 *
300 * @since 3.4.1
301 */
302 public function print_section_info_connect() {
303
304 // Don't output anything if the Kit account isn't on a paid plan, as the
305 // upgrade message is displayed in the section above.
306 if ( ! $this->is_paid_plan() ) {
307 return;
308 }
309
310 // The output is wrapped in its own container, so that the settings screen's
311 // styles for a beta section's immediate children aren't applied to it.
312 echo '<div class="convertkit-mcp">';
313
314 if ( ! $this->settings->enabled() ) {
315 // The MCP server isn't enabled; tell the user how to enable it.
316 ?>
317 <p class="description">
318 <?php esc_html_e( 'Enable the MCP server above and click Save Changes, to then connect an AI client to this site.', 'convertkit' ); ?>
319 </p>
320 <?php
321 } elseif ( $this->application_passwords_available() ) {
322 // Get the Application Password for this Plugin, if one exists.
323 $application_password = $this->get_application_password();
324 ?>
325
326 <ol class="kit-numbered-steps">
327 <li>
328 <h3><?php esc_html_e( 'Enable the MCP server', 'convertkit' ); ?></h3>
329 <p class="description">
330 <?php
331 printf(
332 /* translators: %s: MCP server URL. */
333 esc_html__( 'Done. AI clients connect to %s', 'convertkit' ),
334 '<code>' . esc_url( ConvertKit_MCP::get_server_url() ) . '</code>'
335 );
336 ?>
337 </p>
338 </li>
339
340 <li>
341 <h3><?php esc_html_e( 'Create an Application Password', 'convertkit' ); ?></h3>
342 <?php
343 if ( is_array( $application_password ) ) {
344 $this->output_application_password( $application_password );
345 } else {
346 $this->output_create_application_password();
347 }
348 ?>
349 </li>
350
351 <li>
352 <h3><?php esc_html_e( 'Connect your AI client', 'convertkit' ); ?></h3>
353 <?php
354 if ( ! is_array( $application_password ) ) {
355 ?>
356 <p class="description">
357 <?php esc_html_e( 'Create an Application Password above to display the configuration for your AI client.', 'convertkit' ); ?>
358 </p>
359 <?php
360 } else {
361 $this->output_client_instructions();
362 }
363 ?>
364 </li>
365 </ol>
366
367 <?php
368 // Output a summary of what the AI client can do once connected.
369 $this->output_capabilities_summary();
370 }
371
372 echo '</div>';
373
374 }
375
376 /**
377 * Returns whether WordPress' Application Passwords feature is available for
378 * the site and the current user, outputting an error message if it isn't.
379 *
380 * WordPress disables Application Passwords when the site isn't served over
381 * HTTPS and isn't a local environment, meaning no AI client can authenticate.
382 *
383 * @since 3.4.1
384 *
385 * @return bool Application Passwords are available.
386 */
387 private function application_passwords_available() {
388
389 // Application Passwords are disabled for the site.
390 if ( ! wp_is_application_passwords_available() ) {
391 $this->output_error(
392 sprintf(
393 /* translators: %1$s: Site URL, %2$s: WP_ENVIRONMENT_TYPE constant. */
394 __( 'Application Passwords are disabled on this site, so AI clients cannot authenticate. WordPress disables Application Passwords when a site is not served over HTTPS. Serve %1$s over HTTPS, or set %2$s to local on a development site, and then reload this screen.', 'convertkit' ),
395 home_url(),
396 'WP_ENVIRONMENT_TYPE'
397 )
398 );
399 return false;
400 }
401
402 // Application Passwords are disabled for this user.
403 if ( ! wp_is_application_passwords_available_for_user( get_current_user_id() ) ) {
404 $this->output_error( __( 'Application Passwords are disabled for your WordPress user, so you cannot create the password an AI client needs. Ask an administrator to enable Application Passwords for your user.', 'convertkit' ) );
405 return false;
406 }
407
408 return true;
409
410 }
411
412 /**
413 * Renders the Create Application Password button, which sends the user to
414 * WordPress' authorize-application.php screen.
415 *
416 * @since 3.4.1
417 */
418 private function output_create_application_password() {
419
420 // Build the WordPress authorize-application.php URL.
421 // See: https://developer.wordpress.org/advanced-administration/security/application-passwords/.
422 // We don't use add_query_arg(), as rawurlencode() is needed for authorize-application.php's JS to work correctly.
423 $authorize_url = admin_url( 'authorize-application.php' )
424 . '?app_name=' . rawurlencode( CONVERTKIT_MCP_APP_NAME )
425 . '&success_url=' . rawurlencode(
426 $this->get_settings_url(
427 array(
428 '_convertkit_settings_mcp_create_application_password' => wp_create_nonce( 'convertkit-mcp-create-application-password' ),
429 )
430 )
431 )
432 . '&reject_url=' . rawurlencode( $this->get_settings_url() );
433 ?>
434 <p class="description">
435 <?php
436 printf(
437 /* translators: %s: WordPress user's display name. */
438 esc_html__( 'An AI client signs in to this site using an Application Password. The client will act as %s, and can only do what that user can do.', 'convertkit' ),
439 '<strong>' . esc_html( wp_get_current_user()->display_name ) . '</strong>'
440 );
441 ?>
442 </p>
443 <p>
444 <a href="<?php echo esc_attr( $authorize_url ); ?>" id="convertkit-settings-mcp-create-application-password" class="button button-primary">
445 <?php esc_html_e( 'Create Application Password', 'convertkit' ); ?>
446 </a>
447 </p>
448 <?php
449
450 }
451
452 /**
453 * Renders the Application Password's details, the authorization header (if the
454 * password was just created), and the Revoke Application Password button.
455 *
456 * @since 3.4.1
457 *
458 * @param array $application_password Application Password.
459 */
460 private function output_application_password( $application_password ) {
461
462 // Build disconnect URL.
463 $disconnect_url = $this->get_settings_url( array( '_convertkit_settings_mcp_revoke_application_password' => wp_create_nonce( 'convertkit-mcp-revoke-application-password' ) ) );
464
465 // Define the date and time format used for the Application Password's dates.
466 $date_format = get_option( 'date_format' ) . ' ' . get_option( 'time_format' );
467 ?>
468 <p class="description">
469 <?php
470 printf(
471 /* translators: %1$s: WordPress user's display name, %2$s: Date and time the Application Password was created. */
472 esc_html__( 'AI clients using this Application Password act as %1$s, and can only do what that user can do. Created %2$s.', 'convertkit' ),
473 '<strong>' . esc_html( wp_get_current_user()->display_name ) . '</strong>',
474 esc_html( (string) wp_date( $date_format, $application_password['created'] ) )
475 );
476
477 if ( ! empty( $application_password['last_used'] ) ) {
478 echo ' ';
479 printf(
480 /* translators: %s: Date and time the Application Password was last used. */
481 esc_html__( 'Last used %s.', 'convertkit' ),
482 esc_html( (string) wp_date( $date_format, $application_password['last_used'] ) )
483 );
484 } else {
485 echo ' ';
486 esc_html_e( 'Not yet used by an AI client.', 'convertkit' );
487 }
488 ?>
489 </p>
490
491 <?php
492 if ( $this->authorization_header ) {
493 ?>
494 <p>
495 <strong><?php esc_html_e( 'Authorization header:', 'convertkit' ); ?></strong>
496 </p>
497 <?php
498 $this->output_code_block( 'Basic ' . $this->authorization_header, 'kit-authorization-header' );
499 ?>
500 <p class="description">
501 <?php esc_html_e( 'Copy the above now. It won\'t be displayed again. If you lose it, revoke the Application Password and create a new one.', 'convertkit' ); ?>
502 </p>
503 <?php
504 } else {
505 ?>
506 <p class="description">
507 <?php
508 printf(
509 /* translators: %s: Placeholder text displayed in the configuration snippets in place of the authorization header. */
510 esc_html__( 'For security, WordPress only displays an Application Password once, at the point it is created. The configuration below therefore shows %s in place of your Application Password. If you no longer have it, revoke the Application Password and create a new one.', 'convertkit' ),
511 '<code>BASE64_ENCODED_USERNAME_AND_APPLICATION_PASSWORD</code>'
512 );
513 ?>
514 </p>
515 <?php
516 }
517 ?>
518
519 <p>
520 <a href="<?php echo esc_url( $disconnect_url ); ?>" id="convertkit-settings-mcp-revoke-application-password" class="button button-secondary"><?php esc_html_e( 'Revoke Application Password', 'convertkit' ); ?></a>
521 </p>
522 <?php
523
524 }
525
526 /**
527 * Renders the configuration for each supported AI client, in a tabbed interface.
528 *
529 * @since 3.4.1
530 */
531 private function output_client_instructions() {
532
533 // Build the server URL and authorization header used in each client's configuration.
534 // When the Application Password isn't available to display, a placeholder is used, so
535 // that the configuration is still valid and shows where the header value belongs.
536 $server_url = ConvertKit_MCP::get_server_url();
537 $auth_header = 'Basic ' . ( $this->authorization_header ? $this->authorization_header : 'BASE64_ENCODED_USERNAME_AND_APPLICATION_PASSWORD' );
538
539 // Claude Desktop JSON.
540 // Claude Desktop only supports remote MCP servers that authenticate using OAuth, so
541 // mcp-remote is used to proxy requests to the MCP server, adding the authorization header.
542 $claude_desktop_config = wp_json_encode(
543 array(
544 'mcpServers' => array(
545 'kit-wordpress' => array(
546 'command' => 'npx',
547 'args' => array(
548 '-y',
549 'mcp-remote',
550 $server_url,
551 '--header',
552 'Authorization: ' . $auth_header,
553 ),
554 ),
555 ),
556 ),
557 JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES
558 );
559
560 // Claude Code command.
561 $claude_code_command = sprintf(
562 'claude mcp add --transport http kit-wordpress %s --header "Authorization: %s"',
563 $server_url,
564 $auth_header
565 );
566
567 // Cursor JSON.
568 $cursor_config = wp_json_encode(
569 array(
570 'mcpServers' => array(
571 'kit-wordpress' => array(
572 'url' => $server_url,
573 'headers' => array(
574 'Authorization' => $auth_header,
575 ),
576 ),
577 ),
578 ),
579 JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES
580 );
581
582 // Codex TOML.
583 $codex_config = '[mcp_servers.kit_wordpress]' . "\n"
584 . 'url = "' . $server_url . '"' . "\n"
585 . 'http_headers = { "Authorization" = "' . $auth_header . '" }';
586
587 // Define the clients to display, in the order they should be displayed.
588 $clients = array(
589 'claude-desktop' => __( 'Claude Desktop', 'convertkit' ),
590 'claude-code' => __( 'Claude Code', 'convertkit' ),
591 'cursor' => __( 'Cursor', 'convertkit' ),
592 'codex' => __( 'Codex', 'convertkit' ),
593 'other' => __( 'Other clients', 'convertkit' ),
594 );
595 ?>
596 <div class="kit-inline-tabs">
597 <ul class="kit-inline-tabs-nav">
598 <?php
599 $first_client = true;
600 foreach ( $clients as $client => $label ) {
601 ?>
602 <li>
603 <button type="button" class="kit-inline-tab<?php echo ( $first_client ? ' is-active' : '' ); ?>" data-tab="<?php echo esc_attr( $client ); ?>">
604 <?php echo esc_html( $label ); ?>
605 </button>
606 </li>
607 <?php
608 $first_client = false;
609 }
610 ?>
611 </ul>
612
613 <div class="kit-inline-tab-panel is-active" data-tab="claude-desktop">
614 <p>
615 <?php
616 printf(
617 /* translators: %s: Claude Desktop configuration file name. */
618 esc_html__( 'Add the following to your %s file, then restart Claude Desktop:', 'convertkit' ),
619 '<code>claude_desktop_config.json</code>'
620 );
621 ?>
622 <br />
623 macOS: <code>~/Library/Application Support/Claude/claude_desktop_config.json</code>
624 <br />
625 Windows: <code>%APPDATA%\Claude\claude_desktop_config.json</code>
626 </p>
627 <?php $this->output_code_block( (string) $claude_desktop_config ); ?>
628 <p class="description">
629 <?php
630 printf(
631 /* translators: %1$s: mcp-remote, %2$s: Node.js. */
632 esc_html__( 'Claude Desktop only connects to remote MCP servers that use OAuth, so %1$s is used to connect to this site. This requires %2$s to be installed on your computer.', 'convertkit' ),
633 '<code>mcp-remote</code>',
634 '<a href="https://nodejs.org/" target="_blank">Node.js</a>'
635 );
636 ?>
637 </p>
638 </div>
639
640 <div class="kit-inline-tab-panel" data-tab="claude-code">
641 <p>
642 <?php esc_html_e( 'Run the following command in your terminal:', 'convertkit' ); ?>
643 </p>
644 <?php $this->output_code_block( $claude_code_command ); ?>
645 </div>
646
647 <div class="kit-inline-tab-panel" data-tab="cursor">
648 <p>
649 <?php
650 printf(
651 /* translators: %s: Cursor configuration file name. */
652 esc_html__( 'Add the following to your %s file, then restart Cursor:', 'convertkit' ),
653 '<code>~/.cursor/mcp.json</code>'
654 );
655 ?>
656 </p>
657 <?php $this->output_code_block( (string) $cursor_config ); ?>
658 </div>
659
660 <div class="kit-inline-tab-panel" data-tab="codex">
661 <p>
662 <?php
663 printf(
664 /* translators: %s: Codex configuration file name. */
665 esc_html__( 'Add the following to your %s file, then restart Codex:', 'convertkit' ),
666 '<code>~/.codex/config.toml</code>'
667 );
668 ?>
669 </p>
670 <?php $this->output_code_block( $codex_config ); ?>
671 <p class="description">
672 <?php
673 printf(
674 /* translators: %s: Codex configuration option. */
675 esc_html__( 'Older versions of Codex require %s at the top of the configuration file to connect to remote MCP servers.', 'convertkit' ),
676 '<code>experimental_use_rmcp_client = true</code>'
677 );
678 ?>
679 </p>
680 </div>
681
682 <div class="kit-inline-tab-panel" data-tab="other">
683 <p>
684 <?php esc_html_e( 'For any other MCP client, use the following. The server uses the streamable HTTP transport, and authenticates using HTTP Basic authentication.', 'convertkit' ); ?>
685 </p>
686 <p>
687 <strong><?php esc_html_e( 'Server URL:', 'convertkit' ); ?></strong>
688 </p>
689 <?php $this->output_code_block( $server_url ); ?>
690 <p>
691 <strong><?php esc_html_e( 'Authorization header:', 'convertkit' ); ?></strong>
692 </p>
693 <?php $this->output_code_block( $auth_header ); ?>
694 </div>
695 </div>
696 <?php
697
698 }
699
700 /**
701 * Renders a summary of what an AI client can do once connected.
702 *
703 * @since 3.4.1
704 */
705 private function output_capabilities_summary() {
706
707 ?>
708 <h3><?php esc_html_e( 'What your AI client can do', 'convertkit' ); ?></h3>
709 <p class="description">
710 <?php esc_html_e( 'Describe what you want in your own words; your AI client works out which tools to use.', 'convertkit' ); ?>
711 </p>
712 <ul class="convertkit-mcp-capabilities">
713 <li><?php esc_html_e( 'Look up the Forms, Landing Pages, Products and Tags in your Kit account.', 'convertkit' ); ?></li>
714 <li><?php esc_html_e( 'Add, list, change and remove Kit Forms, Form Triggers, Products and Broadcasts within a post or page\'s content.', 'convertkit' ); ?></li>
715 <li><?php esc_html_e( 'Read and change a post or page\'s Kit settings, such as its Form, Landing Page, Tag and Member Content.', 'convertkit' ); ?></li>
716 <li><?php esc_html_e( 'Read and change a category\'s Kit Form and Form Position.', 'convertkit' ); ?></li>
717 <li><?php esc_html_e( 'Read and change this Plugin\'s General, Broadcasts and Member Content settings.', 'convertkit' ); ?></li>
718 </ul>
719 <p class="description">
720 <?php esc_html_e( 'Your Kit account credentials, and your Form Entries, are never exposed to AI clients.', 'convertkit' ); ?>
721 </p>
722 <?php
723
724 // Output a link to the documentation, if it's defined.
725 if ( $this->documentation_url() === '#' ) {
726 return;
727 }
728 ?>
729 <p>
730 <a href="<?php echo esc_url( $this->documentation_url() ); ?>" target="_blank">
731 <?php esc_html_e( 'Read the MCP documentation', 'convertkit' ); ?>
732 </a>
733 </p>
734 <?php
735
736 }
737
738 /**
739 * Returns the URL for the this settings screen.
740 *
741 * @since 3.4.0
742 *
743 * @param array $query_args Query arguments to add to the URL.
744 * @return string
745 */
746 private function get_settings_url( $query_args = array() ) {
747
748 return add_query_arg(
749 array_merge(
750 array(
751 'page' => '_wp_convertkit_settings',
752 'tab' => $this->name,
753 ),
754 $query_args
755 ),
756 admin_url( 'options-general.php' )
757 );
758
759 }
760
761 /**
762 * Returns whether the Kit account is on a paid plan.
763 *
764 * @since 3.4.1
765 *
766 * @return bool
767 */
768 private function is_paid_plan() {
769
770 // If the result has already been fetched for this request, return it.
771 if ( ! is_null( $this->is_paid_plan ) ) {
772 return $this->is_paid_plan;
773 }
774
775 // Fetch the account resource and return the result.
776 $account = new ConvertKit_Resource_Account();
777 $this->is_paid_plan = $account->is_paid_plan();
778 return $this->is_paid_plan;
779
780 }
781
782 /**
783 * Finds the most recently-created Application Password for this Plugin, belonging
784 * to the currently logged in user.
785 *
786 * @since 3.4.1
787 *
788 * @return bool|array
789 */
790 private function get_application_password() {
791
792 // Get the user's Application Passwords.
793 $passwords = WP_Application_Passwords::get_user_application_passwords( get_current_user_id() );
794
795 // Return false if no Application Passwords exist.
796 if ( empty( $passwords ) ) {
797 return false;
798 }
799
800 // Iterate through the Application Passwords and return the password that matches the app name.
801 foreach ( $passwords as $password ) {
802 if ( $password['name'] === CONVERTKIT_MCP_APP_NAME ) {
803 return $password;
804 }
805 }
806
807 return false;
808
809 }
810
811 /**
812 * Finds the UUID of the most recently-created Application Password for the
813 * currently logged in user
814 *
815 * @since 3.4.0
816 *
817 * @return bool|string
818 */
819 private function get_application_password_uuid() {
820
821 // Get the Application Password for this Plugin.
822 $password = $this->get_application_password();
823
824 // Return false if no Application Password exists.
825 if ( ! is_array( $password ) ) {
826 return false;
827 }
828
829 return $password['uuid'];
830
831 }
832
833 }
834
835 // Bootstrap.
836 add_filter(
837 'convertkit_admin_settings_register_sections',
838 function ( $sections ) {
839
840 // Don't register the MCP section if the Abilities API is not available (WordPress < 6.9).
841 if ( ! function_exists( 'wp_register_ability' ) ) {
842 return $sections;
843 }
844
845 // Don't register the MCP section if PHP 7.4+ is not installed.
846 if ( version_compare( PHP_VERSION, '7.4', '<' ) ) {
847 return $sections;
848 }
849
850 $sections['mcp'] = new ConvertKit_Admin_Section_MCP();
851 return $sections;
852
853 }
854 );
855