Files
WP-LIT-MCP/src/Abilities/AbilityRegistrar.php
T
janniclas.loosenandClaude Opus 5.5 a9b2f2db71 fix: typed write failures, template rollback, parent and term cleanup
WriteFailed maps to lit_write_failed / lit_partial_write (partial writes are
logged with result partial); old template read before the update; row marker
set before wp_update_post; created terms deleted on rollback; parent must be a
readable non-descendant of a hierarchical type; featured image must be
readable; snapshot keeps empty values; meta comparison after sanitize_meta;
follow-up failures (listener, change log) only add warnings.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01At3V6SjwbMe6eMzVGW3Hs7
2026-09-25 23:11:18 +02:00

99 lines
3.5 KiB
PHP

<?php
declare(strict_types=1);
namespace LIT\MCP\Abilities;
use LIT\MCP\Access;
use LIT\MCP\Integrations\IntegrationRegistry;
use LIT\MCP\Server\RequestContext;
use Throwable;
use WP_Error;
/**
* Registers all abilities of available integrations with the Abilities API and
* wraps them with the role gate and neutral error handling.
*/
final class AbilityRegistrar
{
public const CATEGORY = 'lit-mcp';
public function __construct(private IntegrationRegistry $registry)
{
}
public function register(): void
{
add_action('wp_abilities_api_categories_init', [$this, 'registerCategory']);
add_action('wp_abilities_api_init', [$this, 'registerAbilities']);
}
public function registerCategory(): void
{
wp_register_ability_category(self::CATEGORY, [
'label' => 'LIT MCP',
'description' => 'Website-Pflege für WordPress, Divi und Pods über MCP.',
]);
}
public function registerAbilities(): void
{
foreach ($this->registry->abilities() as $ability) {
wp_register_ability($ability->name(), $this->argsFor($ability));
}
}
/** @return array<string, mixed> */
public function argsFor(Ability $ability): array
{
$args = [
'label' => $ability->label(),
'description' => $ability->description(),
'category' => self::CATEGORY,
'input_schema' => $ability->inputSchema(),
'permission_callback' => static function (mixed $input = null) use ($ability): bool|WP_Error {
if (! Access::currentUserMayUse()) {
return Errors::forbidden('Dein Benutzerkonto ist für LIT MCP nicht freigegeben.');
}
if (! RequestContext::isOAuthServerRequest()) {
return Errors::forbidden('LIT MCP ist nur über den OAuth-geschützten MCP-Server nutzbar.');
}
try {
return $ability->permission(is_array($input) ? $input : []);
} catch (Throwable $e) {
$message = sprintf('[wp-lit-mcp] %s failed: %s', $ability->name(), $e);
error_log($message); // phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_error_log
return Errors::internal();
}
},
'execute_callback' => static function (mixed $input = null) use ($ability): mixed {
try {
return $ability->execute(is_array($input) ? $input : []);
} catch (WriteFailed $e) {
$message = sprintf('[wp-lit-mcp] %s failed: %s', $ability->name(), $e);
error_log($message); // phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_error_log
return $e->toError();
} catch (Throwable $e) {
$message = sprintf('[wp-lit-mcp] %s failed: %s', $ability->name(), $e);
error_log($message); // phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_error_log
return Errors::internal();
}
},
'meta' => [
'annotations' => $ability->annotations(),
'show_in_rest' => false,
'mcp' => ['public' => true, 'type' => 'tool'],
],
];
$output = $ability->outputSchema();
if ($output !== null) {
$args['output_schema'] = $output;
}
return $args;
}
}