refactor client API
parent
84e2e092f0
commit
c8054f8090
80
README.md
80
README.md
|
@ -12,48 +12,30 @@
|
||||||
|
|
||||||
## 组件
|
## 组件
|
||||||
|
|
||||||
### 核心协议 (`mcp_message.h`, `mcp_message.cpp`)
|
MCP C++库包含以下主要组件:
|
||||||
|
|
||||||
定义MCP的基本结构和类型:
|
### 核心组件
|
||||||
- 请求/响应处理
|
|
||||||
- 错误代码
|
|
||||||
- 工具定义
|
|
||||||
|
|
||||||
### HTTP服务器 (`mcp_server.h`, `mcp_server.cpp`)
|
#### 客户端接口 (`mcp_client.h`)
|
||||||
|
定义了MCP客户端的抽象接口,所有具体的客户端实现都继承自这个接口。
|
||||||
|
|
||||||
实现一个HTTP服务器,暴露MCP资源和工具:
|
#### SSE客户端 (`mcp_sse_client.h`, `mcp_sse_client.cpp`)
|
||||||
- 在特定路径注册资源
|
使用HTTP和Server-Sent Events (SSE)与MCP服务器通信的客户端实现。
|
||||||
- 注册带有处理程序的工具
|
|
||||||
- 处理传入的HTTP请求
|
|
||||||
- 将请求路由到适当的资源
|
|
||||||
|
|
||||||
### 客户端
|
|
||||||
|
|
||||||
#### HTTP客户端 (`mcp_client.h`, `mcp_client.cpp`)
|
|
||||||
|
|
||||||
实现连接到HTTP MCP服务器的HTTP客户端,符合SSE通信规范。
|
|
||||||
|
|
||||||
可使用[MCP Inspector](https://github.com/modelcontextprotocol/inspector)连接并测试:
|
|
||||||

|
|
||||||
|
|
||||||
#### Stdio客户端 (`mcp_stdio_client.h`, `mcp_stdio_client.cpp`)
|
#### Stdio客户端 (`mcp_stdio_client.h`, `mcp_stdio_client.cpp`)
|
||||||
|
使用标准输入/输出与MCP服务器通信的客户端实现,可以启动子进程并与之通信。
|
||||||
|
|
||||||
通过标准输入/输出与MCP服务器通信的客户端:
|
#### 消息处理 (`mcp_message.h`, `mcp_message.cpp`)
|
||||||
- 启动本地服务器进程
|
处理JSON-RPC消息的序列化和反序列化。
|
||||||
- 通过管道进行通信
|
|
||||||
- 支持资源访问和工具调用
|
|
||||||
- 适合与本地进程集成
|
|
||||||
|
|
||||||
### 资源 (`mcp_resource.h`, `mcp_resource.cpp`)
|
#### 工具管理 (`mcp_tool.h`, `mcp_tool.cpp`)
|
||||||
|
管理和调用MCP工具。
|
||||||
|
|
||||||
提供常见资源类型的基本实现:
|
#### 资源管理 (`mcp_resource.h`, `mcp_resource.cpp`)
|
||||||
- 文件资源
|
管理MCP资源。
|
||||||
- API资源
|
|
||||||
|
|
||||||
### 工具 (`mcp_tool.h`, `mcp_tool.cpp`)
|
#### 服务器 (`mcp_server.h`, `mcp_server.cpp`)
|
||||||
|
实现MCP服务器功能。
|
||||||
提供工具相关定义与功能:
|
|
||||||
- 工具构建器
|
|
||||||
|
|
||||||
## 示例
|
## 示例
|
||||||
|
|
||||||
|
@ -119,7 +101,7 @@ server.start(true); // 阻塞模式
|
||||||
|
|
||||||
```cpp
|
```cpp
|
||||||
// 连接到服务器
|
// 连接到服务器
|
||||||
mcp::client client("localhost", 8080);
|
mcp::sse_client client("localhost", 8080);
|
||||||
|
|
||||||
// 初始化连接
|
// 初始化连接
|
||||||
client.initialize("My Client", "1.0.0");
|
client.initialize("My Client", "1.0.0");
|
||||||
|
@ -132,6 +114,36 @@ mcp::json params = {
|
||||||
mcp::json result = client.call_tool("hello", params);
|
mcp::json result = client.call_tool("hello", params);
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### 使用SSE客户端
|
||||||
|
|
||||||
|
SSE客户端使用HTTP和Server-Sent Events (SSE) 与MCP服务器通信。这是一种基于Web标准的通信方式,适合与支持HTTP/SSE的服务器通信。
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
#include "mcp_sse_client.h"
|
||||||
|
|
||||||
|
// 创建客户端,指定服务器地址和端口
|
||||||
|
mcp::sse_client client("localhost", 8080);
|
||||||
|
// 或者使用基础URL
|
||||||
|
// mcp::sse_client client("http://localhost:8080");
|
||||||
|
|
||||||
|
// 设置认证令牌(如果需要)
|
||||||
|
client.set_auth_token("your_auth_token");
|
||||||
|
|
||||||
|
// 设置自定义请求头(如果需要)
|
||||||
|
client.set_header("X-Custom-Header", "value");
|
||||||
|
|
||||||
|
// 初始化客户端
|
||||||
|
if (!client.initialize("My Client", "1.0.0")) {
|
||||||
|
// 初始化失败处理
|
||||||
|
}
|
||||||
|
|
||||||
|
// 调用工具
|
||||||
|
json result = client.call_tool("tool_name", {
|
||||||
|
{"param1", "value1"},
|
||||||
|
{"param2", 42}
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
### 使用Stdio客户端
|
### 使用Stdio客户端
|
||||||
|
|
||||||
Stdio客户端可以与任何支持stdio传输的MCP服务器进行通信,例如:
|
Stdio客户端可以与任何支持stdio传输的MCP服务器进行通信,例如:
|
||||||
|
|
|
@ -2,8 +2,8 @@ cmake_minimum_required(VERSION 3.10)
|
||||||
|
|
||||||
include_directories(${CMAKE_CURRENT_SOURCE_DIR})
|
include_directories(${CMAKE_CURRENT_SOURCE_DIR})
|
||||||
|
|
||||||
set(TARGET client_example)
|
set(TARGET sse_client_example)
|
||||||
add_executable(${TARGET} client_example.cpp)
|
add_executable(${TARGET} sse_client_example.cpp)
|
||||||
install(TARGETS ${TARGET} RUNTIME)
|
install(TARGETS ${TARGET} RUNTIME)
|
||||||
target_link_libraries(${TARGET} PRIVATE mcp)
|
target_link_libraries(${TARGET} PRIVATE mcp)
|
||||||
if(OPENSSL_FOUND)
|
if(OPENSSL_FOUND)
|
||||||
|
@ -11,6 +11,10 @@ if(OPENSSL_FOUND)
|
||||||
endif()
|
endif()
|
||||||
target_compile_features(${TARGET} PRIVATE cxx_std_17)
|
target_compile_features(${TARGET} PRIVATE cxx_std_17)
|
||||||
|
|
||||||
|
add_executable(stdio_client_example stdio_client_example.cpp)
|
||||||
|
target_link_libraries(stdio_client_example PRIVATE mcp)
|
||||||
|
target_include_directories(stdio_client_example PRIVATE ${CMAKE_SOURCE_DIR}/include)
|
||||||
|
|
||||||
set(TARGET server_example)
|
set(TARGET server_example)
|
||||||
add_executable(${TARGET} server_example.cpp)
|
add_executable(${TARGET} server_example.cpp)
|
||||||
install(TARGETS ${TARGET} RUNTIME)
|
install(TARGETS ${TARGET} RUNTIME)
|
||||||
|
@ -19,14 +23,3 @@ if(OPENSSL_FOUND)
|
||||||
target_link_libraries(${TARGET} PRIVATE ${OPENSSL_LIBRARIES})
|
target_link_libraries(${TARGET} PRIVATE ${OPENSSL_LIBRARIES})
|
||||||
endif()
|
endif()
|
||||||
target_compile_features(${TARGET} PRIVATE cxx_std_17)
|
target_compile_features(${TARGET} PRIVATE cxx_std_17)
|
||||||
|
|
||||||
# Create a directory for files if it doesn't exist
|
|
||||||
file(MAKE_DIRECTORY ${CMAKE_BINARY_DIR}/files)
|
|
||||||
|
|
||||||
# Copy example files if needed
|
|
||||||
# file(COPY ${CMAKE_CURRENT_SOURCE_DIR}/example_files/ DESTINATION ${CMAKE_BINARY_DIR}/files)
|
|
||||||
|
|
||||||
# 添加stdio客户端示例
|
|
||||||
add_executable(stdio_client_example stdio_client_example.cpp)
|
|
||||||
target_link_libraries(stdio_client_example PRIVATE mcp)
|
|
||||||
target_include_directories(stdio_client_example PRIVATE ${CMAKE_SOURCE_DIR}/include)
|
|
|
@ -6,13 +6,13 @@
|
||||||
* Follows the 2024-11-05 basic protocol specification.
|
* Follows the 2024-11-05 basic protocol specification.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "mcp_client.h"
|
#include "mcp_sse_client.h"
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
int main() {
|
int main() {
|
||||||
// Create a client
|
// Create a client
|
||||||
mcp::client client("localhost", 8888);
|
mcp::sse_client client("localhost", 8888);
|
||||||
|
|
||||||
// Set capabilites
|
// Set capabilites
|
||||||
mcp::json capabilities = {
|
mcp::json capabilities = {
|
|
@ -1,8 +1,8 @@
|
||||||
/**
|
/**
|
||||||
* @file mcp_client.h
|
* @file mcp_client.h
|
||||||
* @brief MCP Client implementation
|
* @brief MCP Client interface
|
||||||
*
|
*
|
||||||
* This file implements the client-side functionality for the Model Context Protocol.
|
* This file defines the interface for the Model Context Protocol clients.
|
||||||
* Follows the 2024-11-05 protocol specification.
|
* Follows the 2024-11-05 protocol specification.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
@ -13,48 +13,25 @@
|
||||||
#include "mcp_tool.h"
|
#include "mcp_tool.h"
|
||||||
#include "mcp_logger.h"
|
#include "mcp_logger.h"
|
||||||
|
|
||||||
// Include the HTTP library
|
|
||||||
#include "httplib.h"
|
|
||||||
|
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <map>
|
|
||||||
#include <vector>
|
#include <vector>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <mutex>
|
|
||||||
#include <functional>
|
|
||||||
#include <atomic>
|
|
||||||
#include <condition_variable>
|
|
||||||
#include <future>
|
|
||||||
|
|
||||||
namespace mcp {
|
namespace mcp {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @class client
|
* @class client
|
||||||
* @brief Client for connecting to MCP servers
|
* @brief Abstract interface for MCP clients
|
||||||
*
|
*
|
||||||
* The client class provides functionality to connect to MCP servers,
|
* The client class defines the interface for all MCP client implementations,
|
||||||
* initialize the connection, and send/receive JSON-RPC messages.
|
* regardless of the transport mechanism used (HTTP/SSE, stdio, etc.).
|
||||||
*/
|
*/
|
||||||
class client {
|
class client {
|
||||||
public:
|
public:
|
||||||
/**
|
/**
|
||||||
* @brief Constructor
|
* @brief Virtual destructor
|
||||||
* @param host The server host (e.g., "localhost", "example.com")
|
|
||||||
* @param port The server port
|
|
||||||
* @param sse_endpoint The endpoint for server-sent events
|
|
||||||
*/
|
*/
|
||||||
client(const std::string& host, int port = 8080, const std::string& sse_endpoint = "/sse");
|
virtual ~client() = default;
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Constructor
|
|
||||||
* @param base_url The base URL of the server (e.g., "localhost:8080")
|
|
||||||
*/
|
|
||||||
client(const std::string& base_url, const std::string& sse_endpoint = "/sse");
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Destructor
|
|
||||||
*/
|
|
||||||
~client();
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Initialize the connection with the server
|
* @brief Initialize the connection with the server
|
||||||
|
@ -62,38 +39,19 @@ public:
|
||||||
* @param client_version The version of the client
|
* @param client_version The version of the client
|
||||||
* @return True if initialization was successful
|
* @return True if initialization was successful
|
||||||
*/
|
*/
|
||||||
bool initialize(const std::string& client_name, const std::string& client_version);
|
virtual bool initialize(const std::string& client_name, const std::string& client_version) = 0;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Ping request
|
* @brief Ping request
|
||||||
* @return True if the server is alive
|
* @return True if the server is alive
|
||||||
*/
|
*/
|
||||||
bool ping();
|
virtual bool ping() = 0;
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Set authentication token
|
|
||||||
* @param token The authentication token
|
|
||||||
*/
|
|
||||||
void set_auth_token(const std::string& token);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Set a request header that will be sent with all requests
|
|
||||||
* @param key Header name
|
|
||||||
* @param value Header value
|
|
||||||
*/
|
|
||||||
void set_header(const std::string& key, const std::string& value);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Set timeout for requests
|
|
||||||
* @param timeout_seconds Timeout in seconds
|
|
||||||
*/
|
|
||||||
void set_timeout(int timeout_seconds);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Set client capabilities
|
* @brief Set client capabilities
|
||||||
* @param capabilities The capabilities of the client
|
* @param capabilities The capabilities of the client
|
||||||
*/
|
*/
|
||||||
void set_capabilities(const json& capabilities);
|
virtual void set_capabilities(const json& capabilities) = 0;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Send a request and wait for a response
|
* @brief Send a request and wait for a response
|
||||||
|
@ -102,7 +60,7 @@ public:
|
||||||
* @return The response
|
* @return The response
|
||||||
* @throws mcp_exception on error
|
* @throws mcp_exception on error
|
||||||
*/
|
*/
|
||||||
response send_request(const std::string& method, const json& params = json::object());
|
virtual response send_request(const std::string& method, const json& params = json::object()) = 0;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Send a notification (no response expected)
|
* @brief Send a notification (no response expected)
|
||||||
|
@ -110,14 +68,14 @@ public:
|
||||||
* @param params The parameters to pass
|
* @param params The parameters to pass
|
||||||
* @throws mcp_exception on error
|
* @throws mcp_exception on error
|
||||||
*/
|
*/
|
||||||
void send_notification(const std::string& method, const json& params = json::object());
|
virtual void send_notification(const std::string& method, const json& params = json::object()) = 0;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Get server capabilities
|
* @brief Get server capabilities
|
||||||
* @return The server capabilities
|
* @return The server capabilities
|
||||||
* @throws mcp_exception on error
|
* @throws mcp_exception on error
|
||||||
*/
|
*/
|
||||||
json get_server_capabilities();
|
virtual json get_server_capabilities() = 0;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Call a tool
|
* @brief Call a tool
|
||||||
|
@ -126,125 +84,53 @@ public:
|
||||||
* @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& arguments = json::object());
|
virtual json call_tool(const std::string& tool_name, const json& arguments = json::object()) = 0;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Get available tools
|
* @brief Get available tools
|
||||||
* @return List of available tools
|
* @return List of available tools
|
||||||
* @throws mcp_exception on error
|
* @throws mcp_exception on error
|
||||||
*/
|
*/
|
||||||
std::vector<tool> get_tools();
|
virtual std::vector<tool> get_tools() = 0;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Get client capabilities
|
* @brief Get client capabilities
|
||||||
* @return The client capabilities
|
* @return The client capabilities
|
||||||
*/
|
*/
|
||||||
json get_capabilities();
|
virtual json get_capabilities() = 0;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief List available resources
|
* @brief List available resources
|
||||||
* @param cursor Optional cursor for pagination
|
* @param cursor Optional cursor for pagination
|
||||||
* @return List of resources
|
* @return List of resources
|
||||||
*/
|
*/
|
||||||
json list_resources(const std::string& cursor = "");
|
virtual json list_resources(const std::string& cursor = "") = 0;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Read a resource
|
* @brief Read a resource
|
||||||
* @param resource_uri The URI of the resource
|
* @param resource_uri The URI of the resource
|
||||||
* @return The resource content
|
* @return The resource content
|
||||||
*/
|
*/
|
||||||
json read_resource(const std::string& resource_uri);
|
virtual json read_resource(const std::string& resource_uri) = 0;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Subscribe to resource changes
|
* @brief Subscribe to resource changes
|
||||||
* @param resource_uri The URI of the resource
|
* @param resource_uri The URI of the resource
|
||||||
* @return Subscription result
|
* @return Subscription result
|
||||||
*/
|
*/
|
||||||
json subscribe_to_resource(const std::string& resource_uri);
|
virtual json subscribe_to_resource(const std::string& resource_uri) = 0;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief List resource templates
|
* @brief List resource templates
|
||||||
* @return List of resource templates
|
* @return List of resource templates
|
||||||
*/
|
*/
|
||||||
json list_resource_templates();
|
virtual json list_resource_templates() = 0;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief 检查服务器是否可访问
|
* @brief Check if the client is running
|
||||||
* @return True if the server is accessible
|
* @return True if the client is running
|
||||||
*/
|
*/
|
||||||
bool check_server_accessible();
|
virtual bool is_running() const = 0;
|
||||||
|
|
||||||
private:
|
|
||||||
// 初始化HTTP客户端
|
|
||||||
void init_client(const std::string& host, int port);
|
|
||||||
void init_client(const std::string& base_url);
|
|
||||||
|
|
||||||
// 打开SSE连接
|
|
||||||
void open_sse_connection();
|
|
||||||
|
|
||||||
// 解析SSE数据
|
|
||||||
bool parse_sse_data(const char* data, size_t length);
|
|
||||||
|
|
||||||
// 关闭SSE连接
|
|
||||||
void close_sse_connection();
|
|
||||||
|
|
||||||
// 发送JSON-RPC请求
|
|
||||||
json send_jsonrpc(const request& req);
|
|
||||||
|
|
||||||
// 服务器主机和端口
|
|
||||||
std::string host_;
|
|
||||||
int port_ = 8080;
|
|
||||||
|
|
||||||
// 或者使用基础URL
|
|
||||||
std::string base_url_;
|
|
||||||
|
|
||||||
// SSE端点
|
|
||||||
std::string sse_endpoint_ = "/sse";
|
|
||||||
|
|
||||||
// 消息端点
|
|
||||||
std::string msg_endpoint_;
|
|
||||||
|
|
||||||
// HTTP客户端
|
|
||||||
std::unique_ptr<httplib::Client> http_client_;
|
|
||||||
|
|
||||||
// SSE专用HTTP客户端
|
|
||||||
std::unique_ptr<httplib::Client> sse_client_;
|
|
||||||
|
|
||||||
// SSE线程
|
|
||||||
std::unique_ptr<std::thread> sse_thread_;
|
|
||||||
|
|
||||||
// SSE运行状态
|
|
||||||
std::atomic<bool> sse_running_{false};
|
|
||||||
|
|
||||||
// 认证令牌
|
|
||||||
std::string auth_token_;
|
|
||||||
|
|
||||||
// 默认请求头
|
|
||||||
std::map<std::string, std::string> default_headers_;
|
|
||||||
|
|
||||||
// 超时设置(秒)
|
|
||||||
int timeout_seconds_ = 30;
|
|
||||||
|
|
||||||
// 客户端能力
|
|
||||||
json capabilities_;
|
|
||||||
|
|
||||||
// 服务器能力
|
|
||||||
json server_capabilities_;
|
|
||||||
|
|
||||||
// 互斥锁
|
|
||||||
mutable std::mutex mutex_;
|
|
||||||
|
|
||||||
// 条件变量,用于等待消息端点设置
|
|
||||||
std::condition_variable endpoint_cv_;
|
|
||||||
|
|
||||||
// 请求ID到Promise的映射,用于异步等待响应
|
|
||||||
std::map<json, std::promise<json>> pending_requests_;
|
|
||||||
|
|
||||||
// 响应处理互斥锁
|
|
||||||
std::mutex response_mutex_;
|
|
||||||
|
|
||||||
// 响应条件变量
|
|
||||||
std::condition_variable response_cv_;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace mcp
|
} // namespace mcp
|
||||||
|
|
|
@ -0,0 +1,259 @@
|
||||||
|
/**
|
||||||
|
* @file mcp_client.h
|
||||||
|
* @brief MCP Client implementation
|
||||||
|
*
|
||||||
|
* This file implements the client-side functionality for the Model Context Protocol.
|
||||||
|
* Follows the 2024-11-05 protocol specification.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef MCP_SSE_CLIENT_H
|
||||||
|
#define MCP_SSE_CLIENT_H
|
||||||
|
|
||||||
|
#include "mcp_client.h"
|
||||||
|
#include "mcp_message.h"
|
||||||
|
#include "mcp_tool.h"
|
||||||
|
#include "mcp_logger.h"
|
||||||
|
|
||||||
|
// Include the HTTP library
|
||||||
|
#include "httplib.h"
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
#include <map>
|
||||||
|
#include <vector>
|
||||||
|
#include <memory>
|
||||||
|
#include <mutex>
|
||||||
|
#include <functional>
|
||||||
|
#include <atomic>
|
||||||
|
#include <condition_variable>
|
||||||
|
#include <future>
|
||||||
|
|
||||||
|
namespace mcp {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @class client
|
||||||
|
* @brief Client for connecting to MCP servers
|
||||||
|
*
|
||||||
|
* The client class provides functionality to connect to MCP servers,
|
||||||
|
* initialize the connection, and send/receive JSON-RPC messages.
|
||||||
|
*/
|
||||||
|
class sse_client : public client {
|
||||||
|
public:
|
||||||
|
/**
|
||||||
|
* @brief Constructor
|
||||||
|
* @param host The server host (e.g., "localhost", "example.com")
|
||||||
|
* @param port The server port
|
||||||
|
* @param sse_endpoint The endpoint for server-sent events
|
||||||
|
*/
|
||||||
|
sse_client(const std::string& host, int port = 8080, const std::string& sse_endpoint = "/sse");
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Constructor
|
||||||
|
* @param base_url The base URL of the server (e.g., "localhost:8080")
|
||||||
|
*/
|
||||||
|
sse_client(const std::string& base_url, const std::string& sse_endpoint = "/sse");
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Destructor
|
||||||
|
*/
|
||||||
|
~sse_client();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Initialize the connection with the server
|
||||||
|
* @param client_name The name of the client
|
||||||
|
* @param client_version The version of the client
|
||||||
|
* @return True if initialization was successful
|
||||||
|
*/
|
||||||
|
bool initialize(const std::string& client_name, const std::string& client_version) override;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Ping request
|
||||||
|
* @return True if the server is alive
|
||||||
|
*/
|
||||||
|
bool ping() override;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Set authentication token
|
||||||
|
* @param token The authentication token
|
||||||
|
*/
|
||||||
|
void set_auth_token(const std::string& token);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Set a request header that will be sent with all requests
|
||||||
|
* @param key Header name
|
||||||
|
* @param value Header value
|
||||||
|
*/
|
||||||
|
void set_header(const std::string& key, const std::string& value);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Set timeout for requests
|
||||||
|
* @param timeout_seconds Timeout in seconds
|
||||||
|
*/
|
||||||
|
void set_timeout(int timeout_seconds);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Set client capabilities
|
||||||
|
* @param capabilities The capabilities of the client
|
||||||
|
*/
|
||||||
|
void set_capabilities(const json& capabilities) override;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Send a request and wait for a response
|
||||||
|
* @param method The method to call
|
||||||
|
* @param params The parameters to pass
|
||||||
|
* @return The response
|
||||||
|
* @throws mcp_exception on error
|
||||||
|
*/
|
||||||
|
response send_request(const std::string& method, const json& params = json::object()) override;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Send a notification (no response expected)
|
||||||
|
* @param method The method to call
|
||||||
|
* @param params The parameters to pass
|
||||||
|
* @throws mcp_exception on error
|
||||||
|
*/
|
||||||
|
void send_notification(const std::string& method, const json& params = json::object()) override;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Get server capabilities
|
||||||
|
* @return The server capabilities
|
||||||
|
* @throws mcp_exception on error
|
||||||
|
*/
|
||||||
|
json get_server_capabilities() override;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Call a tool
|
||||||
|
* @param tool_name The name of the tool to call
|
||||||
|
* @param arguments The arguments to pass to the tool
|
||||||
|
* @return The result of the tool call
|
||||||
|
* @throws mcp_exception on error
|
||||||
|
*/
|
||||||
|
json call_tool(const std::string& tool_name, const json& arguments = json::object()) override;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Get available tools
|
||||||
|
* @return List of available tools
|
||||||
|
* @throws mcp_exception on error
|
||||||
|
*/
|
||||||
|
std::vector<tool> get_tools() override;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Get client capabilities
|
||||||
|
* @return The client capabilities
|
||||||
|
*/
|
||||||
|
json get_capabilities() override;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief List available resources
|
||||||
|
* @param cursor Optional cursor for pagination
|
||||||
|
* @return List of resources
|
||||||
|
*/
|
||||||
|
json list_resources(const std::string& cursor = "") override;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Read a resource
|
||||||
|
* @param resource_uri The URI of the resource
|
||||||
|
* @return The resource content
|
||||||
|
*/
|
||||||
|
json read_resource(const std::string& resource_uri) override;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Subscribe to resource changes
|
||||||
|
* @param resource_uri The URI of the resource
|
||||||
|
* @return Subscription result
|
||||||
|
*/
|
||||||
|
json subscribe_to_resource(const std::string& resource_uri) override;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief List resource templates
|
||||||
|
* @return List of resource templates
|
||||||
|
*/
|
||||||
|
json list_resource_templates() override;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 检查服务器是否可访问
|
||||||
|
* @return True if the server is accessible
|
||||||
|
*/
|
||||||
|
bool check_server_accessible();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Check if the client is running
|
||||||
|
* @return True if the client is running
|
||||||
|
*/
|
||||||
|
bool is_running() const override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
// 初始化HTTP客户端
|
||||||
|
void init_client(const std::string& host, int port);
|
||||||
|
void init_client(const std::string& base_url);
|
||||||
|
|
||||||
|
// 打开SSE连接
|
||||||
|
void open_sse_connection();
|
||||||
|
|
||||||
|
// 解析SSE数据
|
||||||
|
bool parse_sse_data(const char* data, size_t length);
|
||||||
|
|
||||||
|
// 关闭SSE连接
|
||||||
|
void close_sse_connection();
|
||||||
|
|
||||||
|
// 发送JSON-RPC请求
|
||||||
|
json send_jsonrpc(const request& req);
|
||||||
|
|
||||||
|
// 服务器主机和端口
|
||||||
|
std::string host_;
|
||||||
|
int port_ = 8080;
|
||||||
|
|
||||||
|
// 或者使用基础URL
|
||||||
|
std::string base_url_;
|
||||||
|
|
||||||
|
// SSE端点
|
||||||
|
std::string sse_endpoint_ = "/sse";
|
||||||
|
|
||||||
|
// 消息端点
|
||||||
|
std::string msg_endpoint_;
|
||||||
|
|
||||||
|
// HTTP客户端
|
||||||
|
std::unique_ptr<httplib::Client> http_client_;
|
||||||
|
|
||||||
|
// SSE专用HTTP客户端
|
||||||
|
std::unique_ptr<httplib::Client> sse_client_;
|
||||||
|
|
||||||
|
// SSE线程
|
||||||
|
std::unique_ptr<std::thread> sse_thread_;
|
||||||
|
|
||||||
|
// SSE运行状态
|
||||||
|
std::atomic<bool> sse_running_{false};
|
||||||
|
|
||||||
|
// 认证令牌
|
||||||
|
std::string auth_token_;
|
||||||
|
|
||||||
|
// 默认请求头
|
||||||
|
std::map<std::string, std::string> default_headers_;
|
||||||
|
|
||||||
|
// 超时设置(秒)
|
||||||
|
int timeout_seconds_ = 30;
|
||||||
|
|
||||||
|
// 客户端能力
|
||||||
|
json capabilities_;
|
||||||
|
|
||||||
|
// 服务器能力
|
||||||
|
json server_capabilities_;
|
||||||
|
|
||||||
|
// 互斥锁
|
||||||
|
mutable std::mutex mutex_;
|
||||||
|
|
||||||
|
// 条件变量,用于等待消息端点设置
|
||||||
|
std::condition_variable endpoint_cv_;
|
||||||
|
|
||||||
|
// 请求ID到Promise的映射,用于异步等待响应
|
||||||
|
std::map<json, std::promise<json>> pending_requests_;
|
||||||
|
|
||||||
|
// 响应处理互斥锁
|
||||||
|
std::mutex response_mutex_;
|
||||||
|
|
||||||
|
// 响应条件变量
|
||||||
|
std::condition_variable response_cv_;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace mcp
|
||||||
|
|
||||||
|
#endif // MCP_SSE_CLIENT_H
|
|
@ -10,6 +10,7 @@
|
||||||
#ifndef MCP_STDIO_CLIENT_H
|
#ifndef MCP_STDIO_CLIENT_H
|
||||||
#define MCP_STDIO_CLIENT_H
|
#define MCP_STDIO_CLIENT_H
|
||||||
|
|
||||||
|
#include "mcp_client.h"
|
||||||
#include "mcp_message.h"
|
#include "mcp_message.h"
|
||||||
#include "mcp_tool.h"
|
#include "mcp_tool.h"
|
||||||
#include "mcp_logger.h"
|
#include "mcp_logger.h"
|
||||||
|
@ -39,7 +40,7 @@ namespace mcp {
|
||||||
* The stdio_client class provides functionality to connect to MCP servers
|
* The stdio_client class provides functionality to connect to MCP servers
|
||||||
* by spawning a separate process and communicating via standard input/output.
|
* by spawning a separate process and communicating via standard input/output.
|
||||||
*/
|
*/
|
||||||
class stdio_client {
|
class stdio_client : public client {
|
||||||
public:
|
public:
|
||||||
/**
|
/**
|
||||||
* @brief Constructor
|
* @brief Constructor
|
||||||
|
@ -54,7 +55,7 @@ public:
|
||||||
/**
|
/**
|
||||||
* @brief Destructor
|
* @brief Destructor
|
||||||
*/
|
*/
|
||||||
~stdio_client();
|
~stdio_client() override;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Set environment variables for the server process
|
* @brief Set environment variables for the server process
|
||||||
|
@ -69,19 +70,19 @@ public:
|
||||||
* @param client_version The version of the client
|
* @param client_version The version of the client
|
||||||
* @return True if initialization was successful
|
* @return True if initialization was successful
|
||||||
*/
|
*/
|
||||||
bool initialize(const std::string& client_name, const std::string& client_version);
|
bool initialize(const std::string& client_name, const std::string& client_version) override;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Ping request
|
* @brief Ping request
|
||||||
* @return True if the server is alive
|
* @return True if the server is alive
|
||||||
*/
|
*/
|
||||||
bool ping();
|
bool ping() override;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Set client capabilities
|
* @brief Set client capabilities
|
||||||
* @param capabilities The capabilities of the client
|
* @param capabilities The capabilities of the client
|
||||||
*/
|
*/
|
||||||
void set_capabilities(const json& capabilities);
|
void set_capabilities(const json& capabilities) override;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Send a request and wait for a response
|
* @brief Send a request and wait for a response
|
||||||
|
@ -90,7 +91,7 @@ public:
|
||||||
* @return The response
|
* @return The response
|
||||||
* @throws mcp_exception on error
|
* @throws mcp_exception on error
|
||||||
*/
|
*/
|
||||||
response send_request(const std::string& method, const json& params = json::object());
|
response send_request(const std::string& method, const json& params = json::object()) override;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Send a notification (no response expected)
|
* @brief Send a notification (no response expected)
|
||||||
|
@ -98,14 +99,14 @@ public:
|
||||||
* @param params The parameters to pass
|
* @param params The parameters to pass
|
||||||
* @throws mcp_exception on error
|
* @throws mcp_exception on error
|
||||||
*/
|
*/
|
||||||
void send_notification(const std::string& method, const json& params = json::object());
|
void send_notification(const std::string& method, const json& params = json::object()) override;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Get server capabilities
|
* @brief Get server capabilities
|
||||||
* @return The server capabilities
|
* @return The server capabilities
|
||||||
* @throws mcp_exception on error
|
* @throws mcp_exception on error
|
||||||
*/
|
*/
|
||||||
json get_server_capabilities();
|
json get_server_capabilities() override;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Call a tool
|
* @brief Call a tool
|
||||||
|
@ -114,53 +115,53 @@ public:
|
||||||
* @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& arguments = json::object());
|
json call_tool(const std::string& tool_name, const json& arguments = json::object()) override;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Get available tools
|
* @brief Get available tools
|
||||||
* @return List of available tools
|
* @return List of available tools
|
||||||
* @throws mcp_exception on error
|
* @throws mcp_exception on error
|
||||||
*/
|
*/
|
||||||
std::vector<tool> get_tools();
|
std::vector<tool> get_tools() override;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Get client capabilities
|
* @brief Get client capabilities
|
||||||
* @return The client capabilities
|
* @return The client capabilities
|
||||||
*/
|
*/
|
||||||
json get_capabilities();
|
json get_capabilities() override;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief List available resources
|
* @brief List available resources
|
||||||
* @param cursor Optional cursor for pagination
|
* @param cursor Optional cursor for pagination
|
||||||
* @return List of resources
|
* @return List of resources
|
||||||
*/
|
*/
|
||||||
json list_resources(const std::string& cursor = "");
|
json list_resources(const std::string& cursor = "") override;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Read a resource
|
* @brief Read a resource
|
||||||
* @param resource_uri The URI of the resource
|
* @param resource_uri The URI of the resource
|
||||||
* @return The resource content
|
* @return The resource content
|
||||||
*/
|
*/
|
||||||
json read_resource(const std::string& resource_uri);
|
json read_resource(const std::string& resource_uri) override;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Subscribe to resource changes
|
* @brief Subscribe to resource changes
|
||||||
* @param resource_uri The URI of the resource
|
* @param resource_uri The URI of the resource
|
||||||
* @return Subscription result
|
* @return Subscription result
|
||||||
*/
|
*/
|
||||||
json subscribe_to_resource(const std::string& resource_uri);
|
json subscribe_to_resource(const std::string& resource_uri) override;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief List resource templates
|
* @brief List resource templates
|
||||||
* @return List of resource templates
|
* @return List of resource templates
|
||||||
*/
|
*/
|
||||||
json list_resource_templates();
|
json list_resource_templates() override;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Check if the server process is running
|
* @brief Check if the server process is running
|
||||||
* @return True if the server process is running
|
* @return True if the server process is running
|
||||||
*/
|
*/
|
||||||
bool is_running() const;
|
bool is_running() const override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
// 启动服务器进程
|
// 启动服务器进程
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
set(TARGET mcp)
|
set(TARGET mcp)
|
||||||
|
|
||||||
add_library(${TARGET} STATIC
|
add_library(${TARGET} STATIC
|
||||||
mcp_client.cpp
|
|
||||||
../include/mcp_client.h
|
../include/mcp_client.h
|
||||||
mcp_message.cpp
|
mcp_message.cpp
|
||||||
../include/mcp_message.h
|
../include/mcp_message.h
|
||||||
|
@ -13,6 +12,8 @@ add_library(${TARGET} STATIC
|
||||||
../include/mcp_tool.h
|
../include/mcp_tool.h
|
||||||
mcp_stdio_client.cpp
|
mcp_stdio_client.cpp
|
||||||
../include/mcp_stdio_client.h
|
../include/mcp_stdio_client.h
|
||||||
|
mcp_sse_client.cpp
|
||||||
|
../include/mcp_sse_client.h
|
||||||
)
|
)
|
||||||
|
|
||||||
target_link_libraries(${TARGET} PUBLIC ${CMAKE_THREAD_LIBS_INIT})
|
target_link_libraries(${TARGET} PUBLIC ${CMAKE_THREAD_LIBS_INIT})
|
||||||
|
|
|
@ -1,32 +1,32 @@
|
||||||
/**
|
/**
|
||||||
* @file mcp_client.cpp
|
* @file mcp_sse_client.cpp
|
||||||
* @brief Implementation of the MCP client
|
* @brief Implementation of the MCP SSE client
|
||||||
*
|
*
|
||||||
* This file implements the client-side functionality for the Model Context Protocol.
|
* This file implements the client-side functionality for the Model Context Protocol using SSE.
|
||||||
* Follows the 2024-11-05 basic protocol specification.
|
* Follows the 2024-11-05 basic protocol specification.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "mcp_client.h"
|
#include "mcp_sse_client.h"
|
||||||
#include "base64.hpp"
|
#include "base64.hpp"
|
||||||
|
|
||||||
namespace mcp {
|
namespace mcp {
|
||||||
|
|
||||||
client::client(const std::string& host, int port, const std::string& sse_endpoint)
|
sse_client::sse_client(const std::string& host, int port, const std::string& sse_endpoint)
|
||||||
: host_(host), port_(port), sse_endpoint_(sse_endpoint) {
|
: host_(host), port_(port), sse_endpoint_(sse_endpoint) {
|
||||||
|
|
||||||
init_client(host, port);
|
init_client(host, port);
|
||||||
}
|
}
|
||||||
|
|
||||||
client::client(const std::string& base_url, const std::string& sse_endpoint)
|
sse_client::sse_client(const std::string& base_url, const std::string& sse_endpoint)
|
||||||
: base_url_(base_url), sse_endpoint_(sse_endpoint) {
|
: base_url_(base_url), sse_endpoint_(sse_endpoint) {
|
||||||
init_client(base_url);
|
init_client(base_url);
|
||||||
}
|
}
|
||||||
|
|
||||||
client::~client() {
|
sse_client::~sse_client() {
|
||||||
close_sse_connection();
|
close_sse_connection();
|
||||||
}
|
}
|
||||||
|
|
||||||
void client::init_client(const std::string& host, int port) {
|
void sse_client::init_client(const std::string& host, int port) {
|
||||||
http_client_ = std::make_unique<httplib::Client>(host.c_str(), port);
|
http_client_ = std::make_unique<httplib::Client>(host.c_str(), port);
|
||||||
sse_client_ = std::make_unique<httplib::Client>(host.c_str(), port);
|
sse_client_ = std::make_unique<httplib::Client>(host.c_str(), port);
|
||||||
|
|
||||||
|
@ -38,7 +38,7 @@ void client::init_client(const std::string& host, int port) {
|
||||||
sse_client_->set_write_timeout(timeout_seconds_, 0);
|
sse_client_->set_write_timeout(timeout_seconds_, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
void client::init_client(const std::string& base_url) {
|
void sse_client::init_client(const std::string& base_url) {
|
||||||
http_client_ = std::make_unique<httplib::Client>(base_url.c_str());
|
http_client_ = std::make_unique<httplib::Client>(base_url.c_str());
|
||||||
sse_client_ = std::make_unique<httplib::Client>(base_url.c_str());
|
sse_client_ = std::make_unique<httplib::Client>(base_url.c_str());
|
||||||
|
|
||||||
|
@ -51,7 +51,7 @@ void client::init_client(const std::string& base_url) {
|
||||||
sse_client_->set_write_timeout(timeout_seconds_, 0);
|
sse_client_->set_write_timeout(timeout_seconds_, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool client::initialize(const std::string& client_name, const std::string& client_version) {
|
bool sse_client::initialize(const std::string& client_name, const std::string& client_version) {
|
||||||
LOG_INFO("Initializing MCP client...");
|
LOG_INFO("Initializing MCP client...");
|
||||||
|
|
||||||
if (!check_server_accessible()) {
|
if (!check_server_accessible()) {
|
||||||
|
@ -118,7 +118,7 @@ bool client::initialize(const std::string& client_name, const std::string& clien
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool client::ping() {
|
bool sse_client::ping() {
|
||||||
request req = request::create("ping", {});
|
request req = request::create("ping", {});
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
@ -129,13 +129,13 @@ bool client::ping() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void client::set_auth_token(const std::string& token) {
|
void sse_client::set_auth_token(const std::string& token) {
|
||||||
std::lock_guard<std::mutex> lock(mutex_);
|
std::lock_guard<std::mutex> lock(mutex_);
|
||||||
auth_token_ = token;
|
auth_token_ = token;
|
||||||
set_header("Authorization", "Bearer " + auth_token_);
|
set_header("Authorization", "Bearer " + auth_token_);
|
||||||
}
|
}
|
||||||
|
|
||||||
void client::set_header(const std::string& key, const std::string& value) {
|
void sse_client::set_header(const std::string& key, const std::string& value) {
|
||||||
std::lock_guard<std::mutex> lock(mutex_);
|
std::lock_guard<std::mutex> lock(mutex_);
|
||||||
default_headers_[key] = value;
|
default_headers_[key] = value;
|
||||||
|
|
||||||
|
@ -147,7 +147,7 @@ void client::set_header(const std::string& key, const std::string& value) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void client::set_timeout(int timeout_seconds) {
|
void sse_client::set_timeout(int timeout_seconds) {
|
||||||
std::lock_guard<std::mutex> lock(mutex_);
|
std::lock_guard<std::mutex> lock(mutex_);
|
||||||
timeout_seconds_ = timeout_seconds;
|
timeout_seconds_ = timeout_seconds;
|
||||||
|
|
||||||
|
@ -162,12 +162,12 @@ void client::set_timeout(int timeout_seconds) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void client::set_capabilities(const json& capabilities) {
|
void sse_client::set_capabilities(const json& capabilities) {
|
||||||
std::lock_guard<std::mutex> lock(mutex_);
|
std::lock_guard<std::mutex> lock(mutex_);
|
||||||
capabilities_ = capabilities;
|
capabilities_ = capabilities;
|
||||||
}
|
}
|
||||||
|
|
||||||
response client::send_request(const std::string& method, const json& params) {
|
response sse_client::send_request(const std::string& method, const json& params) {
|
||||||
request req = request::create(method, params);
|
request req = request::create(method, params);
|
||||||
json result = send_jsonrpc(req);
|
json result = send_jsonrpc(req);
|
||||||
|
|
||||||
|
@ -179,23 +179,23 @@ response client::send_request(const std::string& method, const json& params) {
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
void client::send_notification(const std::string& method, const json& params) {
|
void sse_client::send_notification(const std::string& method, const json& params) {
|
||||||
request req = request::create_notification(method, params);
|
request req = request::create_notification(method, params);
|
||||||
send_jsonrpc(req);
|
send_jsonrpc(req);
|
||||||
}
|
}
|
||||||
|
|
||||||
json client::get_server_capabilities() {
|
json sse_client::get_server_capabilities() {
|
||||||
return server_capabilities_;
|
return server_capabilities_;
|
||||||
}
|
}
|
||||||
|
|
||||||
json client::call_tool(const std::string& tool_name, const json& arguments) {
|
json sse_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},
|
||||||
{"arguments", arguments}
|
{"arguments", arguments}
|
||||||
}).result;
|
}).result;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<tool> client::get_tools() {
|
std::vector<tool> sse_client::get_tools() {
|
||||||
json response_json = send_request("tools/list", {}).result;
|
json response_json = send_request("tools/list", {}).result;
|
||||||
std::vector<tool> tools;
|
std::vector<tool> tools;
|
||||||
|
|
||||||
|
@ -223,11 +223,11 @@ std::vector<tool> client::get_tools() {
|
||||||
return tools;
|
return tools;
|
||||||
}
|
}
|
||||||
|
|
||||||
json client::get_capabilities() {
|
json sse_client::get_capabilities() {
|
||||||
return capabilities_;
|
return capabilities_;
|
||||||
}
|
}
|
||||||
|
|
||||||
json client::list_resources(const std::string& cursor) {
|
json sse_client::list_resources(const std::string& cursor) {
|
||||||
json params = json::object();
|
json params = json::object();
|
||||||
if (!cursor.empty()) {
|
if (!cursor.empty()) {
|
||||||
params["cursor"] = cursor;
|
params["cursor"] = cursor;
|
||||||
|
@ -235,23 +235,23 @@ json client::list_resources(const std::string& cursor) {
|
||||||
return send_request("resources/list", params).result;
|
return send_request("resources/list", params).result;
|
||||||
}
|
}
|
||||||
|
|
||||||
json client::read_resource(const std::string& resource_uri) {
|
json sse_client::read_resource(const std::string& resource_uri) {
|
||||||
return send_request("resources/read", {
|
return send_request("resources/read", {
|
||||||
{"uri", resource_uri}
|
{"uri", resource_uri}
|
||||||
}).result;
|
}).result;
|
||||||
}
|
}
|
||||||
|
|
||||||
json client::subscribe_to_resource(const std::string& resource_uri) {
|
json sse_client::subscribe_to_resource(const std::string& resource_uri) {
|
||||||
return send_request("resources/subscribe", {
|
return send_request("resources/subscribe", {
|
||||||
{"uri", resource_uri}
|
{"uri", resource_uri}
|
||||||
}).result;
|
}).result;
|
||||||
}
|
}
|
||||||
|
|
||||||
json client::list_resource_templates() {
|
json sse_client::list_resource_templates() {
|
||||||
return send_request("resources/templates/list").result;
|
return send_request("resources/templates/list").result;
|
||||||
}
|
}
|
||||||
|
|
||||||
void client::open_sse_connection() {
|
void sse_client::open_sse_connection() {
|
||||||
sse_running_ = true;
|
sse_running_ = true;
|
||||||
|
|
||||||
{
|
{
|
||||||
|
@ -331,7 +331,7 @@ void client::open_sse_connection() {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
bool client::parse_sse_data(const char* data, size_t length) {
|
bool sse_client::parse_sse_data(const char* data, size_t length) {
|
||||||
try {
|
try {
|
||||||
std::string sse_data(data, length);
|
std::string sse_data(data, length);
|
||||||
|
|
||||||
|
@ -406,7 +406,7 @@ bool client::parse_sse_data(const char* data, size_t length) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void client::close_sse_connection() {
|
void sse_client::close_sse_connection() {
|
||||||
if (!sse_running_) {
|
if (!sse_running_) {
|
||||||
LOG_INFO("SSE connection already closed");
|
LOG_INFO("SSE connection already closed");
|
||||||
return;
|
return;
|
||||||
|
@ -451,7 +451,7 @@ void client::close_sse_connection() {
|
||||||
LOG_INFO("SSE connection successfully closed (normal exit flow)");
|
LOG_INFO("SSE connection successfully closed (normal exit flow)");
|
||||||
}
|
}
|
||||||
|
|
||||||
json client::send_jsonrpc(const request& req) {
|
json sse_client::send_jsonrpc(const request& req) {
|
||||||
std::lock_guard<std::mutex> lock(mutex_);
|
std::lock_guard<std::mutex> lock(mutex_);
|
||||||
|
|
||||||
if (msg_endpoint_.empty()) {
|
if (msg_endpoint_.empty()) {
|
||||||
|
@ -561,7 +561,7 @@ json client::send_jsonrpc(const request& req) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool client::check_server_accessible() {
|
bool sse_client::check_server_accessible() {
|
||||||
LOG_INFO("Checking if server is accessible...");
|
LOG_INFO("Checking if server is accessible...");
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
@ -581,4 +581,8 @@ bool client::check_server_accessible() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool sse_client::is_running() const {
|
||||||
|
return sse_running_;
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace mcp
|
} // namespace mcp
|
|
@ -1 +1 @@
|
||||||
Subproject commit 6910c9d9165801d8827d628cb72eb7ea9dd538c5
|
Subproject commit b514bdc898e2951020cbdca1304b75f5950d1f59
|
|
@ -11,6 +11,7 @@
|
||||||
#include "mcp_client.h"
|
#include "mcp_client.h"
|
||||||
#include "mcp_server.h"
|
#include "mcp_server.h"
|
||||||
#include "mcp_tool.h"
|
#include "mcp_tool.h"
|
||||||
|
#include "mcp_sse_client.h"
|
||||||
|
|
||||||
using namespace mcp;
|
using namespace mcp;
|
||||||
using json = nlohmann::ordered_json;
|
using json = nlohmann::ordered_json;
|
||||||
|
@ -135,7 +136,7 @@ protected:
|
||||||
{"roots", {{"listChanged", true}}},
|
{"roots", {{"listChanged", true}}},
|
||||||
{"sampling", json::object()}
|
{"sampling", json::object()}
|
||||||
};
|
};
|
||||||
client_ = std::make_unique<client>("localhost", 8080);
|
client_ = std::make_unique<sse_client>("localhost", 8080);
|
||||||
client_->set_capabilities(client_capabilities);
|
client_->set_capabilities(client_capabilities);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -147,7 +148,7 @@ protected:
|
||||||
}
|
}
|
||||||
|
|
||||||
std::unique_ptr<server> server_;
|
std::unique_ptr<server> server_;
|
||||||
std::unique_ptr<client> client_;
|
std::unique_ptr<sse_client> client_;
|
||||||
};
|
};
|
||||||
|
|
||||||
// 测试初始化流程
|
// 测试初始化流程
|
||||||
|
@ -186,7 +187,7 @@ protected:
|
||||||
// 启动服务器(非阻塞模式)
|
// 启动服务器(非阻塞模式)
|
||||||
server_->start(false);
|
server_->start(false);
|
||||||
|
|
||||||
client_ = std::make_unique<client>("localhost", 8081);
|
client_ = std::make_unique<sse_client>("localhost", 8081);
|
||||||
}
|
}
|
||||||
|
|
||||||
void TearDown() override {
|
void TearDown() override {
|
||||||
|
@ -197,11 +198,12 @@ protected:
|
||||||
}
|
}
|
||||||
|
|
||||||
std::unique_ptr<server> server_;
|
std::unique_ptr<server> server_;
|
||||||
std::unique_ptr<client> client_;
|
std::unique_ptr<sse_client> client_;
|
||||||
};
|
};
|
||||||
|
|
||||||
// 测试支持的版本
|
// 测试支持的版本
|
||||||
TEST_F(VersioningTest, SupportedVersion) {
|
TEST_F(VersioningTest, SupportedVersion) {
|
||||||
|
std::this_thread::sleep_for(std::chrono::milliseconds(100));
|
||||||
// 执行初始化
|
// 执行初始化
|
||||||
bool init_result = client_->initialize("TestClient", "1.0.0");
|
bool init_result = client_->initialize("TestClient", "1.0.0");
|
||||||
|
|
||||||
|
@ -211,6 +213,7 @@ TEST_F(VersioningTest, SupportedVersion) {
|
||||||
|
|
||||||
// 测试不支持的版本
|
// 测试不支持的版本
|
||||||
TEST_F(VersioningTest, UnsupportedVersion) {
|
TEST_F(VersioningTest, UnsupportedVersion) {
|
||||||
|
std::this_thread::sleep_for(std::chrono::milliseconds(100));
|
||||||
try {
|
try {
|
||||||
// 使用 httplib::Client 发送不支持的版本请求
|
// 使用 httplib::Client 发送不支持的版本请求
|
||||||
std::unique_ptr<httplib::Client> sse_client = std::make_unique<httplib::Client>("localhost", 8081);
|
std::unique_ptr<httplib::Client> sse_client = std::make_unique<httplib::Client>("localhost", 8081);
|
||||||
|
@ -233,7 +236,7 @@ TEST_F(VersioningTest, UnsupportedVersion) {
|
||||||
size_t pos = response.find("data: ");
|
size_t pos = response.find("data: ");
|
||||||
if (pos != std::string::npos) {
|
if (pos != std::string::npos) {
|
||||||
std::string data_content = response.substr(pos + 6);
|
std::string data_content = response.substr(pos + 6);
|
||||||
data_content = data_content.substr(0, data_content.find("\n"));
|
data_content = data_content.substr(0, data_content.find("\r\n"));
|
||||||
|
|
||||||
if (!msg_endpoint_received.load() && response.find("endpoint") != std::string::npos) {
|
if (!msg_endpoint_received.load() && response.find("endpoint") != std::string::npos) {
|
||||||
msg_endpoint_received.store(true);
|
msg_endpoint_received.store(true);
|
||||||
|
@ -266,7 +269,7 @@ TEST_F(VersioningTest, UnsupportedVersion) {
|
||||||
auto res = http_client->Post(endpoint.c_str(), req.dump(), "application/json");
|
auto res = http_client->Post(endpoint.c_str(), req.dump(), "application/json");
|
||||||
|
|
||||||
EXPECT_TRUE(res != nullptr);
|
EXPECT_TRUE(res != nullptr);
|
||||||
EXPECT_EQ(res->status, 202);
|
EXPECT_EQ(res->status / 100, 2);
|
||||||
|
|
||||||
auto mcp_res = json::parse(sse_response.get());
|
auto mcp_res = json::parse(sse_response.get());
|
||||||
EXPECT_EQ(mcp_res["error"]["code"].get<int>(), static_cast<int>(error_code::invalid_params));
|
EXPECT_EQ(mcp_res["error"]["code"].get<int>(), static_cast<int>(error_code::invalid_params));
|
||||||
|
@ -320,7 +323,12 @@ protected:
|
||||||
server_->start(false);
|
server_->start(false);
|
||||||
|
|
||||||
// 创建客户端
|
// 创建客户端
|
||||||
client_ = std::make_unique<client>("localhost", 8082);
|
json client_capabilities = {
|
||||||
|
{"roots", {{"listChanged", true}}},
|
||||||
|
{"sampling", json::object()}
|
||||||
|
};
|
||||||
|
client_ = std::make_unique<sse_client>("localhost", 8082);
|
||||||
|
client_->set_capabilities(client_capabilities);
|
||||||
}
|
}
|
||||||
|
|
||||||
void TearDown() override {
|
void TearDown() override {
|
||||||
|
@ -331,7 +339,7 @@ protected:
|
||||||
}
|
}
|
||||||
|
|
||||||
std::unique_ptr<server> server_;
|
std::unique_ptr<server> server_;
|
||||||
std::unique_ptr<client> client_;
|
std::unique_ptr<sse_client> client_;
|
||||||
};
|
};
|
||||||
|
|
||||||
// 测试Ping请求
|
// 测试Ping请求
|
||||||
|
@ -365,7 +373,7 @@ TEST_F(PingTest, DirectPing) {
|
||||||
size_t pos = response.find("data: ");
|
size_t pos = response.find("data: ");
|
||||||
if (pos != std::string::npos) {
|
if (pos != std::string::npos) {
|
||||||
std::string data_content = response.substr(pos + 6);
|
std::string data_content = response.substr(pos + 6);
|
||||||
data_content = data_content.substr(0, data_content.find("\n"));
|
data_content = data_content.substr(0, data_content.find("\r\n"));
|
||||||
|
|
||||||
if (!msg_endpoint_received.load() && response.find("endpoint") != std::string::npos) {
|
if (!msg_endpoint_received.load() && response.find("endpoint") != std::string::npos) {
|
||||||
msg_endpoint_received.store(true);
|
msg_endpoint_received.store(true);
|
||||||
|
@ -522,7 +530,12 @@ protected:
|
||||||
server_->start(false);
|
server_->start(false);
|
||||||
|
|
||||||
// 创建客户端
|
// 创建客户端
|
||||||
client_ = std::make_unique<client>("localhost", 8083);
|
json client_capabilities = {
|
||||||
|
{"roots", {{"listChanged", true}}},
|
||||||
|
{"sampling", json::object()}
|
||||||
|
};
|
||||||
|
client_ = std::make_unique<sse_client>("localhost", 8083);
|
||||||
|
client_->set_capabilities(client_capabilities);
|
||||||
client_->initialize("TestClient", "1.0.0");
|
client_->initialize("TestClient", "1.0.0");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -534,7 +547,7 @@ protected:
|
||||||
}
|
}
|
||||||
|
|
||||||
std::unique_ptr<server> server_;
|
std::unique_ptr<server> server_;
|
||||||
std::unique_ptr<client> client_;
|
std::unique_ptr<sse_client> client_;
|
||||||
};
|
};
|
||||||
|
|
||||||
// 测试列出工具
|
// 测试列出工具
|
||||||
|
@ -553,6 +566,7 @@ TEST_F(ToolsTest, ListTools) {
|
||||||
|
|
||||||
// 测试调用工具
|
// 测试调用工具
|
||||||
TEST_F(ToolsTest, CallTool) {
|
TEST_F(ToolsTest, CallTool) {
|
||||||
|
std::this_thread::sleep_for(std::chrono::milliseconds(100));
|
||||||
// 调用工具
|
// 调用工具
|
||||||
json tool_result = client_->call_tool("get_weather", {{"location", "New York"}});
|
json tool_result = client_->call_tool("get_weather", {{"location", "New York"}});
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue