diff --git a/src/Abilities/AbilityRegistrar.php b/src/Abilities/AbilityRegistrar.php index 6b14c6f..439044a 100644 --- a/src/Abilities/AbilityRegistrar.php +++ b/src/Abilities/AbilityRegistrar.php @@ -85,7 +85,13 @@ final class AbilityRegistrar 'meta' => [ 'annotations' => $ability->annotations(), 'show_in_rest' => false, - 'mcp' => ['public' => true, 'type' => 'tool'], + // mcp.public only governs the adapter's meta-tools (discover/get-info/execute); + // direct tools are listed on the server explicitly. Direct-only abilities + // (image/resource results) would not work through execute, so hide them there. + 'mcp' => [ + 'public' => ! in_array($ability->name(), ExposurePolicy::DIRECT_ONLY, true), + 'type' => 'tool', + ], ], ]; diff --git a/tests/Unit/Abilities/AbilityRegistrarTest.php b/tests/Unit/Abilities/AbilityRegistrarTest.php index 33b1ece..6b96417 100644 --- a/tests/Unit/Abilities/AbilityRegistrarTest.php +++ b/tests/Unit/Abilities/AbilityRegistrarTest.php @@ -69,6 +69,24 @@ final class AbilityRegistrarTest extends TestCase self::assertFalse($args['meta']['show_in_rest']); } + public function testDirectOnlyAbilitiesAreNotPublicForTheMetaTools(): void + { + $ability = Mockery::mock(Ability::class); + $ability->allows('name')->andReturn('lit/media-download'); + $ability->allows('label')->andReturn('Download'); + $ability->allows('description')->andReturn('Download'); + $ability->allows('inputSchema')->andReturn(['type' => 'object', 'properties' => []]); + $ability->allows('outputSchema')->andReturn(null); + $ability->allows('annotations')->andReturn(['readonly' => true]); + $ability->allows('isDirect')->andReturn(true); + + $args = $this->registrarFor($ability)->argsFor($ability); + + self::assertFalse($args['meta']['mcp']['public']); + self::assertSame('tool', $args['meta']['mcp']['type']); + self::assertIsCallable($args['execute_callback']); + } + public function testPermissionDeniedWithoutLitCapability(): void { Functions\when('current_user_can')->justReturn(false);