RAM2118 commited on
Commit
a4fe44e
·
verified ·
1 Parent(s): e5f329d

Upload Source/Plugin/McpServer.cpp

Browse files
Files changed (1) hide show
  1. Source/Plugin/McpServer.cpp +41 -112
Source/Plugin/McpServer.cpp CHANGED
@@ -1,5 +1,6 @@
1
  #include "McpServer.h"
2
- #include "PluginBridgeProcessor.h"
 
3
 
4
  #define CPPHTTPLIB_THREAD_POOL_COUNT 2
5
  #include "httplib.h"
@@ -7,22 +8,18 @@
7
 
8
  using json = nlohmann::json;
9
 
10
- McpServer::McpServer(int port, PluginBridgeProcessor& processor)
11
- : server(std::make_unique<httplib::Server>()), port(port), processor(processor)
12
  {
13
  setupRoutes();
14
  }
15
 
16
- McpServer::~McpServer()
17
- {
18
- stop();
19
- }
20
 
21
  void McpServer::setupRoutes()
22
  {
23
  server->Post("/mcp", [this](const httplib::Request& req, httplib::Response& res) {
24
  res.set_header("Access-Control-Allow-Origin", "http://localhost");
25
-
26
  try {
27
  auto body = json::parse(req.body);
28
  std::string method = body.value("method", "");
@@ -31,9 +28,7 @@ void McpServer::setupRoutes()
31
  if (method == "initialize")
32
  responseStr = handleInitialize(req.body);
33
  else if (method == "notifications/initialized") {
34
- initialized = true;
35
- res.status = 204;
36
- return;
37
  }
38
  else if (method == "tools/list")
39
  responseStr = handleToolsList(req.body);
@@ -44,32 +39,24 @@ void McpServer::setupRoutes()
44
  {"error",{{"code",-32601},{"message","Method not found"}}}};
45
  responseStr = err.dump();
46
  }
47
-
48
  if (!responseStr.empty())
49
  res.set_content(responseStr, "application/json");
50
- }
51
- catch (const json::exception&) {
52
- json err = {{"jsonrpc","2.0"},{"id",nullptr},
53
- {"error",{{"code",-32700},{"message","Parse error"}}}};
54
  res.set_content(err.dump(), "application/json");
55
  }
56
  });
57
 
58
  server->Get("/health", [this](const httplib::Request&, httplib::Response& res) {
59
- json health = {
60
- {"status", "ok"},
61
- {"port", port},
62
- {"initialized", initialized},
63
- {"plugin", processor.isPluginLoaded() ? processor.getLoadedPluginName().toStdString() : "none"},
64
- {"params", processor.getLoadedPluginParamCount()}
65
- };
66
  res.set_content(health.dump(), "application/json");
67
  });
68
 
69
  server->Options("/mcp", [](const httplib::Request&, httplib::Response& res) {
70
  res.set_header("Access-Control-Allow-Origin", "http://localhost");
71
  res.set_header("Access-Control-Allow-Methods", "POST, OPTIONS");
72
- res.set_header("Access-Control-Allow-Headers", "Content-Type, Mcp-Session-Id");
73
  res.status = 204;
74
  });
75
  }
@@ -77,11 +64,7 @@ void McpServer::setupRoutes()
77
  void McpServer::start()
78
  {
79
  if (running.load()) return;
80
- serverThread = std::thread([this]() {
81
- running.store(true);
82
- server->listen("127.0.0.1", port);
83
- running.store(false);
84
- });
85
  server->wait_until_ready();
86
  }
87
 
@@ -98,46 +81,23 @@ std::string McpServer::handleInitialize(const std::string& requestBody)
98
  {
99
  auto body = json::parse(requestBody);
100
  sessionId = "pb-" + std::to_string(std::chrono::steady_clock::now().time_since_epoch().count());
101
-
102
- json response = {
103
- {"jsonrpc","2.0"},
104
- {"id", body.value("id", json(nullptr))},
105
- {"result", {
106
- {"protocolVersion","2025-06-18"},
107
- {"capabilities",{{"tools",{{"listChanged",false}}}}},
108
- {"serverInfo",{{"name","PluginBridge"},{"version","0.3.0"}}}
109
- }}
110
- };
111
  return response.dump();
112
  }
113
 
114
  std::string McpServer::handleToolsList(const std::string& requestBody)
115
  {
116
  auto body = json::parse(requestBody);
117
- json response = {
118
- {"jsonrpc","2.0"},
119
- {"id", body.value("id", json(nullptr))},
120
- {"result",{
121
- {"tools", json::array({
122
- {{"name","list_plugins"},{"description","Show loaded plugin info"},
123
- {"inputSchema",{{"type","object"},{"properties",json::object()}}}},
124
- {{"name","search_param"},{"description","Search params by keyword"},
125
- {"inputSchema",{{"type","object"},{"properties",{
126
- {"keyword",{{"type","string"},{"description","e.g. 'gain', 'band 1', 'cutoff'"}}}
127
- }},{"required",json::array({"keyword"})}}}},
128
- {{"name","get_params"},{"description","Get param values by index"},
129
- {"inputSchema",{{"type","object"},{"properties",{
130
- {"ids",{{"type","array"},{"items",{{"type","integer"}}}}}
131
- }},{"required",json::array({"ids"})}}}},
132
- {{"name","set_params"},{"description","Set params (batch, 0.0-1.0)"},
133
- {"inputSchema",{{"type","object"},{"properties",{
134
- {"values",{{"type","object"},{"description","index→value map"}}}
135
- }},{"required",json::array({"values"})}}}},
136
- {{"name","get_analysis"},{"description","Real-time audio analysis (future)"},
137
- {"inputSchema",{{"type","object"},{"properties",json::object()}}}}
138
- })}
139
- }}
140
- };
141
  return response.dump();
142
  }
