diff --git a/admin/section/class-convertkit-admin-section-mcp.php b/admin/section/class-convertkit-admin-section-mcp.php index daf2c91a8..4d04a0722 100644 --- a/admin/section/class-convertkit-admin-section-mcp.php +++ b/admin/section/class-convertkit-admin-section-mcp.php @@ -241,6 +241,27 @@ public function documentation_url() { } + /** + * Renders the upgrade CTA when the connected Kit account is on + * the free plan. + * + * @since 3.4.0 + */ + public function output_upgrade_required_message() { + + ?> +

+ +

+

+ + + +

+ is_paid_plan() ) { + // Disable saving settings. + $this->save_disabled = true; + + $this->output_upgrade_required_message(); + return; + } + // Output field. $this->output_checkbox_field( $args['name'], diff --git a/includes/class-convertkit-settings-mcp.php b/includes/class-convertkit-settings-mcp.php index 9e58b50f1..8e62099b2 100644 --- a/includes/class-convertkit-settings-mcp.php +++ b/includes/class-convertkit-settings-mcp.php @@ -65,7 +65,8 @@ public function get() { } /** - * Returns whether the MCP server is enabled. + * Returns whether the user has access to MCP via a paid plan, + * and if so whether the MCP server is enabled in the Plugin's settings. * * @since 3.4.0 * @@ -73,6 +74,13 @@ public function get() { */ public function enabled() { + // Bail if the connected Kit account isn't on a paid plan. + // This queries the cached account details, so no live API call is made. + $account = new ConvertKit_Resource_Account(); + if ( ! $account->is_paid_plan() ) { + return false; + } + return ( $this->settings['enabled'] === 'on' ? true : false ); } diff --git a/includes/mcp/class-convertkit-mcp.php b/includes/mcp/class-convertkit-mcp.php index 7be6e074a..cbc0b8559 100644 --- a/includes/mcp/class-convertkit-mcp.php +++ b/includes/mcp/class-convertkit-mcp.php @@ -259,6 +259,12 @@ public function register_abilities() { */ public function register_mcp_server( $adapter ) { + // Bail if the MCP server isn't enabled. + $settings = new ConvertKit_Settings_MCP(); + if ( ! $settings->enabled() ) { + return; + } + // Get abilities. $abilities = convertkit_get_abilities(); diff --git a/tests/EndToEnd/general/plugin-screens/PluginSettingsMCPCest.php b/tests/EndToEnd/general/plugin-screens/PluginSettingsMCPCest.php index cf0a22683..d74639698 100644 --- a/tests/EndToEnd/general/plugin-screens/PluginSettingsMCPCest.php +++ b/tests/EndToEnd/general/plugin-screens/PluginSettingsMCPCest.php @@ -22,9 +22,6 @@ public function _before(EndToEndTester $I) { // Activate Kit Plugin. $I->activateKitPlugin($I); - - // Setup Plugin. - $I->setupKitPlugin($I); } /** @@ -36,8 +33,20 @@ public function _before(EndToEndTester $I) */ public function testEnableAndDisableMCPServerSetting(EndToEndTester $I) { + // Simulate a Kit account that is on a paid plan. + $I->setupKitPlugin($I); + $I->haveOptionInDatabase( + 'convertkit_account', + [ + 'account' => [ + 'plan_type' => 'creator_pro', + ], + ] + ); + // Check that the MCP server is not registered. - $I->doesNotHaveRoute($I, '/kit-mcp'); + $I->doesNotHaveRoute($I, '/kit/mcp'); + $I->doesNotHaveRoute($I, '/kit/mcp/v1'); // Go to the Plugin's MCP Screen. $I->loadKitSettingsMCPScreen($I); @@ -88,6 +97,17 @@ public function testEnableAndDisableMCPServerSetting(EndToEndTester $I) */ public function testGenerateAndRevokeApplicationPassword(EndToEndTester $I) { + // Simulate a Kit account that is on a paid plan. + $I->setupKitPlugin($I); + $I->haveOptionInDatabase( + 'convertkit_account', + [ + 'account' => [ + 'plan_type' => 'creator_pro', + ], + ] + ); + // Go to the Plugin's MCP Screen. $I->loadKitSettingsMCPScreen($I); @@ -158,6 +178,86 @@ public function testGenerateAndRevokeApplicationPassword(EndToEndTester $I) $I->waitForElementNotVisible('#convertkit-settings-mcp-revoke-application-password'); } + /** + * Tests that a free-plan Kit account sees the upgrade CTA on the MCP tab + * instead of the enable / connect UI, and that the MCP REST route is not + * registered even when the enabled setting is on. + * + * @since 3.4.0 + * + * @param EndToEndTester $I Tester. + */ + public function testFreePlanShowsUpgradeCTA(EndToEndTester $I) + { + // Simulate a Kit account that is on the free plan. + $I->setupKitPluginFakeAPIKey($I); + $I->setupKitPluginResources($I); + $I->haveOptionInDatabase( + 'convertkit_account', + [ + 'account' => [ + 'plan_type' => 'free', + ], + ] + ); + + // Enable MCP server. + $I->haveOptionInDatabase( + '_wp_convertkit_settings_mcp', + [ + 'enabled' => 'on', + ] + ); + + // Load the MCP settings tab. + $I->loadKitSettingsMCPScreen($I); + + // Assert that the upgrade CTA is shown. + $I->see('The Kit WordPress MCP is available on paid Kit plans. Upgrade your Kit account to connect AI clients to your WordPress site.'); + $I->seeLink('Upgrade Kit Account'); + + // Assert no option to enable/disable the MCP server are shown. + $I->dontSeeElement('#enabled'); + $I->dontSee('Create Application Password'); + + // Assert that the MCP server is not registered. + $I->doesNotHaveRoute($I, '/kit/mcp'); + $I->doesNotHaveRoute($I, '/kit/mcp/v1'); + } + + /** + * Tests that a paid-plan Kit account sees the enable UI on the MCP tab + * (i.e. the upgrade CTA is not shown). + * + * @since 3.4.0 + * + * @param EndToEndTester $I Tester. + */ + public function testPaidPlanShowsEnableUI(EndToEndTester $I) + { + // Simulate a Kit account that is on a paid plan. + $I->setupKitPlugin($I); + $I->setupKitPluginResources($I); + $I->haveOptionInDatabase( + 'convertkit_account', + [ + 'account' => [ + 'plan_type' => 'creator_pro', + ], + ] + ); + + // Load the MCP settings tab. + $I->loadKitSettingsMCPScreen($I); + + // The upgrade CTA should not be shown. + $I->dontSee('The Kit WordPress MCP is available on paid Kit plans. Upgrade your Kit account to connect AI clients to your WordPress site.'); + $I->dontSeeLink('Upgrade Kit Account'); + + // The Enable checkbox should be visible. + $I->seeElement('#enabled'); + } + /** * Deactivate and reset Plugin(s) after each test, if the test passes. * We don't use _after, as this would provide a screenshot of the Plugin diff --git a/tests/Integration/SettingsMCPTest.php b/tests/Integration/SettingsMCPTest.php new file mode 100644 index 000000000..edb393b29 --- /dev/null +++ b/tests/Integration/SettingsMCPTest.php @@ -0,0 +1,132 @@ + '' ]); + update_option( + 'convertkit_account', + [ 'account' => [ 'plan_type' => 'creator_pro' ] ] + ); + + $settings = new \ConvertKit_Settings_MCP(); + $this->assertSame(false, $settings->enabled()); + } + + /** + * Test that enabled() returns false when the toggle is on but no account is + * cached (fail closed). + * + * @since 3.4.0 + */ + public function testEnabledFalseWhenToggleOnAndNoAccountCache() + { + update_option(\ConvertKit_Settings_MCP::SETTINGS_NAME, [ 'enabled' => 'on' ]); + delete_option('convertkit_account'); + + $settings = new \ConvertKit_Settings_MCP(); + $this->assertSame(false, $settings->enabled()); + } + + /** + * Test that enabled() returns false when the toggle is on but the cached + * plan is free. + * + * @since 3.4.0 + */ + public function testEnabledFalseWhenToggleOnAndFreePlan() + { + update_option(\ConvertKit_Settings_MCP::SETTINGS_NAME, [ 'enabled' => 'on' ]); + update_option( + 'convertkit_account', + [ 'account' => [ 'plan_type' => 'free' ] ] + ); + + $settings = new \ConvertKit_Settings_MCP(); + $this->assertSame(false, $settings->enabled()); + } + + /** + * Test that enabled() returns true when the toggle is on and the cached + * plan is a paid plan. + * + * @since 3.4.0 + */ + public function testEnabledTrueWhenToggleOnAndPaidPlan() + { + update_option(\ConvertKit_Settings_MCP::SETTINGS_NAME, [ 'enabled' => 'on' ]); + update_option( + 'convertkit_account', + [ 'account' => [ 'plan_type' => 'creator' ] ] + ); + + $settings = new \ConvertKit_Settings_MCP(); + $this->assertSame(true, $settings->enabled()); + } + + /** + * Test that enabled() returns true for creator_pro plans. + * + * @since 3.4.0 + */ + public function testEnabledTrueForCreatorProPlan() + { + update_option(\ConvertKit_Settings_MCP::SETTINGS_NAME, [ 'enabled' => 'on' ]); + update_option( + 'convertkit_account', + [ 'account' => [ 'plan_type' => 'creator_pro' ] ] + ); + + $settings = new \ConvertKit_Settings_MCP(); + $this->assertSame(true, $settings->enabled()); + } +}