Skip to content

Commit 8589082

Browse files
feat(core): dynamic plugin load is supported now.
- It is able to detect the plugins dynamicly if plugins has changed - make sure the same way to make_error and make_response.
1 parent 83888cf commit 8589082

File tree

15 files changed

+513
-265
lines changed

15 files changed

+513
-265
lines changed

.gitmodules

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,4 +9,4 @@
99
url = https://github.com/yhirose/cpp-httplib.git
1010
[submodule "third_party/googletest"]
1111
path = third_party/googletest
12-
url = https://github.com/google/googletest.git
12+
url = https://github.com/google/googletest.git

plugins/CMakeLists.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,2 @@
11
add_subdirectory(official)
2-
add_subdirectory(sdk)
2+
add_subdirectory(sdk)

plugins/official/example_stream_plugin/example_stream_plugin.cpp

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -67,13 +67,11 @@ static int number_stream_next(StreamGenerator generator, const char **result_jso
6767
batch.push_back(gen->current_num++);
6868
}
6969

70-
71-
nlohmann::json response = {
72-
{"jsonrpc", "2.0"},
73-
{"result", {{"batch", batch}, {"remaining", 1024 - gen->current_num + 1}}}};
70+
nlohmann::json result = {{"batch", batch}, {"remaining", 1024 - gen->current_num + 1}};
71+
std::string response = mcp::protocol::generate_result(result);
7472

7573
static thread_local std::string buffer;
76-
buffer = response.dump();
74+
buffer = response;
7775
*result_json = buffer.c_str();
7876

7977
gen->last_send_time = now;

plugins/official/file_plugin/file_plugin.cpp

Lines changed: 7 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -16,20 +16,14 @@ static std::string read_file(const std::string &path) {
1616
std::ifstream f(path);
1717
if (!f.is_open()) {
1818
// Return custom error code and message, consistent with safe_system_plugin
19-
return nlohmann::json{
20-
{"error", {{"code", -32000},// Custom error code
21-
{"message", "File not found or cannot open"}}}}
22-
.dump();
19+
return mcp::protocol::generate_error(mcp::protocol::error_code::TOOL_NOT_FOUND, "File not found or cannot open");
2320
}
2421
std::stringstream buffer;
2522
buffer << f.rdbuf();
26-
return nlohmann::json{{"content", buffer.str()}}.dump();
23+
return mcp::protocol::generate_result(nlohmann::json{{"content", buffer.str()}});
2724
} catch (const std::exception &e) {
2825
// Return custom error code and message, consistent with safe_system_plugin
29-
return nlohmann::json{
30-
{"error", {{"code", -32000},// Custom error code
31-
{"message", "Failed to read file: " + std::string(e.what())}}}}
32-
.dump();
26+
return mcp::protocol::generate_error(mcp::protocol::error_code::TOOL_NOT_FOUND, "Failed to read file: " + std::string(e.what()));
3327
}
3428
}
3529

@@ -38,27 +32,21 @@ static std::string write_file(const std::string &path, const std::string &conten
3832
std::ofstream f(path);
3933
if (!f.is_open()) {
4034
// Return custom error code and message, consistent with safe_system_plugin
41-
return nlohmann::json{
42-
{"error", {{"code", -32000},// Custom error code
43-
{"message", "Cannot open file for writing"}}}}
44-
.dump();
35+
return mcp::protocol::generate_error(mcp::protocol::error_code::TOOL_NOT_FOUND, "Cannot open file for writing");
4536
}
4637
f << content;
4738
f.close();
48-
return R"({"result": "success"})";
39+
return mcp::protocol::generate_result(nlohmann::json{{"result", "success"}});
4940
} catch (const std::exception &e) {
5041
// Return custom error code and message, consistent with safe_system_plugin
51-
return nlohmann::json{
52-
{"error", {{"code", -32000},// Custom error code
53-
{"message", "Failed to write file: " + std::string(e.what())}}}}
54-
.dump();
42+
return mcp::protocol::generate_error(mcp::protocol::error_code::TOOL_NOT_FOUND, "Failed to write file: " + std::string(e.what()));
5543
}
5644
}
5745

5846
static std::string list_files(const std::string &path) {
5947
// This is a simplified implementation that just returns the path
6048
// A real implementation would actually list files in the directory
61-
return nlohmann::json{{"path", path}}.dump();
49+
return mcp::protocol::generate_result(nlohmann::json{{"path", path}});
6250
}
6351

