719 lines
21 KiB
C++
719 lines
21 KiB
C++
/**
|
||
* @file test_mcp_direct_requests.cpp
|
||
* @brief Test direct POST requests to MCP server
|
||
*
|
||
* This file contains tests for direct HTTP requests to the MCP server,
|
||
* testing various request types and ID formats.
|
||
* Based on specification 2024-11-05.
|
||
*/
|
||
|
||
#include "mcp_server.h"
|
||
#include "mcp_client.h"
|
||
#include "mcp_tool.h"
|
||
#include <gtest/gtest.h>
|
||
#include <httplib.h>
|
||
#include <thread>
|
||
#include <chrono>
|
||
#include <filesystem>
|
||
|
||
// Mock tool handler function
|
||
static mcp::json test_tool_handler(const mcp::json& params) {
|
||
if (params.contains("input")) {
|
||
return {
|
||
{
|
||
{"type", "text"},
|
||
{"text", "Result: " + params["input"].get<std::string>()}
|
||
}
|
||
};
|
||
} else {
|
||
return {
|
||
{
|
||
{"type", "text"},
|
||
{"text", "Default result"}
|
||
}
|
||
};
|
||
}
|
||
}
|
||
|
||
// Test fixture for setting up and cleaning up the test environment
|
||
class DirectRequestTest : public ::testing::Test {
|
||
protected:
|
||
void SetUp() override {
|
||
// Create and configure server
|
||
server = std::make_unique<mcp::server>("localhost", 8096);
|
||
server->set_server_info("TestServer", mcp::MCP_VERSION);
|
||
|
||
// Set server capabilities
|
||
mcp::json capabilities = {
|
||
{"tools", {{"listChanged", true}}}
|
||
};
|
||
server->set_capabilities(capabilities);
|
||
|
||
|
||
// Register tools
|
||
mcp::tool test_tool = mcp::tool_builder("test_tool")
|
||
.with_description("Test Tool")
|
||
.with_string_param("input", "Input parameter")
|
||
.build();
|
||
|
||
server->register_tool(test_tool, test_tool_handler);
|
||
|
||
// Start server
|
||
server_thread = std::thread([this]() {
|
||
server->start(true);
|
||
});
|
||
|
||
// Wait for server to start
|
||
std::this_thread::sleep_for(std::chrono::milliseconds(100));
|
||
|
||
// Create HTTP client
|
||
http_client = std::make_unique<httplib::Client>("localhost", 8096);
|
||
http_client->set_connection_timeout(5, 0);
|
||
http_client->set_read_timeout(5, 0);
|
||
http_client->set_write_timeout(5, 0);
|
||
}
|
||
|
||
void TearDown() override {
|
||
// Stop server
|
||
server->stop();
|
||
|
||
// Wait for server thread to end
|
||
if (server_thread.joinable()) {
|
||
server_thread.join();
|
||
}
|
||
}
|
||
|
||
// Send JSON-RPC request and return response
|
||
mcp::json send_jsonrpc_request(const mcp::json& request) {
|
||
httplib::Headers headers = {
|
||
{"Content-Type", "application/json"}
|
||
};
|
||
|
||
auto res = http_client->Post("/jsonrpc", headers, request.dump(), "application/json");
|
||
|
||
EXPECT_TRUE(res != nullptr);
|
||
if (!res) {
|
||
return mcp::json::object();
|
||
}
|
||
|
||
// 检查状态码,202表示请求已接受,但响应将通过SSE发送
|
||
if (res->status == 202) {
|
||
// 在实际测试中,我们需要等待SSE响应
|
||
// 但在这个测试中,我们只是返回一个空对象
|
||
// 实际应用中应该使用客户端类来处理这种情况
|
||
std::cout << "收到202 Accepted响应,实际响应将通过SSE发送" << std::endl;
|
||
return mcp::json::object();
|
||
}
|
||
|
||
EXPECT_EQ(res->status, 200);
|
||
|
||
try {
|
||
return mcp::json::parse(res->body);
|
||
} catch (const mcp::json::exception& e) {
|
||
ADD_FAILURE() << "Failed to parse response: " << e.what();
|
||
return mcp::json::object();
|
||
}
|
||
}
|
||
|
||
void mock_initialize() {
|
||
// Mock initialization request
|
||
mcp::json init_request = {
|
||
{"jsonrpc", "2.0"},
|
||
{"id", "init-1"},
|
||
{"method", "initialize"},
|
||
{"params", {
|
||
{"protocolVersion", mcp::MCP_VERSION},
|
||
{"capabilities", {}},
|
||
{"clientInfo", {
|
||
{"name", "TestClient"},
|
||
{"version", "1.0.0"}
|
||
}}
|
||
}}
|
||
};
|
||
|
||
send_jsonrpc_request(init_request);
|
||
|
||
send_jsonrpc_request({
|
||
{"jsonrpc", "2.0"},
|
||
{"method", "notifications/initialized"}
|
||
});
|
||
}
|
||
|
||
std::unique_ptr<mcp::server> server;
|
||
std::unique_ptr<httplib::Client> http_client;
|
||
std::thread server_thread;
|
||
};
|
||
|
||
// Test if server can handle requests w/ and w/o initialization
|
||
TEST_F(DirectRequestTest, InitializationTest) {
|
||
// Send request without initialization
|
||
mcp::json request = {
|
||
{"jsonrpc", "2.0"},
|
||
{"id", "no-init-1"},
|
||
{"method", "ping"},
|
||
{"params", {}}
|
||
};
|
||
|
||
mcp::json response = send_jsonrpc_request(request);
|
||
|
||
// Only ping and logging methods should be supported without initialization
|
||
EXPECT_EQ(response["jsonrpc"], "2.0");
|
||
EXPECT_EQ(response["id"], "no-init-1");
|
||
EXPECT_TRUE(response.contains("result"));
|
||
|
||
request = {
|
||
{"jsonrpc", "2.0"},
|
||
{"id", "no-init-2"},
|
||
{"method", "tools/call"},
|
||
{"params", {
|
||
{"name", "test_tool"},
|
||
{"arguments", {
|
||
{"input", "Test input"}
|
||
}}
|
||
}}
|
||
};
|
||
|
||
response = send_jsonrpc_request(request);
|
||
|
||
// Should return error
|
||
EXPECT_EQ(response["jsonrpc"], "2.0");
|
||
EXPECT_EQ(response["id"], "no-init-2");
|
||
EXPECT_FALSE(response.contains("result"));
|
||
EXPECT_TRUE(response.contains("error"));
|
||
EXPECT_EQ(response["error"]["code"], static_cast<int>(mcp::error_code::invalid_request));
|
||
|
||
// Mock initialization request
|
||
mcp::json init_request = {
|
||
{"jsonrpc", "2.0"},
|
||
{"id", "init-1"},
|
||
{"method", "initialize"},
|
||
{"params", {
|
||
{"protocolVersion", mcp::MCP_VERSION},
|
||
{"capabilities", {}},
|
||
{"clientInfo", {
|
||
{"name", "TestClient"},
|
||
{"version", "1.0.0"}
|
||
}}
|
||
}}
|
||
};
|
||
|
||
response = send_jsonrpc_request(init_request);
|
||
|
||
EXPECT_EQ(response["jsonrpc"], "2.0");
|
||
EXPECT_EQ(response["id"], "init-1");
|
||
EXPECT_TRUE(response.contains("result"));
|
||
EXPECT_FALSE(response.contains("error"));
|
||
|
||
response = send_jsonrpc_request({
|
||
{"jsonrpc", "2.0"},
|
||
{"method", "notifications/initialized"}
|
||
});
|
||
|
||
EXPECT_TRUE(response.empty());
|
||
|
||
// Now all methods should be supported
|
||
request = {
|
||
{"jsonrpc", "2.0"},
|
||
{"id", "init-2"},
|
||
{"method", "ping"},
|
||
{"params", {}}
|
||
};
|
||
|
||
response = send_jsonrpc_request(request);
|
||
|
||
// Should return result
|
||
EXPECT_EQ(response["jsonrpc"], "2.0");
|
||
EXPECT_EQ(response["id"], "init-2");
|
||
EXPECT_TRUE(response.contains("result"));
|
||
|
||
request = {
|
||
{"jsonrpc", "2.0"},
|
||
{"id", "init-3"},
|
||
{"method", "tools/call"},
|
||
{"params", {
|
||
{"name", "test_tool"},
|
||
{"arguments", {
|
||
{"input", "Test input"}
|
||
}}
|
||
}}
|
||
};
|
||
|
||
response = send_jsonrpc_request(request);
|
||
|
||
// Should return result
|
||
EXPECT_EQ(response["jsonrpc"], "2.0");
|
||
EXPECT_EQ(response["id"], "init-3");
|
||
EXPECT_TRUE(response.contains("result"));
|
||
EXPECT_FALSE(response.contains("error"));
|
||
EXPECT_TRUE(response["result"].contains("content"));
|
||
EXPECT_TRUE(response["result"]["content"][0]["text"].get<std::string>().find("Result: Test input") != std::string::npos);
|
||
}
|
||
|
||
// Test initialization with numeric ID
|
||
TEST_F(DirectRequestTest, InitializeWithNumericId) {
|
||
// Create initialization request with numeric ID
|
||
mcp::json request = {
|
||
{"jsonrpc", "2.0"},
|
||
{"id", 12345},
|
||
{"method", "initialize"},
|
||
{"params", {
|
||
{"protocolVersion", mcp::MCP_VERSION},
|
||
{"capabilities", {
|
||
{"tools", {{"listChanged", true}}}
|
||
}},
|
||
{"clientInfo", {
|
||
{"name", "TestClient"},
|
||
{"version", "1.0.0"}
|
||
}}
|
||
}}
|
||
};
|
||
|
||
// Send request
|
||
mcp::json response = send_jsonrpc_request(request);
|
||
|
||
// Verify response
|
||
EXPECT_EQ(response["jsonrpc"], "2.0");
|
||
EXPECT_EQ(response["id"], 12345);
|
||
EXPECT_TRUE(response.contains("result"));
|
||
EXPECT_FALSE(response.contains("error"));
|
||
|
||
// Verify result content
|
||
EXPECT_EQ(response["result"]["protocolVersion"], mcp::MCP_VERSION);
|
||
EXPECT_TRUE(response["result"].contains("capabilities"));
|
||
EXPECT_TRUE(response["result"].contains("serverInfo"));
|
||
EXPECT_EQ(response["result"]["serverInfo"]["name"], "TestServer");
|
||
}
|
||
|
||
// Test initialization with string ID
|
||
TEST_F(DirectRequestTest, InitializeWithStringId) {
|
||
// Create initialization request with string ID
|
||
mcp::json request = {
|
||
{"jsonrpc", "2.0"},
|
||
{"id", "request-id-abc123"},
|
||
{"method", "initialize"},
|
||
{"params", {
|
||
{"protocolVersion", mcp::MCP_VERSION},
|
||
{"capabilities", {
|
||
{"tools", {{"listChanged", true}}}
|
||
}},
|
||
{"clientInfo", {
|
||
{"name", "TestClient"},
|
||
{"version", "1.0.0"}
|
||
}}
|
||
}}
|
||
};
|
||
|
||
// Send request
|
||
mcp::json response = send_jsonrpc_request(request);
|
||
|
||
// Verify response
|
||
EXPECT_EQ(response["jsonrpc"], "2.0");
|
||
EXPECT_EQ(response["id"], "request-id-abc123");
|
||
EXPECT_TRUE(response.contains("result"));
|
||
EXPECT_FALSE(response.contains("error"));
|
||
|
||
// Verify result content
|
||
EXPECT_EQ(response["result"]["protocolVersion"], mcp::MCP_VERSION);
|
||
EXPECT_TRUE(response["result"].contains("capabilities"));
|
||
EXPECT_TRUE(response["result"].contains("serverInfo"));
|
||
EXPECT_EQ(response["result"]["serverInfo"]["name"], "TestServer");
|
||
}
|
||
|
||
// Test getting tools list
|
||
TEST_F(DirectRequestTest, GetTools) {
|
||
// Initialize first
|
||
mock_initialize();
|
||
|
||
// Get tools list
|
||
mcp::json request = {
|
||
{"jsonrpc", "2.0"},
|
||
{"id", 2},
|
||
{"method", "getTools"},
|
||
{"params", {}}
|
||
};
|
||
|
||
mcp::json response = send_jsonrpc_request(request);
|
||
|
||
// Verify response
|
||
EXPECT_EQ(response["jsonrpc"], "2.0");
|
||
EXPECT_EQ(response["id"], 2);
|
||
|
||
// Server may not implement getTools method, so check if error is returned
|
||
if (response.contains("error")) {
|
||
EXPECT_EQ(response["error"]["code"], static_cast<int>(mcp::error_code::method_not_found));
|
||
} else {
|
||
EXPECT_TRUE(response.contains("result"));
|
||
EXPECT_FALSE(response.contains("error"));
|
||
|
||
// Verify tools list
|
||
EXPECT_TRUE(response["result"].is_array());
|
||
EXPECT_GE(response["result"].size(), 1);
|
||
|
||
// Verify test tool
|
||
bool found_test_tool = false;
|
||
for (const auto& tool : response["result"]) {
|
||
if (tool["name"] == "test_tool") {
|
||
found_test_tool = true;
|
||
EXPECT_EQ(tool["description"], "Test Tool");
|
||
EXPECT_TRUE(tool.contains("parameters"));
|
||
break;
|
||
}
|
||
}
|
||
EXPECT_TRUE(found_test_tool);
|
||
}
|
||
}
|
||
|
||
// Test calling a tool
|
||
TEST_F(DirectRequestTest, CallTool) {
|
||
// Initialize first
|
||
mock_initialize();
|
||
|
||
// Call tool
|
||
mcp::json request = {
|
||
{"jsonrpc", "2.0"},
|
||
{"id", "call-1"},
|
||
{"method", "tools/call"},
|
||
{"params", {
|
||
{"name", "test_tool"},
|
||
{"arguments", {
|
||
{"input", "Test input"}
|
||
}}
|
||
}}
|
||
};
|
||
|
||
mcp::json response = send_jsonrpc_request(request);
|
||
|
||
// Verify response
|
||
EXPECT_EQ(response["jsonrpc"], "2.0");
|
||
EXPECT_EQ(response["id"], "call-1");
|
||
EXPECT_TRUE(response.contains("result"));
|
||
EXPECT_FALSE(response.contains("error"));
|
||
|
||
// Verify tool call result
|
||
EXPECT_TRUE(response["result"].contains("content"));
|
||
EXPECT_TRUE(response["result"]["content"][0]["text"].get<std::string>().find("Result: Test input") != std::string::npos);
|
||
}
|
||
|
||
// Test sending notification (no ID)
|
||
TEST_F(DirectRequestTest, SendNotification) {
|
||
// Initialize first
|
||
mcp::json init_request = {
|
||
{"jsonrpc", "2.0"},
|
||
{"id", 200},
|
||
{"method", "initialize"},
|
||
{"params", {
|
||
{"protocolVersion", mcp::MCP_VERSION},
|
||
{"capabilities", {}},
|
||
{"clientInfo", {
|
||
{"name", "TestClient"},
|
||
{"version", "1.0.0"}
|
||
}}
|
||
}}
|
||
};
|
||
|
||
send_jsonrpc_request(init_request);
|
||
|
||
// Send notification
|
||
mcp::json notification = {
|
||
{"jsonrpc", "2.0"},
|
||
{"method", "initialized"},
|
||
{"params", {}}
|
||
};
|
||
|
||
httplib::Headers headers = {
|
||
{"Content-Type", "application/json"}
|
||
};
|
||
|
||
auto res = http_client->Post("/jsonrpc", headers, notification.dump(), "application/json");
|
||
|
||
// Verify response (notifications may have empty response or error response)
|
||
EXPECT_TRUE(res != nullptr);
|
||
// 状态码可能是200或202,取决于服务器实现
|
||
EXPECT_TRUE(res->status == 200 || res->status == 202);
|
||
}
|
||
|
||
// Test error handling - method not found
|
||
TEST_F(DirectRequestTest, MethodNotFound) {
|
||
mock_initialize();
|
||
|
||
mcp::json request = {
|
||
{"jsonrpc", "2.0"},
|
||
{"id", 999},
|
||
{"method", "nonExistentMethod"},
|
||
{"params", {}}
|
||
};
|
||
|
||
mcp::json response = send_jsonrpc_request(request);
|
||
|
||
// Verify error response
|
||
EXPECT_EQ(response["jsonrpc"], "2.0");
|
||
EXPECT_EQ(response["id"], 999);
|
||
EXPECT_FALSE(response.contains("result"));
|
||
EXPECT_TRUE(response.contains("error"));
|
||
EXPECT_EQ(response["error"]["code"], static_cast<int>(mcp::error_code::method_not_found));
|
||
}
|
||
|
||
// Test error handling - invalid parameters
|
||
TEST_F(DirectRequestTest, InvalidParams) {
|
||
// Initialize first
|
||
mock_initialize();
|
||
|
||
// Call tool but missing required parameters
|
||
mcp::json request = {
|
||
{"jsonrpc", "2.0"},
|
||
{"id", "invalid-params"},
|
||
{"method", "callTool"},
|
||
{"params", {
|
||
// Missing tool name
|
||
{"parameters", {
|
||
{"input", "Test input"}
|
||
}}
|
||
}}
|
||
};
|
||
|
||
mcp::json response = send_jsonrpc_request(request);
|
||
|
||
// Verify error response
|
||
EXPECT_EQ(response["jsonrpc"], "2.0");
|
||
EXPECT_EQ(response["id"], "invalid-params");
|
||
EXPECT_FALSE(response.contains("result"));
|
||
EXPECT_TRUE(response.contains("error"));
|
||
|
||
// Server may return method_not_found or invalid_params error
|
||
int error_code = response["error"]["code"];
|
||
EXPECT_TRUE(error_code == static_cast<int>(mcp::error_code::method_not_found) ||
|
||
error_code == static_cast<int>(mcp::error_code::invalid_params));
|
||
}
|
||
|
||
// Test logging functionality
|
||
TEST_F(DirectRequestTest, LoggingTest) {
|
||
// Initialize first
|
||
mock_initialize();
|
||
|
||
// Send log request
|
||
mcp::json request = {
|
||
{"jsonrpc", "2.0"},
|
||
{"id", "log-1"},
|
||
{"method", "log"},
|
||
{"params", {
|
||
{"level", "info"},
|
||
{"message", "This is a test log message"},
|
||
{"details", {
|
||
{"source", "test_case"},
|
||
{"timestamp", "2023-01-01T12:00:00Z"}
|
||
}}
|
||
}}
|
||
};
|
||
|
||
mcp::json response = send_jsonrpc_request(request);
|
||
|
||
// Verify response
|
||
EXPECT_EQ(response["jsonrpc"], "2.0");
|
||
EXPECT_EQ(response["id"], "log-1");
|
||
|
||
// Server may not implement log method
|
||
if (response.contains("error")) {
|
||
EXPECT_EQ(response["error"]["code"], static_cast<int>(mcp::error_code::method_not_found));
|
||
} else {
|
||
EXPECT_TRUE(response.contains("result"));
|
||
EXPECT_FALSE(response.contains("error"));
|
||
}
|
||
}
|
||
|
||
// Test sampling functionality
|
||
TEST_F(DirectRequestTest, SamplingTest) {
|
||
// Initialize first
|
||
mock_initialize();
|
||
|
||
// Send sampling request
|
||
mcp::json request = {
|
||
{"jsonrpc", "2.0"},
|
||
{"id", 301},
|
||
{"method", "sample"},
|
||
{"params", {
|
||
{"prompt", "This is a test prompt"},
|
||
{"parameters", {
|
||
{"temperature", 0.7},
|
||
{"max_tokens", 100}
|
||
}}
|
||
}}
|
||
};
|
||
|
||
mcp::json response = send_jsonrpc_request(request);
|
||
|
||
// Verify response (may fail, as server may not implement sampling functionality)
|
||
if (response.contains("error")) {
|
||
EXPECT_EQ(response["error"]["code"], static_cast<int>(mcp::error_code::method_not_found));
|
||
} else {
|
||
EXPECT_TRUE(response.contains("result"));
|
||
}
|
||
}
|
||
|
||
// Test batch request processing
|
||
TEST_F(DirectRequestTest, BatchRequestTest) {
|
||
// Create batch request
|
||
mcp::json batch_request = mcp::json::array();
|
||
|
||
// Add initialization request
|
||
batch_request.push_back({
|
||
{"jsonrpc", "2.0"},
|
||
{"id", "batch-1"},
|
||
{"method", "initialize"},
|
||
{"params", {
|
||
{"protocolVersion", mcp::MCP_VERSION},
|
||
{"capabilities", {}},
|
||
{"clientInfo", {
|
||
{"name", "TestClient"},
|
||
{"version", "1.0.0"}
|
||
}}
|
||
}}
|
||
});
|
||
|
||
// Add ping request
|
||
batch_request.push_back({
|
||
{"jsonrpc", "2.0"},
|
||
{"id", "batch-2"},
|
||
{"method", "ping"},
|
||
{"params", {}}
|
||
});
|
||
|
||
// Add notification
|
||
batch_request.push_back({
|
||
{"jsonrpc", "2.0"},
|
||
{"method", "initialized"},
|
||
{"params", {}}
|
||
});
|
||
|
||
// Send batch request
|
||
httplib::Headers headers = {
|
||
{"Content-Type", "application/json"}
|
||
};
|
||
|
||
auto res = http_client->Post("/jsonrpc", headers, batch_request.dump(), "application/json");
|
||
|
||
// Verify response
|
||
EXPECT_TRUE(res != nullptr);
|
||
EXPECT_EQ(res->status, 200);
|
||
|
||
// Parse response
|
||
try {
|
||
mcp::json response = mcp::json::parse(res->body);
|
||
|
||
// Batch requests may not be supported, so check if error is returned
|
||
if (response.is_object() && response.contains("error")) {
|
||
EXPECT_EQ(response["error"]["code"], static_cast<int>(mcp::error_code::invalid_request));
|
||
EXPECT_TRUE(response["error"]["message"].get<std::string>().find("Batch") != std::string::npos);
|
||
} else if (response.is_array()) {
|
||
// If batch requests are supported, should return an array
|
||
EXPECT_GE(response.size(), 2); // At least two responses (not including notification)
|
||
|
||
// Check each response
|
||
for (const auto& resp : response) {
|
||
EXPECT_EQ(resp["jsonrpc"], "2.0");
|
||
EXPECT_TRUE(resp.contains("id"));
|
||
|
||
if (resp["id"] == "batch-1") {
|
||
EXPECT_TRUE(resp.contains("result"));
|
||
EXPECT_TRUE(resp["result"].contains("protocolVersion"));
|
||
} else if (resp["id"] == "batch-2") {
|
||
EXPECT_TRUE(resp.contains("result"));
|
||
EXPECT_TRUE(resp["result"].contains("pong"));
|
||
}
|
||
}
|
||
}
|
||
} catch (const mcp::json::exception& e) {
|
||
ADD_FAILURE() << "Failed to parse batch response: " << e.what();
|
||
}
|
||
}
|
||
|
||
// Test request cancellation
|
||
TEST_F(DirectRequestTest, CancelRequestTest) {
|
||
// Initialize first
|
||
mock_initialize();
|
||
|
||
// Send cancel request
|
||
mcp::json request = {
|
||
{"jsonrpc", "2.0"},
|
||
{"id", "cancel-1"},
|
||
{"method", "$/cancelRequest"},
|
||
{"params", {
|
||
{"id", "some-request-id"}
|
||
}}
|
||
};
|
||
|
||
mcp::json response = send_jsonrpc_request(request);
|
||
|
||
// Verify response (may fail, as server may not implement cancellation functionality)
|
||
if (response.contains("error")) {
|
||
EXPECT_EQ(response["error"]["code"], static_cast<int>(mcp::error_code::method_not_found));
|
||
} else {
|
||
EXPECT_TRUE(response.contains("result"));
|
||
}
|
||
}
|
||
|
||
// Test using different types of IDs
|
||
TEST_F(DirectRequestTest, DifferentIdTypesTest) {
|
||
// Use integer ID
|
||
mcp::json int_request = {
|
||
{"jsonrpc", "2.0"},
|
||
{"id", 42},
|
||
{"method", "ping"},
|
||
{"params", {}}
|
||
};
|
||
|
||
mcp::json int_response = send_jsonrpc_request(int_request);
|
||
EXPECT_EQ(int_response["id"], 42);
|
||
|
||
// Use string ID
|
||
mcp::json string_request = {
|
||
{"jsonrpc", "2.0"},
|
||
{"id", "string-id-42"},
|
||
{"method", "ping"},
|
||
{"params", {}}
|
||
};
|
||
|
||
mcp::json string_response = send_jsonrpc_request(string_request);
|
||
EXPECT_EQ(string_response["id"], "string-id-42");
|
||
|
||
// Use float ID (should also work)
|
||
mcp::json float_request = {
|
||
{"jsonrpc", "2.0"},
|
||
{"id", 3.14},
|
||
{"method", "ping"},
|
||
{"params", {}}
|
||
};
|
||
|
||
mcp::json float_response = send_jsonrpc_request(float_request);
|
||
EXPECT_EQ(float_response["id"], 3.14);
|
||
|
||
// Use null ID (this should be treated as a notification)
|
||
mcp::json null_request = {
|
||
{"jsonrpc", "2.0"},
|
||
{"id", nullptr},
|
||
{"method", "ping"},
|
||
{"params", {}}
|
||
};
|
||
|
||
httplib::Headers headers = {
|
||
{"Content-Type", "application/json"}
|
||
};
|
||
|
||
auto res = http_client->Post("/jsonrpc", headers, null_request.dump(), "application/json");
|
||
EXPECT_TRUE(res != nullptr);
|
||
EXPECT_EQ(res->status, 200);
|
||
|
||
// Use complex object as ID (this may not be supported)
|
||
mcp::json complex_request = {
|
||
{"jsonrpc", "2.0"},
|
||
{"id", {{"complex", "id"}}},
|
||
{"method", "ping"},
|
||
{"params", {}}
|
||
};
|
||
|
||
mcp::json complex_response = send_jsonrpc_request(complex_request);
|
||
|
||
// Verify response (may fail, as server may not support complex objects as ID)
|
||
if (complex_response.contains("error")) {
|
||
EXPECT_EQ(complex_response["error"]["code"], static_cast<int>(mcp::error_code::invalid_request));
|
||
}
|
||
}
|