fix bugs of tool calls
parent
ebf5c2ea1f
commit
b62d328780
|
@ -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
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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();
|
||||||
|
|
|
@ -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_EQ(result["message"], "Test 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"], "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");
|
||||||
}
|
}
|
|
@ -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;
|
||||||
|
|
|
@ -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
|
||||||
|
|
Loading…
Reference in New Issue