6452
extern "C" MCP_API ToolInfo *get_tools(int *count) {

plugins/official/http_plugin/http_plugin.cpp

Lines changed: 12 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -19,9 +19,7 @@ static std::string http_get(const std::string &url) {
1919
std::smatch url_match;
2020

2121
if (!std::regex_match(url, url_match, url_regex)) {
22-
return nlohmann::json{
23-
{"error", {{"code", -32000}, {"message", "Invalid URL format"}}}}
24-
.dump();
22+
return mcp::protocol::generate_error(mcp::protocol::error_code::TOOL_NOT_FOUND, "Invalid URL format");
2523
}
2624

2725
std::string scheme = url_match[1];
@@ -40,20 +38,16 @@ static std::string http_get(const std::string &url) {
4038
auto res = client.Get(path.c_str());
4139
if (!res) {
4240
// Return custom error code and message, consistent with safe_system_plugin
43-
return nlohmann::json{
44-
{"error", {{"code", -32000},// Custom error code
45-
{"message", "Request failed: Network error or invalid URL"}}}}
46-
.dump();
41+
return mcp::protocol::generate_error(mcp::protocol::error_code::TOOL_NOT_FOUND,
42+
"Request failed: Network error or invalid URL");
4743
}
4844

4945
// Return only the body content to conform to MCP protocol
50-
return res->body;
46+
return mcp::protocol::generate_result(nlohmann::json{{"content", res->body}});
5147
} catch (const std::exception &e) {
5248
// Return custom error code and message, consistent with safe_system_plugin
53-
return nlohmann::json{
54-
{"error", {{"code", -32000},// Custom error code
55-
{"message", "HTTP GET request failed: " + std::string(e.what())}}}}
56-
.dump();
49+
return mcp::protocol::generate_error(mcp::protocol::error_code::TOOL_NOT_FOUND,
50+
"HTTP GET request failed: " + std::string(e.what()));
5751
}
5852
}
5953

@@ -65,9 +59,7 @@ static std::string http_post(const std::string &url, const std::string &body) {
6559
std::smatch url_match;
6660

6761
if (!std::regex_match(url, url_match, url_regex)) {
68-
return nlohmann::json{
69-
{"error", {{"code", -32000}, {"message", "Invalid URL format"}}}}
70-
.dump();
62+
return mcp::protocol::generate_error(mcp::protocol::error_code::TOOL_NOT_FOUND, "Invalid URL format");
7163
}
7264

7365
std::string scheme = url_match[1];
@@ -86,20 +78,16 @@ static std::string http_post(const std::string &url, const std::string &body) {
8678
auto res = client.Post(path.c_str(), body, "application/json");
8779
if (!res) {
8880
// Return custom error code and message, consistent with safe_system_plugin
89-
return nlohmann::json{
90-
{"error", {{"code", -32000},// Custom error code
91-
{"message", "Request failed: Network error or invalid URL"}}}}
92-
.dump();
81+
return mcp::protocol::generate_error(mcp::protocol::error_code::TOOL_NOT_FOUND,
82+
"Request failed: Network error or invalid URL");
9383
}
9484

9585
// Return only the body content to conform to MCP protocol
96-
return res->body;
86+
return mcp::protocol::generate_result(nlohmann::json{{"content", res->body}});
9787
} catch (const std::exception &e) {
9888
// Return custom error code and message, consistent with safe_system_plugin
99-
return nlohmann::json{
100-
{"error", {{"code", -32000},// Custom error code
101-
{"message", "HTTP POST request failed: " + std::string(e.what())}}}}
102-
.dump();
89+
return mcp::protocol::generate_error(mcp::protocol::error_code::TOOL_NOT_FOUND,
90+
"HTTP POST request failed: " + std::string(e.what()));
10391
}
10492
}
10593

plugins/official/safe_system_plugin/safe_system_plugin.cpp

Lines changed: 31 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -37,13 +37,11 @@ static std::string get_current_time() {
3737
ss << std::put_time(std::localtime(&time_t), "%Y-%m-%d %H:%M:%S");
3838

3939
// Return raw business data without RPC wrapper
40-
return nlohmann::json{{"current_time", ss.str()}}.dump();
40+
return mcp::protocol::generate_result(nlohmann::json{{"current_time", ss.str()}});
4141
} catch (const std::exception &e) {
4242
// Return custom error code and message
43-
return nlohmann::json{
44-
{"error", {{"code", -32000},// Custom error code
45-
{"message", "Failed to get current time: " + std::string(e.what())}}}}
46-
.dump();
43+
return mcp::protocol::generate_error(mcp::protocol::error_code::TOOL_NOT_FOUND,
44+
"Failed to get current time: " + std::string(e.what()));
4745
}
4846
}
4947

