$value) { $request->set_param($key, $value); } // The plugin's controllers read the body for non-GET verbs; setting it // as JSON keeps nested arrays (weekly schedules, booking fields) intact // rather than flattening them the way a form-encoded body would. if ($method !== 'GET') { $request->set_header('content-type', 'application/json'); $request->set_body(wp_json_encode($body)); } $response = rest_do_request($request); if ($response->is_error()) { return self::translateError($response); } return (array) $response->get_data(); } /** * Turn a REST failure into an MCP error whose message is the one the admin * UI would have shown, so the agent gets the real reason rather than * "request failed". * * @return \WP_Error */ private static function translateError($response) { $status = $response->get_status(); $data = (array) $response->get_data(); // Three payload shapes reach here, and none of them is the other two: // // {message, errors} Controller::sendError() // {code, message, data} a WP_Error, e.g. from a policy // {field: {rule: message}} a 422 from the framework's validator // // WP_Error::as_error() only understands the second and warns on the // others, so the payload is read directly. $message = (string) Arr::get($data, 'message', ''); $fieldErrors = self::flattenFieldErrors($data); if (!$message && $fieldErrors) { // Lead with the real reasons rather than "the request failed" — // an agent that is told "Event title field is required" fixes its // call in one step. $message = implode(' ', array_values($fieldErrors)); } if (!$message) { $message = __('The request could not be completed.', 'fluent-booking'); } if ($status === 401 || $status === 403) { return MCPHelper::error('permission_denied', $message); } if ($status === 404) { return MCPHelper::error('not_found', $message); } return MCPHelper::error( $status === 422 ? 'validation_failed' : 'request_failed', $message, $fieldErrors ? ['field_errors' => $fieldErrors] : [] ); } /** * Reduce whichever error shape arrived to `field => message`. * * @param array $data * * @return array */ private static function flattenFieldErrors($data) { $errors = Arr::get($data, 'errors'); if (!is_array($errors)) { // A bare validator payload: every key is a field, and `message` is // the only key that is not. $errors = $data; unset($errors['message'], $errors['code'], $errors['data']); } $flat = []; foreach ((array) $errors as $field => $messages) { if (is_string($messages)) { $flat[$field] = $messages; continue; } if (is_array($messages)) { $first = reset($messages); if (is_string($first)) { $flat[$field] = $first; } } } return $flat; } }