Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions examples/mcp/mcp_example_client.cc
Original file line number Diff line number Diff line change
Expand Up @@ -288,6 +288,9 @@ void demonstrateFeatures(McpClient& client, bool verbose) {
}
}
std::cerr << std::endl;
} else {
std::cerr << "[DEMO] Calculator returned error, content size: "
<< call_result.content.size() << std::endl;
}
} catch (const std::exception& e) {
std::cerr << "[ERROR] Tool call failed: " << e.what() << std::endl;
Expand Down
1 change: 1 addition & 0 deletions include/mcp/types.h
Original file line number Diff line number Diff line change
Expand Up @@ -493,6 +493,7 @@ struct Request {

// Generic result type for responses
// Note: ListResourcesResult is defined outside jsonrpc namespace but used here
// For tools and prompts, we use the vector types directly since they're simpler
using ResponseResult = variant<std::nullptr_t,
bool,
int,
Expand Down
68 changes: 50 additions & 18 deletions src/client/mcp_client.cc
Original file line number Diff line number Diff line change
Expand Up @@ -699,11 +699,18 @@ std::future<ListResourcesResult> McpClient::listResources(
if (response.error.has_value()) {
result_promise->set_exception(std::make_exception_ptr(
std::runtime_error(response.error->message)));
} else if (response.result.has_value()) {
// Extract ListResourcesResult from response
// ResponseResult variant directly contains ListResourcesResult
if (holds_alternative<ListResourcesResult>(response.result.value())) {
result_promise->set_value(
get<ListResourcesResult>(response.result.value()));
} else {
// Fallback: return empty result if type doesn't match
result_promise->set_value(ListResourcesResult());
}
} else {
// Parse ListResourcesResult from response
ListResourcesResult result;
// TODO: Parse response into result structure
result_promise->set_value(result);
result_promise->set_value(ListResourcesResult());
}
} catch (...) {
result_promise->set_exception(std::current_exception());
Expand Down Expand Up @@ -915,11 +922,16 @@ std::future<ListToolsResult> McpClient::listTools(
if (response.error.has_value()) {
result_promise->set_exception(std::make_exception_ptr(
std::runtime_error(response.error->message)));
} else {
// Parse ListToolsResult from response
} else if (response.result.has_value()) {
// Extract tools vector from response and wrap in ListToolsResult
// ResponseResult variant contains std::vector<Tool>
ListToolsResult result;
// TODO: Parse response into result structure
if (holds_alternative<std::vector<Tool>>(response.result.value())) {
result.tools = get<std::vector<Tool>>(response.result.value());
}
result_promise->set_value(result);
} else {
result_promise->set_value(ListToolsResult());
}
} catch (...) {
result_promise->set_exception(std::current_exception());
Expand Down Expand Up @@ -951,10 +963,11 @@ std::future<CallToolResult> McpClient::callTool(
auto params = make_metadata();
params["name"] = name;
if (arguments.has_value()) {
// Arguments is a Metadata object, merge it
for (const auto& arg : arguments.value()) {
params["arguments." + arg.first] = arg.second;
}
// Convert arguments to JSON string for nested object support
// Server expects "arguments" as a nested JSON object which is stored
// as a JSON string in Metadata since MetadataValue doesn't support nesting
auto args_json = json::metadataToJson(arguments.value());
params["arguments"] = args_json.toString();
}
auto params_ptr = std::make_shared<Metadata>(std::move(params));

Expand All @@ -977,11 +990,29 @@ std::future<CallToolResult> McpClient::callTool(
if (response.error.has_value()) {
result_promise->set_exception(std::make_exception_ptr(
std::runtime_error(response.error->message)));
} else {
// Parse CallToolResult from response
} else if (response.result.has_value()) {
// Extract CallToolResult from response
// Server returns Metadata with "content" (string) and "isError" (bool)
CallToolResult result;
// TODO: Parse response into result structure
if (holds_alternative<Metadata>(response.result.value())) {
auto metadata = get<Metadata>(response.result.value());
// Extract content string and convert to TextContent
auto content_it = metadata.find("content");
if (content_it != metadata.end() &&
holds_alternative<std::string>(content_it->second)) {
result.content.push_back(ExtendedContentBlock(
TextContent(get<std::string>(content_it->second))));
}
// Extract isError flag
auto error_it = metadata.find("isError");
if (error_it != metadata.end() &&
holds_alternative<bool>(error_it->second)) {
result.isError = get<bool>(error_it->second);
}
}
result_promise->set_value(result);
} else {
result_promise->set_value(CallToolResult());
}
} catch (...) {
result_promise->set_exception(std::current_exception());
Expand Down Expand Up @@ -1068,10 +1099,11 @@ std::future<GetPromptResult> McpClient::getPrompt(
auto params = make_metadata();
params["name"] = name;
if (arguments.has_value()) {
// Arguments is a Metadata object, merge it
for (const auto& arg : arguments.value()) {
params["arguments." + arg.first] = arg.second;
}
// Convert arguments to JSON string for nested object support
// Server expects "arguments" as a nested JSON object which is stored
// as a JSON string in Metadata since MetadataValue doesn't support nesting
auto args_json = json::metadataToJson(arguments.value());
params["arguments"] = args_json.toString();
}
auto params_ptr = std::make_shared<Metadata>(std::move(params));

Expand Down
5 changes: 3 additions & 2 deletions src/json/json_serialization.cc
Original file line number Diff line number Diff line change
Expand Up @@ -542,8 +542,9 @@ jsonrpc::ResponseResult deserialize_ResponseResult(const JsonValue& json) {
}
return jsonrpc::ResponseResult(blocks);
}
} else if (first.contains("name") && first.contains("inputSchema")) {
// Array of Tools
} else if (first.contains("name") && !first.contains("uri")) {
// Array of Tools - tools have "name" but not "uri"
// inputSchema is optional per MCP spec
std::vector<Tool> tools;
size_t size = json.size();
for (size_t i = 0; i < size; ++i) {
Expand Down
46 changes: 9 additions & 37 deletions src/server/mcp_server.cc
Original file line number Diff line number Diff line change
Expand Up @@ -897,23 +897,11 @@ jsonrpc::Response McpServer::handleListResources(
}
}

// Get resources from resource manager
// Get resources from resource manager and return directly
// ResponseResult variant supports ListResourcesResult
auto result = resource_manager_->listResources(cursor);

// Convert to response
// TODO: Serialize ListResourcesResult to ResponseResult
auto response_metadata =
make<Metadata>()
.add("resourceCount", static_cast<int64_t>(result.resources.size()))
.build();

if (result.nextCursor.has_value()) {
// Add nextCursor to the response
response_metadata["nextCursor"] = result.nextCursor.value();
}

return jsonrpc::Response::success(request.id,
jsonrpc::ResponseResult(response_metadata));
jsonrpc::ResponseResult(result));
}

jsonrpc::Response McpServer::handleReadResource(const jsonrpc::Request& request,
Expand Down Expand Up @@ -1003,18 +991,11 @@ jsonrpc::Response McpServer::handleUnsubscribe(const jsonrpc::Request& request,

jsonrpc::Response McpServer::handleListTools(const jsonrpc::Request& request,
SessionContext& session) {
// Get tools from tool registry
// Get tools from tool registry and return tools vector directly
// ResponseResult variant supports std::vector<Tool>
auto result = tool_registry_->listTools();

// Convert to response
// TODO: Serialize ListToolsResult to ResponseResult
auto response_metadata =
make<Metadata>()
.add("toolCount", static_cast<int64_t>(result.tools.size()))
.build();

return jsonrpc::Response::success(request.id,
jsonrpc::ResponseResult(response_metadata));
jsonrpc::ResponseResult(result.tools));
}

jsonrpc::Response McpServer::handleCallTool(const jsonrpc::Request& request,
Expand Down Expand Up @@ -1102,20 +1083,11 @@ jsonrpc::Response McpServer::handleListPrompts(const jsonrpc::Request& request,
}
}

// Get prompts from prompt registry
// Get prompts from prompt registry and return prompts vector directly
// ResponseResult variant supports std::vector<Prompt>
auto result = prompt_registry_->listPrompts(cursor);

// Convert to response
// TODO: Serialize ListPromptsResult to ResponseResult
auto response_metadata =
make<Metadata>()
.add("prompts",
std::string("Prompts list placeholder")) // Simplified - avoid
// nested metadata
.build();

return jsonrpc::Response::success(request.id,
jsonrpc::ResponseResult(response_metadata));
jsonrpc::ResponseResult(result.prompts));
}

jsonrpc::Response McpServer::handleGetPrompt(const jsonrpc::Request& request,
Expand Down