@@ -285,11 +285,13 @@ class Client
285285 response = invoke_request ();
286286 }
287287
288+ const auto & response_body = unwrap_rpc_result (response);
289+
288290 // Optional server-side progress events
289- if (options.progress_handler && response .contains (" progress" ) &&
290- response [" progress" ].is_array ())
291+ if (options.progress_handler && response_body .contains (" progress" ) &&
292+ response_body [" progress" ].is_array ())
291293 {
292- for (const auto & p : response [" progress" ])
294+ for (const auto & p : response_body [" progress" ])
293295 {
294296 float value = p.value (" progress" , 0 .0f );
295297 std::optional<float > total = std::nullopt ;
@@ -301,9 +303,9 @@ class Client
301303 }
302304
303305 // Notification forwarding (sampling/elicitation/roots) if provided by server
304- if (response .contains (" notifications" ) && response [" notifications" ].is_array ())
306+ if (response_body .contains (" notifications" ) && response_body [" notifications" ].is_array ())
305307 {
306- for (const auto & n : response [" notifications" ])
308+ for (const auto & n : response_body [" notifications" ])
307309 {
308310 if (!n.contains (" method" ))
309311 continue ;
@@ -385,16 +387,18 @@ class Client
385387 TaskStatus get_task_status (const std::string& task_id)
386388 {
387389 fastmcpp::Json response = call (" tasks/get" , {{" taskId" , task_id}});
390+ const auto & body = unwrap_rpc_result (response);
388391 TaskStatus status;
389- from_json (response , status);
392+ from_json (body , status);
390393 return status;
391394 }
392395
393396 // / Retrieve raw task result via MCP 'tasks/result' (tool/prompt/resource specific).
394397 // / Callers are responsible for parsing into appropriate result type.
395398 fastmcpp::Json get_task_result_raw (const std::string& task_id)
396399 {
397- return call (" tasks/result" , {{" taskId" , task_id}});
400+ fastmcpp::Json response = call (" tasks/result" , {{" taskId" , task_id}});
401+ return unwrap_rpc_result (response);
398402 }
399403
400404 // / List tasks via MCP 'tasks/list'. Returns raw JSON as provided by server.
@@ -406,16 +410,18 @@ class Client
406410 params[" cursor" ] = *cursor;
407411 if (limit > 0 )
408412 params[" limit" ] = limit;
409- return call (" tasks/list" , params);
413+ fastmcpp::Json response = call (" tasks/list" , params);
414+ return unwrap_rpc_result (response);
410415 }
411416
412417 // / Cancel a background task via MCP 'tasks/cancel'. Returns final task status.
413418 // / @throws fastmcpp::Error if task does not exist or server returns error
414419 TaskStatus cancel_task (const std::string& task_id)
415420 {
416421 fastmcpp::Json response = call (" tasks/cancel" , {{" taskId" , task_id}});
422+ const auto & body = unwrap_rpc_result (response);
417423 TaskStatus status;
418- from_json (response , status);
424+ from_json (body , status);
419425 return status;
420426 }
421427
@@ -705,9 +711,10 @@ class Client
705711 void poll_notifications ()
706712 {
707713 auto response = call (" notifications/poll" , fastmcpp::Json::object ());
708- if (!response.contains (" notifications" ) || !response[" notifications" ].is_array ())
714+ const auto & body = unwrap_rpc_result (response);
715+ if (!body.contains (" notifications" ) || !body[" notifications" ].is_array ())
709716 return ;
710- for (const auto & n : response [" notifications" ])
717+ for (const auto & n : body [" notifications" ])
711718 {
712719 if (!n.contains (" method" ))
713720 continue ;
@@ -916,36 +923,66 @@ class Client
916923 return value;
917924 }
918925
926+ const fastmcpp::Json& unwrap_rpc_result (const fastmcpp::Json& response)
927+ {
928+ if (!response.is_object ())
929+ return response;
930+
931+ if (response.contains (" error" ))
932+ {
933+ if (response[" error" ].is_object ())
934+ {
935+ const auto & error = response[" error" ];
936+ std::string message = error.value (" message" , " Unknown JSON-RPC error" );
937+ if (error.contains (" code" ) && error[" code" ].is_number_integer ())
938+ {
939+ throw fastmcpp::Error (" JSON-RPC error (" +
940+ std::to_string (error[" code" ].get <int >()) + " ): " +
941+ message);
942+ }
943+ throw fastmcpp::Error (" JSON-RPC error: " + message);
944+ }
945+ throw fastmcpp::Error (" JSON-RPC error: " + response[" error" ].dump ());
946+ }
947+
948+ if (response.contains (" result" ))
949+ return response[" result" ];
950+
951+ return response;
952+ }
953+
919954 ListToolsResult parse_list_tools_result (const fastmcpp::Json& response)
920955 {
956+ const auto & body = unwrap_rpc_result (response);
921957 ListToolsResult result;
922- if (response .contains (" tools" ))
923- for (const auto & t : response [" tools" ])
958+ if (body .contains (" tools" ))
959+ for (const auto & t : body [" tools" ])
924960 result.tools .push_back (t.get <ToolInfo>());
925- if (response .contains (" nextCursor" ))
926- result.nextCursor = response [" nextCursor" ].get <std::string>();
927- if (response .contains (" _meta" ))
928- result._meta = response [" _meta" ];
961+ if (body .contains (" nextCursor" ))
962+ result.nextCursor = body [" nextCursor" ].get <std::string>();
963+ if (body .contains (" _meta" ))
964+ result._meta = body [" _meta" ];
929965 return result;
930966 }
931967
932968 CallToolResult parse_call_tool_result (const fastmcpp::Json& response,
933969 const std::string& tool_name)
934970 {
971+ const auto & body = unwrap_rpc_result (response);
935972
936973 CallToolResult result;
937- result.isError = response .value (" isError" , false );
974+ result.isError = body .value (" isError" , false );
938975
939- if (!response .contains (" content" ))
976+ if (!body .contains (" content" ))
940977 throw fastmcpp::ValidationError (" tools/call response missing content" );
941978
942- if (response .contains (" content" ))
943- for (const auto & c : response [" content" ])
979+ if (body .contains (" content" ))
980+ for (const auto & c : body [" content" ])
944981 result.content .push_back (parse_content_block (c));
945982
946- if (response .contains (" structuredContent" ))
983+ if (body .contains (" structuredContent" ))
947984 {
948- result.structuredContent = response [" structuredContent" ];
985+ result.structuredContent = body [" structuredContent" ];
949986 // Try to provide a convenient data view similar to Python
950987 auto structured = *result.structuredContent ;
951988 auto it = tool_output_schemas_.find (tool_name);
@@ -1011,31 +1048,33 @@ class Client
10111048 }
10121049 }
10131050
1014- if (response .contains (" _meta" ))
1015- result.meta = response [" _meta" ];
1051+ if (body .contains (" _meta" ))
1052+ result.meta = body [" _meta" ];
10161053
10171054 return result;
10181055 }
10191056
10201057 ListResourcesResult parse_list_resources_result (const fastmcpp::Json& response)
10211058 {
1059+ const auto & body = unwrap_rpc_result (response);
10221060 ListResourcesResult result;
1023- if (response .contains (" resources" ))
1024- for (const auto & r : response [" resources" ])
1061+ if (body .contains (" resources" ))
1062+ for (const auto & r : body [" resources" ])
10251063 result.resources .push_back (r.get <ResourceInfo>());
1026- if (response .contains (" nextCursor" ))
1027- result.nextCursor = response [" nextCursor" ].get <std::string>();
1028- if (response .contains (" _meta" ))
1029- result._meta = response [" _meta" ];
1064+ if (body .contains (" nextCursor" ))
1065+ result.nextCursor = body [" nextCursor" ].get <std::string>();
1066+ if (body .contains (" _meta" ))
1067+ result._meta = body [" _meta" ];
10301068 return result;
10311069 }
10321070
10331071 ListResourceTemplatesResult parse_list_resource_templates_result (const fastmcpp::Json& response)
10341072 {
1073+ const auto & body = unwrap_rpc_result (response);
10351074 ListResourceTemplatesResult result;
1036- if (response .contains (" resourceTemplates" ))
1075+ if (body .contains (" resourceTemplates" ))
10371076 {
1038- for (const auto & r : response [" resourceTemplates" ])
1077+ for (const auto & r : body [" resourceTemplates" ])
10391078 {
10401079 ResourceTemplate rt;
10411080 rt.uriTemplate = r.at (" uriTemplate" ).get <std::string>();
@@ -1071,45 +1110,48 @@ class Client
10711110 result.resourceTemplates .push_back (rt);
10721111 }
10731112 }
1074- if (response .contains (" nextCursor" ))
1075- result.nextCursor = response [" nextCursor" ].get <std::string>();
1076- if (response .contains (" _meta" ))
1077- result._meta = response [" _meta" ];
1113+ if (body .contains (" nextCursor" ))
1114+ result.nextCursor = body [" nextCursor" ].get <std::string>();
1115+ if (body .contains (" _meta" ))
1116+ result._meta = body [" _meta" ];
10781117 return result;
10791118 }
10801119
10811120 ReadResourceResult parse_read_resource_result (const fastmcpp::Json& response)
10821121 {
1122+ const auto & body = unwrap_rpc_result (response);
10831123 ReadResourceResult result;
1084- if (response .contains (" contents" ))
1085- for (const auto & c : response [" contents" ])
1124+ if (body .contains (" contents" ))
1125+ for (const auto & c : body [" contents" ])
10861126 result.contents .push_back (parse_resource_content (c));
1087- if (response .contains (" _meta" ))
1088- result._meta = response [" _meta" ];
1127+ if (body .contains (" _meta" ))
1128+ result._meta = body [" _meta" ];
10891129 return result;
10901130 }
10911131
10921132 ListPromptsResult parse_list_prompts_result (const fastmcpp::Json& response)
10931133 {
1134+ const auto & body = unwrap_rpc_result (response);
10941135 ListPromptsResult result;
1095- if (response .contains (" prompts" ))
1096- for (const auto & p : response [" prompts" ])
1136+ if (body .contains (" prompts" ))
1137+ for (const auto & p : body [" prompts" ])
10971138 result.prompts .push_back (p.get <PromptInfo>());
1098- if (response .contains (" nextCursor" ))
1099- result.nextCursor = response [" nextCursor" ].get <std::string>();
1100- if (response .contains (" _meta" ))
1101- result._meta = response [" _meta" ];
1139+ if (body .contains (" nextCursor" ))
1140+ result.nextCursor = body [" nextCursor" ].get <std::string>();
1141+ if (body .contains (" _meta" ))
1142+ result._meta = body [" _meta" ];
11021143 return result;
11031144 }
11041145
11051146 GetPromptResult parse_get_prompt_result (const fastmcpp::Json& response)
11061147 {
1148+ const auto & body = unwrap_rpc_result (response);
11071149 GetPromptResult result;
1108- if (response .contains (" description" ))
1109- result.description = response [" description" ].get <std::string>();
1110- if (response .contains (" messages" ))
1150+ if (body .contains (" description" ))
1151+ result.description = body [" description" ].get <std::string>();
1152+ if (body .contains (" messages" ))
11111153 {
1112- for (const auto & m : response [" messages" ])
1154+ for (const auto & m : body [" messages" ])
11131155 {
11141156 PromptMessage msg;
11151157 std::string role = m.at (" role" ).get <std::string>();
@@ -1136,37 +1178,39 @@ class Client
11361178 result.messages .push_back (msg);
11371179 }
11381180 }
1139- if (response .contains (" _meta" ))
1140- result._meta = response [" _meta" ];
1181+ if (body .contains (" _meta" ))
1182+ result._meta = body [" _meta" ];
11411183 return result;
11421184 }
11431185
11441186 CompleteResult parse_complete_result (const fastmcpp::Json& response)
11451187 {
1188+ const auto & body = unwrap_rpc_result (response);
11461189 CompleteResult result;
1147- if (response .contains (" completion" ))
1190+ if (body .contains (" completion" ))
11481191 {
1149- const auto & c = response [" completion" ];
1192+ const auto & c = body [" completion" ];
11501193 if (c.contains (" values" ))
11511194 for (const auto & v : c[" values" ])
11521195 result.completion .values .push_back (v.get <std::string>());
11531196 if (c.contains (" total" ))
11541197 result.completion .total = c[" total" ].get <int >();
11551198 result.completion .hasMore = c.value (" hasMore" , false );
11561199 }
1157- if (response .contains (" _meta" ))
1158- result._meta = response [" _meta" ];
1200+ if (body .contains (" _meta" ))
1201+ result._meta = body [" _meta" ];
11591202 return result;
11601203 }
11611204
11621205 InitializeResult parse_initialize_result (const fastmcpp::Json& response)
11631206 {
1207+ const auto & body = unwrap_rpc_result (response);
11641208 InitializeResult result;
1165- result.protocolVersion = response .value (" protocolVersion" , " 2024-11-05" );
1209+ result.protocolVersion = body .value (" protocolVersion" , " 2024-11-05" );
11661210
1167- if (response .contains (" capabilities" ))
1211+ if (body .contains (" capabilities" ))
11681212 {
1169- const auto & caps = response [" capabilities" ];
1213+ const auto & caps = body [" capabilities" ];
11701214 if (caps.contains (" experimental" ))
11711215 result.capabilities .experimental = caps[" experimental" ];
11721216 if (caps.contains (" logging" ))
@@ -1185,17 +1229,17 @@ class Client
11851229 result.capabilities .extensions = caps[" extensions" ];
11861230 }
11871231
1188- if (response .contains (" serverInfo" ))
1232+ if (body .contains (" serverInfo" ))
11891233 {
1190- result.serverInfo .name = response [" serverInfo" ].value (" name" , " unknown" );
1191- result.serverInfo .version = response [" serverInfo" ].value (" version" , " unknown" );
1234+ result.serverInfo .name = body [" serverInfo" ].value (" name" , " unknown" );
1235+ result.serverInfo .version = body [" serverInfo" ].value (" version" , " unknown" );
11921236 }
11931237
1194- if (response .contains (" instructions" ))
1195- result.instructions = response [" instructions" ].get <std::string>();
1238+ if (body .contains (" instructions" ))
1239+ result.instructions = body [" instructions" ].get <std::string>();
11961240
1197- if (response .contains (" _meta" ))
1198- result._meta = response [" _meta" ];
1241+ if (body .contains (" _meta" ))
1242+ result._meta = body [" _meta" ];
11991243
12001244 return result;
12011245 }
@@ -1588,18 +1632,19 @@ inline std::shared_ptr<ResourceTask> Client::read_resource_task(const std::strin
15881632 payload[" _meta" ] = *propagated_meta;
15891633
15901634 auto response = call (" resources/read" , payload);
1635+ const auto & body = unwrap_rpc_result (response);
15911636
1592- if (response .contains (" _meta" ) && response [" _meta" ].contains (" modelcontextprotocol.io/task" ))
1637+ if (body .contains (" _meta" ) && body [" _meta" ].contains (" modelcontextprotocol.io/task" ))
15931638 {
1594- const auto & task_obj = response [" _meta" ][" modelcontextprotocol.io/task" ];
1639+ const auto & task_obj = body [" _meta" ][" modelcontextprotocol.io/task" ];
15951640 if (task_obj.contains (" taskId" ))
15961641 {
15971642 std::string task_id = task_obj[" taskId" ].get <std::string>();
15981643 return std::make_shared<ResourceTask>(this , std::move (task_id), uri, std::nullopt );
15991644 }
16001645 }
16011646
1602- ReadResourceResult result = parse_read_resource_result (response );
1647+ ReadResourceResult result = parse_read_resource_result (body );
16031648 return std::make_shared<ResourceTask>(this , std::string{}, uri, std::move (result.contents ));
16041649}
16051650
@@ -1625,18 +1670,19 @@ Client::get_prompt_task(const std::string& name, const fastmcpp::Json& arguments
16251670 payload[" _meta" ] = *propagated_meta;
16261671
16271672 auto response = call (" prompts/get" , payload);
1673+ const auto & body = unwrap_rpc_result (response);
16281674
1629- if (response .contains (" _meta" ) && response [" _meta" ].contains (" modelcontextprotocol.io/task" ))
1675+ if (body .contains (" _meta" ) && body [" _meta" ].contains (" modelcontextprotocol.io/task" ))
16301676 {
1631- const auto & task_obj = response [" _meta" ][" modelcontextprotocol.io/task" ];
1677+ const auto & task_obj = body [" _meta" ][" modelcontextprotocol.io/task" ];
16321678 if (task_obj.contains (" taskId" ))
16331679 {
16341680 std::string task_id = task_obj[" taskId" ].get <std::string>();
16351681 return std::make_shared<PromptTask>(this , std::move (task_id), name, std::nullopt );
16361682 }
16371683 }
16381684
1639- GetPromptResult result = parse_get_prompt_result (response );
1685+ GetPromptResult result = parse_get_prompt_result (body );
16401686 return std::make_shared<PromptTask>(this , std::string{}, name, std::move (result));
16411687}
16421688
0 commit comments