fix bugs of tool calls

main
hkr04 2025-03-09 17:10:01 +08:00
parent ebf5c2ea1f
commit b62d328780
6 changed files with 76 additions and 49 deletions

View File

@ -105,11 +105,11 @@ public:
/** /**
* @brief Call a tool * @brief Call a tool
* @param tool_name The name of the tool to call * @param tool_name The name of the tool to call
* @param parameters The parameters to pass to the tool * @param arguments The arguments to pass to the tool
* @return The result of the tool call * @return The result of the tool call
* @throws mcp_exception on error * @throws mcp_exception on error
*/ */
json call_tool(const std::string& tool_name, const json& parameters = json::object()); json call_tool(const std::string& tool_name, const json& arguments = json::object());
/** /**
* @brief Get available tools * @brief Get available tools

View File

@ -109,10 +109,10 @@ json client::get_server_capabilities() {
return server_capabilities_; return server_capabilities_;
} }
json client::call_tool(const std::string& tool_name, const json& parameters) { json client::call_tool(const std::string& tool_name, const json& arguments) {
return send_request("tools/call", { return send_request("tools/call", {
{"name", tool_name}, {"name", tool_name},
{"parameters", parameters} {"arguments", arguments}
}).result; }).result;
} }
@ -126,8 +126,8 @@ std::vector<tool> client::get_tools() {
t.name = tool_json["name"]; t.name = tool_json["name"];
t.description = tool_json["description"]; t.description = tool_json["description"];
if (tool_json.contains("parameters")) { if (tool_json.contains("inputSchema")) {
t.parameters_schema = tool_json["parameters"]; t.parameters_schema = tool_json["inputSchema"];
} }
tools.push_back(t); tools.push_back(t);

View File

@ -159,8 +159,19 @@ void server::register_tool(const tool& tool, tool_handler handler) {
throw mcp_exception(error_code::invalid_params, "Tool not found: " + tool_name); throw mcp_exception(error_code::invalid_params, "Tool not found: " + tool_name);
} }
json tool_params = params.contains("parameters") ? params["parameters"] : json::object(); json tool_args = params.contains("arguments") ? params["arguments"] : json::array();
return it->second.second(tool_params);
json tool_result = {
{"isError", false}
};
try {
tool_result["content"] = it->second.second(tool_args);
} catch (const std::exception& e) {
tool_result["isError"] = true;
}
return tool_result;
}; };
} }
} }
@ -332,7 +343,7 @@ json server::handle_initialize(const request& req) {
error_code::invalid_params, error_code::invalid_params,
"Unsupported protocol version", "Unsupported protocol version",
{ {
{"supported", MCP_VERSION}, {"supported", {MCP_VERSION}},
{"requested", params["protocolVersion"]} {"requested", params["protocolVersion"]}
} }
).to_json(); ).to_json();

View File

@ -14,8 +14,11 @@
#include <future> #include <future>
// Mock tool handler function // Mock tool handler function
mcp::json echo_tool_handler(const mcp::json& params) { mcp::json echo_tool_handler(const mcp::json& args) {
return params; return {{
{"type", "text"},
{"text", args["message"]}
}};
} }
// Mock resource handler // Mock resource handler
@ -96,12 +99,8 @@ protected:
// Test client initialization // Test client initialization
TEST_F(ClientTest, InitializeTest) { TEST_F(ClientTest, InitializeTest) {
// Initialize client // Initialize client
bool initialized = client->initialize("TestClient", mcp::MCP_VERSION); bool initialized = client->initialize("TestClient", "1.0.0");
EXPECT_TRUE(initialized); EXPECT_TRUE(initialized);
// Verify server information - Note: these methods may not exist, modify according to actual implementation
// EXPECT_EQ(client->get_server_name(), "TestServer");
// EXPECT_EQ(client->get_server_version(), "2024-11-05");
} }
// Test getting server capabilities // Test getting server capabilities
@ -139,10 +138,15 @@ TEST_F(ClientTest, CallToolTest) {
client->initialize("TestClient", mcp::MCP_VERSION); client->initialize("TestClient", mcp::MCP_VERSION);
// Call echo tool // Call echo tool
mcp::json params = {{"message", "Test message"}}; mcp::json args = {{"message", "Test message"}};
mcp::json result = client->call_tool("echo", params); mcp::json result = client->call_tool("echo", args);
EXPECT_TRUE(result.contains("content"));
EXPECT_TRUE(result["content"].is_array());
EXPECT_EQ(result["content"].size(), 1);
EXPECT_EQ(result["message"], "Test message"); EXPECT_EQ(result["content"][0]["type"], "text");
EXPECT_EQ(result["content"][0]["text"], "Test message");
} }
// Test getting resource list // Test getting resource list
@ -175,19 +179,6 @@ TEST_F(ClientTest, AccessResourceTest) {
EXPECT_EQ(result["params"]["query"], "Test query"); EXPECT_EQ(result["params"]["query"], "Test query");
} }
// Test ping method
TEST_F(ClientTest, PingTest) {
// Initialize client
client->initialize("TestClient", mcp::MCP_VERSION);
// Send ping - Note: this method may not exist, modify according to actual implementation
// bool pong = client->ping();
// EXPECT_TRUE(pong);
// Alternative test: ensure client can still call methods
EXPECT_NO_THROW(client->get_server_capabilities());
}
// Test error handling // Test error handling
TEST_F(ClientTest, ErrorHandlingTest) { TEST_F(ClientTest, ErrorHandlingTest) {
// Initialize client // Initialize client
@ -242,5 +233,10 @@ TEST_F(ClientTest, CancellationTest) {
mcp::json result = future.get(); mcp::json result = future.get();
// Since we didn't actually cancel the request, it should return normal result // Since we didn't actually cancel the request, it should return normal result
EXPECT_TRUE(result.contains("message")); EXPECT_TRUE(result.contains("content"));
EXPECT_TRUE(result["content"].is_array());
EXPECT_EQ(result["content"].size(), 1);
EXPECT_EQ(result["content"][0]["type"], "text");
EXPECT_EQ(result["content"][0]["text"], "This call should be cancelled");
} }

View File

@ -43,9 +43,14 @@ protected:
.with_string_param("input", "Input parameter") .with_string_param("input", "Input parameter")
.build(); .build();
server->register_tool(test_tool, [](const mcp::json& params) { server->register_tool(test_tool, [](const mcp::json& params) -> mcp::json {
std::string input = params.contains("input") ? params["input"].get<std::string>() : "Default input"; std::string input = params.contains("input") ? params["input"].get<std::string>() : "Default input";
return mcp::json{{"output", "Result: " + input}}; return {
{
{"type", "text"},
{"text", "Result: " + input}
}
};
}); });
// Register resources // Register resources
@ -531,7 +536,7 @@ TEST_F(DirectRequestTest, ClientAndDirectRequests) {
// Use client API to call tool // Use client API to call tool
try { try {
mcp::json tool_result = client.call_tool("test_tool", {{"input", "Client API call"}}); mcp::json tool_result = client.call_tool("test_tool", {{"input", "Client API call"}});
EXPECT_EQ(tool_result["output"], "Result: Client API call"); EXPECT_EQ(tool_result["content"][0]["text"], "Result: Client API call");
} catch (const std::exception& e) { } catch (const std::exception& e) {
// Client API may throw exception if server doesn't implement callTool method // Client API may throw exception if server doesn't implement callTool method
std::cout << "Client API tool call failed: " << e.what() << std::endl; std::cout << "Client API tool call failed: " << e.what() << std::endl;

View File

@ -17,9 +17,19 @@
// Mock tool handler function // Mock tool handler function
mcp::json test_tool_handler(const mcp::json& params) { mcp::json test_tool_handler(const mcp::json& params) {
if (params.contains("input")) { if (params.contains("input")) {
return {{"output", "Result: " + params["input"].get<std::string>()}}; return {
{
{"type", "text"},
{"text", "Result: " + params["input"].get<std::string>()}
}
};
} else { } else {
return {{"output", "Default result"}}; return {
{
{"type", "text"},
{"text", "Default result"}
}
};
} }
} }
@ -163,7 +173,16 @@ TEST_F(ServerTest, MethodRegistrationTest) {
.with_string_param("param1", "Parameter 1", false) .with_string_param("param1", "Parameter 1", false)
.build(), .build(),
[](const mcp::json& params) -> mcp::json { [](const mcp::json& params) -> mcp::json {
return {{"result", "Method call successful"}, {"params", params}}; return {
{
{"type", "text"},
{"text", "Method call successful"}
},
{
{"type", "text"},
{"text", "params: " + params.dump()}
}
};
} }
); );
@ -177,8 +196,8 @@ TEST_F(ServerTest, MethodRegistrationTest) {
// Call method (via tool) // Call method (via tool)
mcp::json result = client.call_tool("test_method", {{"param1", "value1"}}); mcp::json result = client.call_tool("test_method", {{"param1", "value1"}});
EXPECT_EQ(result["result"], "Method call successful"); EXPECT_EQ(result["content"][0]["text"], "Method call successful");
EXPECT_EQ(result["params"]["param1"], "value1"); EXPECT_EQ(result["content"][1]["text"], "params: {\"param1\":\"value1\"}");
// Call non-existent method // Call non-existent method
EXPECT_THROW(client.call_tool("non_existent_method"), mcp::mcp_exception); EXPECT_THROW(client.call_tool("non_existent_method"), mcp::mcp_exception);
@ -215,7 +234,7 @@ TEST_F(ServerTest, ServerClientInteractionTest) {
// Call tool // Call tool
mcp::json tool_result = client.call_tool("test_tool", {{"input", "Test input"}}); mcp::json tool_result = client.call_tool("test_tool", {{"input", "Test input"}});
EXPECT_EQ(tool_result["output"], "Result: Test input"); EXPECT_EQ(tool_result["content"][0]["text"], "Result: Test input");
// Access resource // Access resource
mcp::json resource_result = client.access_resource("/mock", {{"query", "Test query"}}); mcp::json resource_result = client.access_resource("/mock", {{"query", "Test query"}});
@ -273,13 +292,9 @@ TEST_F(ServerTest, ErrorHandlingTest) {
client.initialize("TestClient", mcp::MCP_VERSION); client.initialize("TestClient", mcp::MCP_VERSION);
// Call tool that throws exception // Call tool that throws exception
try { mcp::json result = client.call_tool("error_tool");
client.call_tool("error_tool"); EXPECT_EQ(result.contains("isError"), true);
FAIL() << "Should throw an exception"; EXPECT_EQ(result["isError"], true);
} catch (const mcp::mcp_exception& e) {
// Verify error code
EXPECT_EQ(e.code(), mcp::error_code::internal_error);
}
} }
// Test server stop and restart // Test server stop and restart