diff --git a/src/Server/ServerRegistrar.php b/src/Server/ServerRegistrar.php index 55e9122..abec288 100644 --- a/src/Server/ServerRegistrar.php +++ b/src/Server/ServerRegistrar.php @@ -8,12 +8,16 @@ use LIT\MCP\Guides\InstructionsBuilder; use LIT\MCP\Integrations\IntegrationRegistry; use WP\MCP\Core\McpAdapter; use WP\MCP\Infrastructure\ErrorHandling\ErrorLogMcpErrorHandler; +use WPMedia\MCP\OAuth\Context as OAuthContext; use WPMedia\MCP\OAuth\Transport\OAuthHttpTransport; use WPMedia\MCP\OAuth\Transport\ServerRegistrar as OAuthServerRegistrar; /** * Registers the OAuth-protected MCP server with our own tool list. * + * `register()` disables mcp-adapter's default server (`mcp_adapter_create_default_server`) + * so no second, unprotected MCP endpoint is exposed alongside ours. + * * wp-media/mcp-oauth hardcodes server id/route (token audience) and only three * meta-tools, and registers its own server unconditionally on the same * `mcp_adapter_init` action (priority 10). We register the same server id @@ -43,11 +47,18 @@ final class ServerRegistrar ?callable $oauthEnabled = null ) { $this->oauthEnabled = $oauthEnabled - ?? static fn (): bool => (bool) apply_filters('wpmedia_mcp_oauth_server_enabled', true); + ?? static fn (): bool => (new OAuthContext())->is_enabled(); } public function register(): void { + // mcp-adapter's default server is a second, unprotected (plain HttpTransport, + // is_user_logged_in()) endpoint exposing execute-ability – and thus our own + // abilities. We register our own OAuth-protected server instead; mcp-oauth's + // ServerRegistrar registers the shared meta-abilities itself once this filter + // disables the default server (see its ensure_shared_abilities_registered()). + add_filter('mcp_adapter_create_default_server', '__return_false'); + add_action('mcp_adapter_init', [$this, 'createServer'], 5); } @@ -95,7 +106,7 @@ final class ServerRegistrar $args['version'], [OAuthHttpTransport::class], ErrorLogMcpErrorHandler::class, - null, + (new OAuthContext())->observability_handler(), $args['tools'] ); diff --git a/tests/Unit/Server/ServerRegistrarTest.php b/tests/Unit/Server/ServerRegistrarTest.php index bd916dd..215f5b2 100644 --- a/tests/Unit/Server/ServerRegistrarTest.php +++ b/tests/Unit/Server/ServerRegistrarTest.php @@ -4,6 +4,7 @@ declare(strict_types=1); namespace LIT\MCP\Tests\Unit\Server; +use Brain\Monkey\Filters; use Brain\Monkey\Functions; use LIT\MCP\Abilities\Ability; use LIT\MCP\Guides\InstructionsBuilder; @@ -85,6 +86,34 @@ final class ServerRegistrarTest extends TestCase self::assertFalse($oauthChecked, 'createServer must bail out before touching the adapter'); } + public function testRegisterDisablesAdapterDefaultServerAndHooksCreateServer(): void + { + $registrar = new ServerRegistrar($this->registry(), $this->instructions()); + + Functions\expect('add_filter') + ->once() + ->with('mcp_adapter_create_default_server', '__return_false'); + Functions\expect('add_action') + ->once() + ->with('mcp_adapter_init', [$registrar, 'createServer'], 5); + + $registrar->register(); + } + + public function testDefaultOauthEnabledDelegatesToMcpOauthContext(): void + { + // LIT\MCP\Server\ServerRegistrar::__construct() defaults to + // (new \WPMedia\MCP\OAuth\Context())->is_enabled() – exercise that real + // Context, only stubbing the filter it reads, to prove we track its notion + // of "enabled" (including the deprecated rocket_mcp_oauth_server_enabled + // filter Context::is_enabled() also seeds from) instead of duplicating it. + Filters\expectApplied('wpmedia_mcp_oauth_server_enabled')->once()->andReturn(false); + + $registrar = new ServerRegistrar($this->registry(), $this->instructions()); + + self::assertNull($registrar->serverArguments()); + } + public function testUnhookOAuthDefaultServerRemovesOnlyTheMcpOauthCallback(): void { $oauthRegistrar = Mockery::mock(OAuthServerRegistrar::class);