@@ -61,13 +59,11 @@ static std::string get_system_info() {
6159
std::string arch = sizeof(void *) == 8 ? "x86_64" : "x86";
6260

6361
// Return raw business data without RPC wrapper
64-
return nlohmann::json{{"os", os}, {"arch", arch}}.dump();
62+
return mcp::protocol::generate_result(nlohmann::json{{"os", os}, {"arch", arch}});
6563
} catch (const std::exception &e) {
6664
// Return custom error code and message
67-
return nlohmann::json{
68-
{"error", {{"code", -32000},// Custom error code
69-
{"message", "Failed to get system info: " + std::string(e.what())}}}}
70-
.dump();
65+
return mcp::protocol::generate_error(mcp::protocol::error_code::TOOL_NOT_FOUND,
66+
"Failed to get system info: " + std::string(e.what()));
7167
}
7268
}
7369

@@ -81,10 +77,8 @@ static std::string list_files(const std::string &path) {
8177
// Prevent path traversal attacks
8278
if (path.find("..") != std::string::npos) {
8379
// Return custom error code and message
84-
return nlohmann::json{
85-
{"error", {{"code", -32000},// Custom error code
86-
{"message", "Path traversal is not allowed"}}}}
87-
.dump();
80+
return mcp::protocol::generate_error(mcp::protocol::error_code::TOOL_NOT_FOUND,
81+
"Path traversal is not allowed");
8882
}
8983

9084
std::array<char, 128> buffer;
@@ -101,10 +95,8 @@ static std::string list_files(const std::string &path) {
10195
std::unique_ptr<FILE, decltype(&pclose)> pipe(popen(cmd.c_str(), "r"), pclose);
10296
if (!pipe) {
10397
// Return custom error code and message
104-
return nlohmann::json{
105-
{"error", {{"code", -32000},// Custom error code
106-
{"message", "Failed to list files: Pipe creation failed"}}}}
107-
.dump();
98+
return mcp::protocol::generate_error(mcp::protocol::error_code::TOOL_NOT_FOUND,
99+
"Failed to list files: Pipe creation failed");
108100
}
109101

110102
// Read command output
@@ -113,13 +105,11 @@ static std::string list_files(const std::string &path) {
113105
}
114106

115107
// Return raw business data
116-
return nlohmann::json{{"files", result}}.dump();
108+
return mcp::protocol::generate_result(nlohmann::json{{"files", result}});
117109
} catch (const std::exception &e) {
118110
// Return custom error code and message
119-
return nlohmann::json{
120-
{"error", {{"code", -32000},// Custom error code
121-
{"message", "Failed to list files: " + std::string(e.what())}}}}
122-
.dump();
111+
return mcp::protocol::generate_error(mcp::protocol::error_code::TOOL_NOT_FOUND,
112+
"Failed to list files: " + std::string(e.what()));
123113
}
124114
}
125115

@@ -135,10 +125,8 @@ static std::string ping_host(const std::string &host) {
135125
return std::isalnum(c) || c == '.' || c == '-';
136126
})) {
137127
// Return custom error code and message
138-
return nlohmann::json{
139-
{"error", {{"code", -32000},// Custom error code
140-
{"message", "Invalid host name format"}}}}
141-
.dump();
128+
return mcp::protocol::generate_error(mcp::protocol::error_code::TOOL_NOT_FOUND,
129+
"Invalid host name format");
142130
}
143131

144132
std::array<char, 128> buffer;
@@ -155,10 +143,8 @@ static std::string ping_host(const std::string &host) {
155143
std::unique_ptr<FILE, decltype(&pclose)> pipe(popen(cmd.c_str(), "r"), pclose);
156144
if (!pipe) {
157145
// Return custom error code and message
158-
return nlohmann::json{
159-
{"error", {{"code", -32000},// Custom error code
160-
{"message", "Ping command failed: Pipe creation failed"}}}}
161-
.dump();
146+
return mcp::protocol::generate_error(mcp::protocol::error_code::TOOL_NOT_FOUND,
147+
"Ping command failed: Pipe creation failed");
162148
}
163149

164150
// Read ping output
@@ -171,13 +157,11 @@ static std::string ping_host(const std::string &host) {
171157
result.find("time=") != std::string::npos;
172158

173159
// Return raw business data
174-
return nlohmann::json{{"output", result}, {"success", success}}.dump();
160+
return mcp::protocol::generate_result(nlohmann::json{{"output", result}, {"success", success}});
175161
} catch (const std::exception &e) {
176162
// Return custom error code and message
177-
return nlohmann::json{
178-
{"error", {{"code", -32000},// Custom error code
179-
{"message", "Ping command failed: " + std::string(e.what())}}}}
180-
.dump();
163+
return mcp::protocol::generate_error(mcp::protocol::error_code::TOOL_NOT_FOUND,
164+
"Ping command failed: " + std::string(e.what()));
181165
}
182166
}
183167

