As mentioned in the comments, you must know the error codes, which could be received from the remote server.
The std::string which you receive from a remote server contains 2 parts as you said,
The problem is that these are identified by strings, eg, "0A01" and also contain a message, and error code requires an integer as value.
As you haven't shared the format of the error message, I am not adding the code for spiting it, split your string into 2 parts, 
- Error Code
- Error Message
Now you can convert Error Code of type std::string to int by using std::stoi(error_code), So lets say
int error_code_int = std::stoi(string_to_hexadecimal(error_code));
And for std::error_category which serves as base class for our custom error messages, do this,
std::string message_received = "This is the message which received from remote server.";
struct OurCustomErrCategory : std::error_category
{
  const char* name() const noexcept override;
  std::string message(int ev) const override;
};
const char* OurCustomErrCategory::name() const noexcept
{
  return "Error Category Name";
}
std::string OurCustomErrCategory::message(int error_code_int) const
{
    switch (error_code_int)
    {
    case 1:
        return message_received;
    default:
        return "(unrecognized error)";
  }
}
const OurCustomErrCategory ourCustomErrCategoryObject;
std::error_code make_error_code(int e)
{
  return {e, ourCustomErrCategoryObject};
}
int main()
{
    int error_code_int = std::stoi(string_to_hexadecimal(error_code));  // error_code = 0A01
    ourCustomErrCategoryObject.message(error_code_int);
    std::error_code ec(error_code_int , ourCustomErrCategoryObject);
    assert(ec);
    std::cout << ec << std::endl;
    std::cout << ec.message() << std::endl;
}
The output for above working example is
Error Category Name : 0A01
This is the message which received from remote server.
You can use function string_to_hexadecimal() from this post.
I hope that now you can modify the above code according to your needs.
Edit 1:
As you said that:
This assumes the dynamic message is a global value. How do I pass it
  to an std::error_category object?
You can see that both std::error_code::assign and constructor std::error_code::error_code are taking parameters of int for error code number and error_category. So It is obvious that std::error_code can't take the dynamic message.
But wait, I said std::error_code are taking error_category as an argument in constructor, so is there any way, we can assign the dynamic message there ?
std::error_category states that:
std::error_category serves as the base class for specific error
  category types.
So it means that the struct we derived from std::error_category at the following line
struct OurCustomErrCategory : std::error_category
can have a data member and we can assign it via member function, so our struct will become like that,
struct OurCustomErrCategory : std::error_category
{
    std::string message_received;
    OurCustomErrCategory(std::string m) : message_received(m) {}
    const char* name() const noexcept override;
    std::string message(int ev) const override;
};
and you can assign it like that wherever you want,
const OurCustomErrCategory ourCustomErrCategoryObject("This is the message which received from remote server.");