docs: drop per-ability isAvailable(); integrations omit unusable abilities instead
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01At3V6SjwbMe6eMzVGW3Hs7
This commit is contained in:
@@ -670,10 +670,10 @@ Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>"
|
|||||||
**Interfaces:**
|
**Interfaces:**
|
||||||
- Consumes: `Access::currentUserMayUse()`, `Access::CAPABILITY` (Task 2).
|
- Consumes: `Access::currentUserMayUse()`, `Access::CAPABILITY` (Task 2).
|
||||||
- Produces:
|
- Produces:
|
||||||
- `interface Ability { name(): string; label(): string; description(): string; inputSchema(): array; outputSchema(): ?array; annotations(): array; isDirect(): bool; isAvailable(): bool; permission(array $input): bool|\WP_Error; execute(array $input): mixed; }`
|
- `interface Ability { name(): string; label(): string; description(): string; inputSchema(): array; outputSchema(): ?array; annotations(): array; isDirect(): bool; permission(array $input): bool|\WP_Error; execute(array $input): mixed; }`
|
||||||
- `Errors::forbidden(string $message): \WP_Error` (Code `lit_forbidden`), `Errors::invalidInput(string $message, array $fields = []): \WP_Error` (Code `lit_validation_failed`), `Errors::notFound(string $message): \WP_Error` (Code `lit_not_found`), `Errors::internal(): \WP_Error` (Code `lit_internal_error`).
|
- `Errors::forbidden(string $message): \WP_Error` (Code `lit_forbidden`), `Errors::invalidInput(string $message, array $fields = []): \WP_Error` (Code `lit_validation_failed`), `Errors::notFound(string $message): \WP_Error` (Code `lit_not_found`), `Errors::internal(): \WP_Error` (Code `lit_internal_error`).
|
||||||
- `interface Integration { id(): string; isAvailable(): bool; /** @return list<Ability> */ abilities(): array; }`
|
- `interface Integration { id(): string; isAvailable(): bool; /** @return list<Ability> */ abilities(): array; }`
|
||||||
- `IntegrationRegistry::__construct(Integration ...$integrations)`, `available(): list<Integration>`, `abilities(): list<Ability>` (nur Abilities verfügbarer Integrationen, die selbst `isAvailable()` true liefern), `directAbilityNames(): list<string>`.
|
- `IntegrationRegistry::__construct(Integration ...$integrations)`, `available(): list<Integration>`, `abilities(): list<Ability>`, `directAbilityNames(): list<string>`.
|
||||||
- `AbilityRegistrar::CATEGORY = 'lit-mcp'`, `__construct(IntegrationRegistry $registry)`, `register(): void`, `registerCategory(): void`, `registerAbilities(): void`, `argsFor(Ability $ability): array`.
|
- `AbilityRegistrar::CATEGORY = 'lit-mcp'`, `__construct(IntegrationRegistry $registry)`, `register(): void`, `registerCategory(): void`, `registerAbilities(): void`, `argsFor(Ability $ability): array`.
|
||||||
|
|
||||||
- [ ] **Step 1: Failing Tests**
|
- [ ] **Step 1: Failing Tests**
|
||||||
@@ -710,23 +710,11 @@ final class IntegrationRegistryTest extends TestCase
|
|||||||
self::assertSame(['lit/a'], $registry->directAbilityNames());
|
self::assertSame(['lit/a'], $registry->directAbilityNames());
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testUnavailableAbilitiesAreSkippedEvenInAvailableIntegration(): void
|
private function ability(string $name, bool $direct): Ability
|
||||||
{
|
|
||||||
$working = $this->ability('lit/a', true);
|
|
||||||
$broken = $this->ability('lit/needs-missing-plugin', true, false);
|
|
||||||
|
|
||||||
$registry = new IntegrationRegistry($this->integration('core', true, [$working, $broken]));
|
|
||||||
|
|
||||||
self::assertSame([$working], $registry->abilities());
|
|
||||||
self::assertSame(['lit/a'], $registry->directAbilityNames());
|
|
||||||
}
|
|
||||||
|
|
||||||
private function ability(string $name, bool $direct, bool $available = true): Ability
|
|
||||||
{
|
{
|
||||||
$ability = Mockery::mock(Ability::class);
|
$ability = Mockery::mock(Ability::class);
|
||||||
$ability->allows('name')->andReturn($name);
|
$ability->allows('name')->andReturn($name);
|
||||||
$ability->allows('isDirect')->andReturn($direct);
|
$ability->allows('isDirect')->andReturn($direct);
|
||||||
$ability->allows('isAvailable')->andReturn($available);
|
|
||||||
|
|
||||||
return $ability;
|
return $ability;
|
||||||
}
|
}
|
||||||
@@ -784,7 +772,6 @@ final class AbilityRegistrarTest extends TestCase
|
|||||||
$ability->allows('outputSchema')->andReturn(['type' => 'object']);
|
$ability->allows('outputSchema')->andReturn(['type' => 'object']);
|
||||||
$ability->allows('annotations')->andReturn(['readonly' => true]);
|
$ability->allows('annotations')->andReturn(['readonly' => true]);
|
||||||
$ability->allows('isDirect')->andReturn(true);
|
$ability->allows('isDirect')->andReturn(true);
|
||||||
$ability->allows('isAvailable')->andReturn(true);
|
|
||||||
|
|
||||||
return $ability;
|
return $ability;
|
||||||
}
|
}
|
||||||
@@ -906,13 +893,6 @@ interface Ability
|
|||||||
/** Whether the ability is listed as its own MCP tool (otherwise only via meta-tools). */
|
/** Whether the ability is listed as its own MCP tool (otherwise only via meta-tools). */
|
||||||
public function isDirect(): bool;
|
public function isDirect(): bool;
|
||||||
|
|
||||||
/**
|
|
||||||
* Whether the ability can work on this installation (e.g. a required
|
|
||||||
* plugin function or version exists). Unavailable abilities are not
|
|
||||||
* registered at all, so they can never fail at call time.
|
|
||||||
*/
|
|
||||||
public function isAvailable(): bool;
|
|
||||||
|
|
||||||
/** @param array<string, mixed> $input */
|
/** @param array<string, mixed> $input */
|
||||||
public function permission(array $input): bool|WP_Error;
|
public function permission(array $input): bool|WP_Error;
|
||||||
|
|
||||||
@@ -1022,11 +1002,7 @@ final class IntegrationRegistry
|
|||||||
{
|
{
|
||||||
$abilities = [];
|
$abilities = [];
|
||||||
foreach ($this->available() as $integration) {
|
foreach ($this->available() as $integration) {
|
||||||
foreach ($integration->abilities() as $ability) {
|
array_push($abilities, ...$integration->abilities());
|
||||||
if ($ability->isAvailable()) {
|
|
||||||
$abilities[] = $ability;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return $abilities;
|
return $abilities;
|
||||||
@@ -1500,11 +1476,6 @@ final class GuideAbility implements Ability
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function isAvailable(): bool
|
|
||||||
{
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function permission(array $input): bool|WP_Error
|
public function permission(array $input): bool|WP_Error
|
||||||
{
|
{
|
||||||
return true;
|
return true;
|
||||||
@@ -1760,11 +1731,6 @@ final class SiteStatusAbility implements Ability
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function isAvailable(): bool
|
|
||||||
{
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function permission(array $input): bool|WP_Error
|
public function permission(array $input): bool|WP_Error
|
||||||
{
|
{
|
||||||
return true;
|
return true;
|
||||||
@@ -1877,11 +1843,9 @@ final class ServerRegistrarTest extends TestCase
|
|||||||
$direct = Mockery::mock(Ability::class);
|
$direct = Mockery::mock(Ability::class);
|
||||||
$direct->allows('name')->andReturn('lit/guide');
|
$direct->allows('name')->andReturn('lit/guide');
|
||||||
$direct->allows('isDirect')->andReturn(true);
|
$direct->allows('isDirect')->andReturn(true);
|
||||||
$direct->allows('isAvailable')->andReturn(true);
|
|
||||||
$indirect = Mockery::mock(Ability::class);
|
$indirect = Mockery::mock(Ability::class);
|
||||||
$indirect->allows('name')->andReturn('lit/rare-tool');
|
$indirect->allows('name')->andReturn('lit/rare-tool');
|
||||||
$indirect->allows('isDirect')->andReturn(false);
|
$indirect->allows('isDirect')->andReturn(false);
|
||||||
$indirect->allows('isAvailable')->andReturn(true);
|
|
||||||
|
|
||||||
$core = Mockery::mock(Integration::class);
|
$core = Mockery::mock(Integration::class);
|
||||||
$core->allows('id')->andReturn('core');
|
$core->allows('id')->andReturn('core');
|
||||||
|
|||||||
@@ -138,7 +138,7 @@ src/
|
|||||||
Admin/ Einstellungsseite, Protokoll-Ansicht (Settings API, WP_List_Table)
|
Admin/ Einstellungsseite, Protokoll-Ansicht (Settings API, WP_List_Table)
|
||||||
Integrations/
|
Integrations/
|
||||||
Integration.php Interface: isAvailable(), abilities(), prompts()
|
Integration.php Interface: isAvailable(), abilities(), prompts()
|
||||||
(jede Ability hat zusätzlich isAvailable(): nicht verfügbare werden nicht registriert)
|
(Sonderfälle innerhalb einer Integration: abilities() lässt nicht nutzbare Tools einfach weg)
|
||||||
WordPress/ Content, Schema, Status/Kopie, Revisionen, Menüs, Einstellungen
|
WordPress/ Content, Schema, Status/Kopie, Revisionen, Menüs, Einstellungen
|
||||||
Media/ Medien-Tools, Verwendungssuche
|
Media/ Medien-Tools, Verwendungssuche
|
||||||
Divi/
|
Divi/
|
||||||
|
|||||||
Reference in New Issue
Block a user