12namespace entropic::mcp {
21inline bool starts_with_ci(std::string_view s, std::string_view prefix) {
22 if (s.size() < prefix.size()) {
return false; }
23 for (
size_t i = 0; i < prefix.size(); ++i) {
24 if (std::tolower(
static_cast<unsigned char>(s[i]))
25 != std::tolower(
static_cast<unsigned char>(prefix[i]))) {
37inline std::string_view ltrim(std::string_view s) {
40 && std::isspace(
static_cast<unsigned char>(s[i])) != 0) {
54 for (
unsigned char c : s) {
55 if (std::isspace(c) == 0) {
return false; }
66 if (trimmed.empty() || trimmed.front() !=
'{') {
return false; }
67 auto pos = trimmed.find(
"\"error\"");
68 return pos != std::string_view::npos && pos < 32;
77 if (cap <= 0) {
return; }
78 auto cap_sz =
static_cast<size_t>(cap);
79 if (content.size() <= cap_sz) {
return; }
80 size_t lost = content.size() - cap_sz;
81 std::string tail =
"\n[... truncated, "
82 + std::to_string(lost) +
" more bytes]";
83 size_t keep = (cap_sz > tail.size())
84 ? cap_sz - tail.size() : 0;
95 auto trimmed = ltrim(content);
96 return starts_with_ci(trimmed,
"error")
97 || starts_with_ci(trimmed,
"[error]")