'LIT MCP', 'description' => 'Website-Pflege für WordPress, Divi und Pods über MCP.', ]); } public function registerAbilities(): void { $policy = $this->policy ?? new ExposurePolicy(); foreach ($policy->enabled($this->registry->abilities()) as $ability) { wp_register_ability($ability->name(), $this->argsFor($ability)); } } /** @return array */ 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) { Log::failure($ability->name(), $e); return Errors::internal(); } }, 'execute_callback' => static function (mixed $input = null) use ($ability): mixed { try { return $ability->execute(is_array($input) ? $input : []); } catch (WriteFailed $e) { Log::failure($ability->name(), $e); return $e->toError(); } catch (Throwable $e) { Log::failure($ability->name(), $e); return Errors::internal(); } }, 'meta' => [ 'annotations' => $ability->annotations(), 'show_in_rest' => false, // 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', ], ], ]; $output = $ability->outputSchema(); if ($output !== null) { $args['output_schema'] = $output; } return $args; } }