*/ private $decode; /** @var callable(int, string): mixed */ private $recordUsage; /** * @param (callable(string): ?array)|null $decode JWT → claims, null if invalid. * @param (callable(int, string): mixed)|null $recordUsage user id, application password uuid. */ public function __construct(?callable $decode = null, ?callable $recordUsage = null) { $this->decode = $decode ?? static function (string $token): ?array { $claims = JWT::decode($token, SecretManager::get_secret()); return is_array($claims) ? $claims : null; }; $this->recordUsage = $recordUsage ?? static function (int $userId, string $uuid): void { \WP_Application_Passwords::record_application_password_usage($userId, $uuid); }; } public function register(): void { add_filter('rest_request_after_callbacks', [$this, 'record'], 10, 3); } /** * @param array $handler * @return mixed The response, unchanged. */ public function record(mixed $response, array $handler, WP_REST_Request $request): mixed { $route = '/' . ServerRegistrar::NAMESPACE . '/' . ServerRegistrar::ROUTE; if ($request->get_route() !== $route || ! self::succeeded($response)) { return $response; } $header = $request->get_header('authorization'); $userId = get_current_user_id(); if (! is_string($header) || stripos($header, 'Bearer ') !== 0 || $userId <= 0) { return $response; } try { $claims = ($this->decode)(substr($header, 7)); $uuid = is_array($claims) ? (string) ($claims['app_pass_id'] ?? '') : ''; // Only the password of the user this request actually authenticated as. if ($uuid !== '' && (int) ($claims['sub'] ?? 0) === $userId) { ($this->recordUsage)($userId, $uuid); } } catch (Throwable) { // Bookkeeping only – never break the MCP response. } return $response; } /** * Only successful answers count as use: mcp-oauth answers auth failures with a * WP_REST_Response(401), not a WP_Error (OAuthHttpTransport::build_auth_error_response). */ private static function succeeded(mixed $response): bool { if (is_wp_error($response)) { return false; } return ! $response instanceof \WP_HTTP_Response || $response->get_status() < 400; } }