@@ -190,10 +174,8 @@ static std::string check_connectivity() {
190174
return ping_host("8.8.8.8");// Use Google DNS as test target
191175
} catch (const std::exception &e) {
192176
// Return custom error code and message
193-
return nlohmann::json{
194-
{"error", {{"code", -32000},// Custom error code
195-
{"message", "Failed to check connectivity: " + std::string(e.what())}}}}
196-
.dump();
177+
return mcp::protocol::generate_error(mcp::protocol::error_code::TOOL_NOT_FOUND,
178+
"Failed to check connectivity: " + std::string(e.what()));
197179
}
198180
}
199181

@@ -222,10 +204,7 @@ static std::string get_public_ip() {
222204
std::unique_ptr<FILE, decltype(&pclose)> pipe(popen(cmd.c_str(), "r"), pclose);
223205
if (!pipe) {
224206
// Return custom error code and message
225-
return nlohmann::json{
226-
{"error", {{"code", -32000},// Custom error code
227-
{"message", "curl command not found"}}}}
228-
.dump();
207+
return mcp::protocol::generate_error(mcp::protocol::error_code::TOOL_NOT_FOUND, "curl command not found");
229208
}
230209

231210
if (fgets(buffer.data(), buffer.size(), pipe.get()) != nullptr) {
@@ -235,7 +214,7 @@ static std::string get_public_ip() {
235214
}
236215

237216
if (!ip.empty() && ip.find('.') != std::string::npos) {
238-
return nlohmann::json{{"public_ip", ip}}.dump();
217+
return mcp::protocol::generate_result(nlohmann::json{{"public_ip", ip}});
239218
} else {
240219
// If the IP is not obtained correctly, return an error message
241220
// Try to use foreign - accessible IP query services
@@ -246,32 +225,26 @@ static std::string get_public_ip() {
246225
#endif
247226
std::unique_ptr<FILE, decltype(&pclose)> foreign_pipe(popen(foreign_cmd.c_str(), "r"), pclose);
248227
if (!foreign_pipe) {
249-
return nlohmann::json{
250-
{"error", {{"code", -32000},// Custom error code
251-
{"message", "curl command not found when trying foreign service"}}}}
252-
.dump();
228+
return mcp::protocol::generate_error(mcp::protocol::error_code::TOOL_NOT_FOUND,
229+
"curl command not found when trying foreign service");
253230
}
254231
std::array<char, 128> foreign_buffer;
255232
if (fgets(foreign_buffer.data(), foreign_buffer.size(), foreign_pipe.get()) != nullptr) {
256233
ip = foreign_buffer.data();
257234
ip.erase(std::remove(ip.begin(), ip.end(), '\n'), ip.end());
258235
}
259236
if (!ip.empty() && ip.find('.') != std::string::npos) {
260-
return nlohmann::json{{"public_ip", ip}}.dump();
237+
return mcp::protocol::generate_result(nlohmann::json{{"public_ip", ip}});
261238
} else {
262239
// Return custom error code and message if still failed
263-
return nlohmann::json{
264-
{"error", {{"code", -32000},// Custom error code
265-
{"message", "Failed to get public IP after trying both domestic and foreign services"}}}}
266-
.dump();
240+
return mcp::protocol::generate_error(mcp::protocol::error_code::TOOL_NOT_FOUND,
241+
"Failed to get public IP after trying both domestic and foreign services");
267242
}
268243
}
269244
} catch (const std::exception &e) {
270245
// If an exception occurs, return an error message with the exception details
271-
return nlohmann::json{
272-
{"error", {{"code", -32000},// Custom error code
273-
{"message", "Failed to get public IP: " + std::string(e.what())}}}}
274-
.dump();
246+
return mcp::protocol::generate_error(mcp::protocol::error_code::TOOL_NOT_FOUND,
247+
"Failed to get public IP: " + std::string(e.what()));
275248
}
276249
}
277250

plugins/sdk/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ endif()
2828
# Link required libraries for the SDK
2929
target_link_libraries(mcp_plugin_sdk PRIVATE
3030
mcp_core
31+
mcp_protocol
3132
)
3233

3334
# Conditionally install the library and headers

0 commit comments

Comments
 (0)