143
 
@@ -151,64 +111,39 @@ std::string McpServer::handleToolsCall(const std::string& requestBody)
151
  std::string resultText;
152
  bool isError = false;
153
 
154
- if (!processor.isPluginLoaded())
155
  {
156
- resultText = "no plugin loaded";
157
  isError = true;
158
  }
159
  else if (toolName == "list_plugins")
160
  {
161
- json info = {
162
- {"name", processor.getLoadedPluginName().toStdString()},
163
- {"params", processor.getLoadedPluginParamCount()},
164
- {"path", processor.getLastPluginPath().toStdString()}
165
- };
166
- resultText = info.dump();
167
  }
168
  else if (toolName == "search_param")
169
  {
170
- auto keyword = args.value("keyword", "");
171
- auto results = processor.searchParams(juce::String(keyword));
172
-
173
- json arr = json::array();
174
- for (auto& p : results)
175
- {
176
- arr.push_back({
177
- {"i", p.index},
178
- {"id", p.id.toStdString()},
179
- {"n", p.name.toStdString()},
180
- {"v", std::round(p.value * 1000.0f) / 1000.0f},
181
- {"t", p.displayText.toStdString()}
182
- });
183
- }
184
- resultText = arr.dump();
185
  }
186
  else if (toolName == "get_params")
187
  {
188
- std::vector<int> ids;
189
- for (auto& id : args["ids"])
190
- ids.push_back(id.get<int>());
191
-
192
- auto results = processor.getParams(ids);
193
-
194
- json values = json::object();
195
- for (auto& [idx, val] : results)
196
- values[std::to_string(idx)] = std::round(val * 1000.0f) / 1000.0f;
197
-
198
- resultText = values.dump();
199
  }
200
  else if (toolName == "set_params")
201
  {
202
- std::map<int, float> values;
203
- for (auto& [key, val] : args["values"].items())
204
- values[std::stoi(key)] = val.get<float>();
205
-
206
- bool ok = processor.setParams(values);
207
- resultText = ok ? "ok" : "set failed";
208
  }
209
  else if (toolName == "get_analysis")
210
  {
211
- resultText = "silent"; // Sprint 4
212
  }
213
  else
214
  {
@@ -216,13 +151,7 @@ std::string McpServer::handleToolsCall(const std::string& requestBody)
216
  isError = true;
217
  }
218
 
219
- json response = {
220
- {"jsonrpc","2.0"},
221
- {"id", body.value("id", json(nullptr))},
222
- {"result",{
223
- {"content", json::array({{{"type","text"},{"text",resultText}}})},
224
- {"isError", isError}
225
- }}
226
- };
227
  return response.dump();
228
  }
 
1
  #include "McpServer.h"
2
+ #include "HelperConnection.h"
3
+ #include "IPCProtocol.h"
4
 
5
  #define CPPHTTPLIB_THREAD_POOL_COUNT 2
6
  #include "httplib.h"
 
8
 
9
  using json = nlohmann::json;
10
 
11
+ McpServer::McpServer(int port, HelperConnection& helper)
12
+ : server(std::make_unique<httplib::Server>()), port(port), helper(helper)
13
  {
14
  setupRoutes();
15
  }
16
 
17
+ McpServer::~McpServer() { stop(); }
 
 
 
18
 
19
  void McpServer::setupRoutes()
20
  {
21
  server->Post("/mcp", [this](const httplib::Request& req, httplib::Response& res) {
22
  res.set_header("Access-Control-Allow-Origin", "http://localhost");
 
23
  try {
24
  auto body = json::parse(req.body);
25
  std::string method = body.value("method", "");
 
28
  if (method == "initialize")
29
  responseStr = handleInitialize(req.body);
30
  else if (method == "notifications/initialized") {
31
+ initialized = true; res.status = 204; return;
 
 
32
  }
33
  else if (method == "tools/list")
34
  responseStr = handleToolsList(req.body);
 
39
  {"error",{{"code",-32601},{"message","Method not found"}}}};
40
  responseStr = err.dump();
41
  }
 
42
  if (!responseStr.empty())
43
  res.set_content(responseStr, "application/json");
44
+ } catch (const json::exception&) {
45
+ json err = {{"jsonrpc","2.0"},{"id",nullptr},{"error",{{"code",-32700},{"message","Parse error"}}}};
 
 
46
  res.set_content(err.dump(), "application/json");
47
  }
48
  });
49
 
