← All changes
|
admin/section/class-convertkit-admin-section-mcp.php
+420
-113
3.4.0
→
3.4.3
View file →
| @@ -23,8 +23,17 @@ | ||
| 23 | 23 | */ |
| 24 | 24 | private $authorization_header = false; |
| 25 | 25 | |
| 26 | 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 | + /** | |
| 27 | 36 | * Constructor. |
| 28 | 37 | * |
| 29 | 38 | * @since 3.4.0 |
| 30 | 39 | */ |
| @@ -50,8 +59,13 @@ | ||
| 50 | 59 | 'title' => $this->title, |
| 51 | 60 | 'callback' => array( $this, 'print_section_info' ), |
| 52 | 61 | 'wrap' => true, |
| 53 | 62 | ), |
| 63 | + 'connect' => array( | |
| 64 | + 'title' => __( 'Connect an AI client', 'convertkit' ), | |
| 65 | + 'callback' => array( $this, 'print_section_info_connect' ), | |
| 66 | + 'wrap' => true, | |
| 67 | + ), | |
| 54 | 68 | ); |
| 55 | 69 | |
| 56 | 70 | $this->maybe_generate_authentication_header(); |
| 57 | 71 | $this->maybe_revoke_application_password(); |
| @@ -157,9 +171,21 @@ | ||
| 157 | 171 | } |
| 158 | 172 | |
| 159 | 173 | // Enqueue JS. |
| 160 | 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 ); | |
| 161 | 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 | + | |
| 162 | 188 | } |
| 163 | 189 | |
| 164 | 190 | /** |
| 165 | 191 | * Registers settings fields for this section. |
| @@ -186,32 +212,8 @@ | ||
| 186 | 212 | ), |
| 187 | 213 | ) |
| 188 | 214 | ); |
| 189 | 215 | |
| 190 | - // Bail if MCP is not enabled — none of the connect UI applies. | |
| 191 | - if ( ! $this->settings->enabled() ) { | |
| 192 | - return; | |
| 193 | - } | |
| 194 | - | |
| 195 | - // If an Application Password exists for this Plugin, display the instructions and revoke section. | |
| 196 | - if ( $this->get_application_password_uuid() ) { | |
| 197 | - add_settings_field( | |
| 198 | - 'connect', | |
| 199 | - __( 'Connection', 'convertkit' ), | |
| 200 | - array( $this, 'instructions_disconnect_callback' ), | |
| 201 | - $this->settings_key, | |
| 202 | - $this->name | |
| 203 | - ); | |
| 204 | - } else { | |
| 205 | - add_settings_field( | |
| 206 | - 'connect', | |
| 207 | - __( 'Connection', 'convertkit' ), | |
| 208 | - array( $this, 'connect_callback' ), | |
| 209 | - $this->settings_key, | |
| 210 | - $this->name | |
| 211 | - ); | |
| 212 | - } | |
| 213 | - | |
| 214 | 216 | } |
| 215 | 217 | |
| 216 | 218 | /** |
| 217 | 219 | * Prints help info for this section |
| @@ -226,9 +228,8 @@ | ||
| 226 | 228 | <?php |
| 227 | 229 | |
| 228 | 230 | } |
| 229 | 231 | |
| 230 | - | |
| 231 | 232 | /** |
| 232 | 233 | * Returns the URL for the ConvertKit documentation for this setting section. |
| 233 | 234 | * |
| 234 | 235 | * @since 3.4.0 |
| @@ -236,9 +237,9 @@ | ||
| 236 | 237 | * @return string Documentation URL. |
| 237 | 238 | */ |
| 238 | 239 | public function documentation_url() { |
| 239 | 240 | |
| 240 | - return '#'; | |
| 241 | + return 'https://help.kit.com/en/articles/16729038-using-the-kit-plugin-s-mcp-server-on-your-wordpress-website'; | |
| 241 | 242 | |
| 242 | 243 | } |
| 243 | 244 | |
| 244 | 245 | /** |
| @@ -271,10 +272,9 @@ | ||
| 271 | 272 | */ |
| 272 | 273 | public function enabled_callback( $args ) { |
| 273 | 274 | |
| 274 | 275 | // If the user doesn't have a paid plan, show the upgrade required message. |
| 275 | - $account = new ConvertKit_Resource_Account(); | |
| 276 | - if ( ! $account->is_paid_plan() ) { | |
| 276 | + if ( ! $this->is_paid_plan() ) { | |
| 277 | 277 | // Disable saving settings. |
| 278 | 278 | $this->save_disabled = true; |
| 279 | 279 | |
| 280 | 280 | $this->output_upgrade_required_message(); |
| @@ -293,15 +293,131 @@ | ||
| 293 | 293 | |
| 294 | 294 | } |
| 295 | 295 | |
| 296 | 296 | /** |
| 297 | - * Renders the Connect a client setting, to allow the user to generate an Application Password | |
| 298 | - * for this Plugin which is used to connect AI clients to the MCP Server. | |
| 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 | 299 | * |
| 300 | - * @since 3.4.0 | |
| 300 | + * @since 3.4.1 | |
| 301 | 301 | */ |
| 302 | - public function connect_callback() { | |
| 302 | + public function print_section_info_connect() { | |
| 303 | 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 | + | |
| 304 | 420 | // Build the WordPress authorize-application.php URL. |
| 305 | 421 | // See: https://developer.wordpress.org/advanced-administration/security/application-passwords/. |
| 306 | 422 | // We don't use add_query_arg(), as rawurlencode() is needed for authorize-application.php's JS to work correctly. |
| 307 | 423 | $authorize_url = admin_url( 'authorize-application.php' ) |
| @@ -314,10 +430,16 @@ | ||
| 314 | 430 | ) |
| 315 | 431 | ) |
| 316 | 432 | . '&reject_url=' . rawurlencode( $this->get_settings_url() ); |
| 317 | 433 | ?> |
| 318 | - <p> | |
| 319 | - <?php esc_html_e( 'Click Create Application Password to create a password that AI clients can use to connect to this site\'s MCP server.', 'convertkit' ); ?> | |
| 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 | + ?> | |
| 320 | 442 | </p> |
| 321 | 443 | <p> |
| 322 | 444 | <a href="<?php echo esc_attr( $authorize_url ); ?>" id="convertkit-settings-mcp-create-application-password" class="button button-primary"> |
| 323 | 445 | <?php esc_html_e( 'Create Application Password', 'convertkit' ); ?> |
| @@ -327,58 +449,97 @@ | ||
| 327 | 449 | |
| 328 | 450 | } |
| 329 | 451 | |
| 330 | 452 | /** |
| 331 | - * Renders the instructions and Disconnect section. | |
| 453 | + * Renders the Application Password's details, the authorization header (if the | |
| 454 | + * password was just created), and the Revoke Application Password button. | |
| 332 | 455 | * |
| 333 | - * @since 3.4.0 | |
| 456 | + * @since 3.4.1 | |
| 457 | + * | |
| 458 | + * @param array $application_password Application Password. | |
| 334 | 459 | */ |
| 335 | - public function instructions_disconnect_callback() { | |
| 460 | + private function output_application_password( $application_password ) { | |
| 336 | 461 | |
| 337 | 462 | // Build disconnect URL. |
| 338 | 463 | $disconnect_url = $this->get_settings_url( array( '_convertkit_settings_mcp_revoke_application_password' => wp_create_nonce( 'convertkit-mcp-revoke-application-password' ) ) ); |
| 339 | 464 | |
| 340 | - // Fetch query parameters to build the Basic auth header. | |
| 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 | |
| 341 | 492 | if ( $this->authorization_header ) { |
| 342 | 493 | ?> |
| 343 | 494 | <p> |
| 344 | - <strong><?php esc_html_e( 'Authorization Header:', 'convertkit' ); ?></strong> | |
| 345 | - <code id="kit-authorization-header">Basic <?php echo esc_html( $this->authorization_header ); ?></code> | |
| 495 | + <strong><?php esc_html_e( 'Authorization header:', 'convertkit' ); ?></strong> | |
| 346 | 496 | </p> |
| 347 | - <p> | |
| 348 | - <?php esc_html_e( 'Copy the above. It won\'t be displayed again. If you lose this, you\'ll need to revoke the Application Password and create a new one.', 'convertkit' ); ?> | |
| 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' ); ?> | |
| 349 | 502 | </p> |
| 350 | 503 | <?php |
| 351 | 504 | } else { |
| 352 | 505 | ?> |
| 353 | - <p> | |
| 354 | - <?php esc_html_e( 'An Application Password was previously created for this Plugin. It is not displayed here for security.', 'convertkit' ); ?> | |
| 355 | - <br /> | |
| 356 | - <?php esc_html_e( 'If you forgot your Application Password, you can revoke it using the Revoke Application Password button below, and then create a new one.', 'convertkit' ); ?> | |
| 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 | + ?> | |
| 357 | 514 | </p> |
| 358 | 515 | <?php |
| 359 | 516 | } |
| 360 | 517 | ?> |
| 518 | + | |
| 361 | 519 | <p> |
| 362 | 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> |
| 363 | 521 | </p> |
| 522 | + <?php | |
| 364 | 523 | |
| 365 | - <?php | |
| 366 | - // Build server URL and pre-encoded Basic auth header for use in the | |
| 367 | - // per-client configuration snippets below. | |
| 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. | |
| 368 | 536 | $server_url = ConvertKit_MCP::get_server_url(); |
| 369 | - $auth_header = $this->authorization_header | |
| 370 | - ? 'Basic ' . $this->authorization_header | |
| 371 | - : __( 'Your base64 encoded username and application password', 'convertkit' ); | |
| 537 | + $auth_header = 'Basic ' . ( $this->authorization_header ? $this->authorization_header : 'BASE64_ENCODED_USERNAME_AND_APPLICATION_PASSWORD' ); | |
| 372 | 538 | |
| 373 | - // Claude desktop / Cline JSON. | |
| 374 | - // | |
| 375 | - // The Authorization header value is inlined (not passed via the | |
| 376 | - // `env` block + `${VAR}` substitution as the mcp-remote docs | |
| 377 | - // suggest), because mcp-remote's variable substitution is unreliable | |
| 378 | - // with Basic auth values that contain `$` characters in their | |
| 379 | - // base64 payload — the substitution silently leaves the literal | |
| 380 | - // `${KIT_AUTH}` string in place, producing 401s. | |
| 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. | |
| 381 | 542 | $claude_desktop_config = wp_json_encode( |
| 382 | 543 | array( |
| 383 | 544 | 'mcpServers' => array( |
| 384 | 545 | 'kit-wordpress' => array( |
| @@ -395,8 +556,15 @@ | ||
| 395 | 556 | ), |
| 396 | 557 | JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES |
| 397 | 558 | ); |
| 398 | 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 | + | |
| 399 | 567 | // Cursor JSON. |
| 400 | 568 | $cursor_config = wp_json_encode( |
| 401 | 569 | array( |
| 402 | 570 | 'mcpServers' => array( |
| @@ -409,64 +577,160 @@ | ||
| 409 | 577 | ), |
| 410 | 578 | ), |
| 411 | 579 | JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES |
| 412 | 580 | ); |
| 413 | - ?> | |
| 414 | 581 | |
| 415 | - <h3><?php esc_html_e( 'Claude', 'convertkit' ); ?></h3> | |
| 416 | - <p> | |
| 417 | - <?php | |
| 418 | - printf( | |
| 419 | - /* translators: %s: Path to Claude desktop config file. */ | |
| 420 | - esc_html__( 'Add the following to your %s file, then restart Claude desktop:', 'convertkit' ), | |
| 421 | - '<code>claude_desktop_config.json</code>' | |
| 422 | - ); | |
| 423 | - ?> | |
| 424 | - <br /> | |
| 425 | - macOS: <code>~/Library/Application Support/Claude/claude_desktop_config.json</code> | |
| 426 | - <br /> | |
| 427 | - Windows: <code>%APPDATA%\Claude\claude_desktop_config.json</code> | |
| 428 | - </p> | |
| 429 | - <pre><code><?php echo esc_html( $claude_desktop_config ); ?></code></pre> | |
| 582 | + // Codex TOML. | |
| 583 | + $codex_config = '[mcp_servers.kit_wordpress]' . "\n" | |
| 584 | + . 'url = "' . $server_url . '"' . "\n" | |
| 585 | + . 'http_headers = { "Authorization" = "' . $auth_header . '" }'; | |
| 430 | 586 | |
| 431 | - <h3><?php esc_html_e( 'Claude Code', 'convertkit' ); ?></h3> | |
| 432 | - <p> | |
| 433 | - <?php esc_html_e( 'Run the following command in your terminal.', 'convertkit' ); ?> | |
| 434 | - <br /> | |
| 435 | - <code> | |
| 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"> | |
| 436 | 598 | <?php |
| 437 | - printf( | |
| 438 | - 'claude mcp add --transport http kit-wordpress %s --header "Authorization: %s"', | |
| 439 | - esc_html( $server_url ), | |
| 440 | - esc_html( $auth_header ) | |
| 441 | - ); | |
| 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 | + } | |
| 442 | 610 | ?> |
| 443 | - </code> | |
| 444 | - </p> | |
| 611 | + </ul> | |
| 445 | 612 | |
| 446 | - <h3><?php esc_html_e( 'Cursor', 'convertkit' ); ?></h3> | |
| 447 | - <p> | |
| 448 | - <?php | |
| 449 | - printf( | |
| 450 | - /* translators: %s: Path to Cursor MCP config file. */ | |
| 451 | - esc_html__( 'Add the following to your %s file, then restart Cursor:', 'convertkit' ), | |
| 452 | - '<code>~/.cursor/mcp.json</code>' | |
| 453 | - ); | |
| 454 | - ?> | |
| 455 | - </p> | |
| 456 | - <pre><code><?php echo esc_html( $cursor_config ); ?></code></pre> | |
| 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> | |
| 457 | 639 | |
| 458 | - <h3><?php esc_html_e( 'Other clients', 'convertkit' ); ?></h3> | |
| 459 | - <p> | |
| 460 | - <?php esc_html_e( 'For any other MCP client, provide it with the following:', 'convertkit' ); ?> | |
| 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' ); ?> | |
| 461 | 711 | </p> |
| 462 | - <p> | |
| 463 | - <strong><?php esc_html_e( 'Server URL:', 'convertkit' ); ?></strong> | |
| 464 | - <code><?php echo esc_html( $server_url ); ?></code> | |
| 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' ); ?> | |
| 465 | 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 | + ?> | |
| 466 | 729 | <p> |
| 467 | - <strong><?php esc_html_e( 'Authorization header:', 'convertkit' ); ?></strong> | |
| 468 | - <code><?php echo esc_html( $auth_header ); ?></code> | |
| 730 | + <a href="<?php echo esc_url( $this->documentation_url() ); ?>" target="_blank"> | |
| 731 | + <?php esc_html_e( 'Read the MCP documentation', 'convertkit' ); ?> | |
| 732 | + </a> | |
| 469 | 733 | </p> |
| 470 | 734 | <?php |
| 471 | 735 | |
| 472 | 736 | } |
| @@ -494,17 +758,38 @@ | ||
| 494 | 758 | |
| 495 | 759 | } |
| 496 | 760 | |
| 497 | 761 | /** |
| 498 | - * Finds the UUID of the most recently-created Application Password for the | |
| 499 | - * currently logged in user | |
| 762 | + * Returns whether the Kit account is on a paid plan. | |
| 500 | 763 | * |
| 501 | - * @since 3.4.0 | |
| 764 | + * @since 3.4.1 | |
| 502 | 765 | * |
| 503 | - * @return bool|string | |
| 766 | + * @return bool | |
| 504 | 767 | */ |
| 505 | - private function get_application_password_uuid() { | |
| 768 | + private function is_paid_plan() { | |
| 506 | 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 | + | |
| 507 | 792 | // Get the user's Application Passwords. |
| 508 | 793 | $passwords = WP_Application_Passwords::get_user_application_passwords( get_current_user_id() ); |
| 509 | 794 | |
| 510 | 795 | // Return false if no Application Passwords exist. |
| @@ -514,13 +799,35 @@ | ||
| 514 | 799 | |
| 515 | 800 | // Iterate through the Application Passwords and return the password that matches the app name. |
| 516 | 801 | foreach ( $passwords as $password ) { |
| 517 | 802 | if ( $password['name'] === CONVERTKIT_MCP_APP_NAME ) { |
| 518 | - return $password['uuid']; | |
| 803 | + return $password; | |
| 519 | 804 | } |
| 520 | 805 | } |
| 521 | 806 | |
| 522 | 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']; | |
| 523 | 830 | |
| 524 | 831 | } |
| 525 | 832 | |
| 526 | 833 | } |