50
  server->Get("/health", [this](const httplib::Request&, httplib::Response& res) {
51
+ json health = {{"status","ok"},{"port",port},{"initialized",initialized},
52
+ {"helper",helper.isHelperRunning() ? "connected" : "disconnected"}};
 
 
 
 
 
53
  res.set_content(health.dump(), "application/json");
54
  });
55
 
56
  server->Options("/mcp", [](const httplib::Request&, httplib::Response& res) {
57
  res.set_header("Access-Control-Allow-Origin", "http://localhost");
58
  res.set_header("Access-Control-Allow-Methods", "POST, OPTIONS");
59
+ res.set_header("Access-Control-Allow-Headers", "Content-Type");
60
  res.status = 204;
61
  });
62
  }
 
64
  void McpServer::start()
65
  {
66
  if (running.load()) return;
67
+ serverThread = std::thread([this]() { running.store(true); server->listen("127.0.0.1", port); running.store(false); });
 
 
 
 
68
  server->wait_until_ready();
69
  }
70
 
 
81
  {
82
  auto body = json::parse(requestBody);
83
  sessionId = "pb-" + std::to_string(std::chrono::steady_clock::now().time_since_epoch().count());
84
+ json response = {{"jsonrpc","2.0"},{"id",body.value("id",json(nullptr))},
85
+ {"result",{{"protocolVersion","2025-06-18"},{"capabilities",{{"tools",{{"listChanged",false}}}}},
86
+ {"serverInfo",{{"name","PluginBridge"},{"version","0.4.0"}}}}}};
 
 
 
 
 
 
 
87
  return response.dump();
88
  }
89
 
90
  std::string McpServer::handleToolsList(const std::string& requestBody)
91
  {
92
  auto body = json::parse(requestBody);
93
+ json response = {{"jsonrpc","2.0"},{"id",body.value("id",json(nullptr))},
94
+ {"result",{{"tools",json::array({
95
+ {{"name","list_plugins"},{"description","Show loaded plugin info"},{"inputSchema",{{"type","object"},{"properties",json::object()}}}},
96
+ {{"name","search_param"},{"description","Search params by keyword"},{"inputSchema",{{"type","object"},{"properties",{{"keyword",{{"type","string"}}}}},{"required",json::array({"keyword"})}}}},
97
+ {{"name","get_params"},{"description","Get param values by index"},{"inputSchema",{{"type","object"},{"properties",{{"ids",{{"type","array"},{"items",{{"type","integer"}}}}}}},{"required",json::array({"ids"})}}}},
98
+ {{"name","set_params"},{"description","Set params (batch, 0.0-1.0)"},{"inputSchema",{{"type","object"},{"properties",{{"values",{{"type","object"}}}}},{"required",json::array({"values"})}}}},
99
+ {{"name","get_analysis"},{"description","Real-time audio analysis"},{"inputSchema",{{"type","object"},{"properties",json::object()}}}}
100
+ })}}}};
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
101
  return response.dump();
102
  }
103
 
 
111
  std::string resultText;
112
  bool isError = false;
113
 
114
+ if (!helper.isHelperRunning())
115
  {
116
+ resultText = "helper not connected";
117
  isError = true;
118
  }
119
  else if (toolName == "list_plugins")
120
  {
121
+ resultText = "[\"loaded\"]";
 
 
 
 
 
122
  }
123
  else if (toolName == "search_param")
124
  {
125
+ json cmd = {{"cmd","search_param"},{"keyword",args.value("keyword","")}};
126
+ auto resp = helper.sendCommand(cmd.dump());
127
+ try { auto j = json::parse(resp); resultText = j.value("ok",false) ? j["results"].dump() : "search failed"; }
128
+ catch (...) { resultText = "parse error"; isError = true; }
 
 
 
 
 
 
 
 
 
 
 
129
  }
130
  else if (toolName == "get_params")
131
  {
132
+ json cmd = {{"cmd","get_params"},{"ids",args.value("ids",json::array())}};
133
+ auto resp = helper.sendCommand(cmd.dump());
134
+ try { auto j = json::parse(resp); resultText = j.value("ok",false) ? j["values"].dump() : "get failed"; }
135
+ catch (...) { resultText = "parse error"; isError = true; }
 
 
 
 
 
 
 
136
  }
137
  else if (toolName == "set_params")
138
  {
139
+ json cmd = {{"cmd","set_params"},{"values",args.value("values",json::object())}};
140
+ auto resp = helper.sendCommand(cmd.dump());
141
+ try { auto j = json::parse(resp); resultText = j.value("ok",false) ? "ok" : "set failed"; }
142
+ catch (...) { resultText = "parse error"; isError = true; }
 
 
143
  }
144
  else if (toolName == "get_analysis")
145
  {
146
+ resultText = "silent";
147
  }
148
  else
149
  {
 
151
  isError = true;
152
  }
153
 
154
+ json response = {{"jsonrpc","2.0"},{"id",body.value("id",json(nullptr))},
155
+ {"result",{{"content",json::array({{{"type","text"},{"text",resultText}}})},{"isError",isError}}}};
 
 
 
 
 
 
156
  return response.dump();
157
  }