File size: 6,808 Bytes
df8b77e a4fe44e df8b77e a4fe44e df8b77e a4fe44e df8b77e a4fe44e df8b77e a4fe44e df8b77e a4fe44e df8b77e a4fe44e df8b77e a4fe44e df8b77e a4fe44e 9836bb8 df8b77e a4fe44e 9836bb8 a4fe44e df8b77e a4fe44e df8b77e a4fe44e df8b77e a4fe44e df8b77e a4fe44e df8b77e a4fe44e df8b77e a4fe44e df8b77e 9836bb8 df8b77e a4fe44e df8b77e | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 | #include "McpServer.h"
#include "HelperConnection.h"
#include "IPCProtocol.h"
#define CPPHTTPLIB_THREAD_POOL_COUNT 2
#include "httplib.h"
#include "json.hpp"
using json = nlohmann::json;
McpServer::McpServer(int port, HelperConnection& helper)
: server(std::make_unique<httplib::Server>()), port(port), helper(helper)
{
setupRoutes();
}
McpServer::~McpServer() { stop(); }
void McpServer::setupRoutes()
{
server->Post("/mcp", [this](const httplib::Request& req, httplib::Response& res) {
res.set_header("Access-Control-Allow-Origin", "http://localhost");
try {
auto body = json::parse(req.body);
std::string method = body.value("method", "");
std::string responseStr;
if (method == "initialize")
responseStr = handleInitialize(req.body);
else if (method == "notifications/initialized") {
initialized = true; res.status = 204; return;
}
else if (method == "tools/list")
responseStr = handleToolsList(req.body);
else if (method == "tools/call")
responseStr = handleToolsCall(req.body);
else {
json err = {{"jsonrpc","2.0"},{"id",body.value("id",json(nullptr))},
{"error",{{"code",-32601},{"message","Method not found"}}}};
responseStr = err.dump();
}
if (!responseStr.empty())
res.set_content(responseStr, "application/json");
} catch (const json::exception&) {
json err = {{"jsonrpc","2.0"},{"id",nullptr},{"error",{{"code",-32700},{"message","Parse error"}}}};
res.set_content(err.dump(), "application/json");
}
});
server->Get("/health", [this](const httplib::Request&, httplib::Response& res) {
json health = {{"status","ok"},{"port",port},{"initialized",initialized},
{"helper",helper.isHelperRunning() ? "connected" : "disconnected"}};
res.set_content(health.dump(), "application/json");
});
server->Options("/mcp", [](const httplib::Request&, httplib::Response& res) {
res.set_header("Access-Control-Allow-Origin", "http://localhost");
res.set_header("Access-Control-Allow-Methods", "POST, OPTIONS");
res.set_header("Access-Control-Allow-Headers", "Content-Type");
res.status = 204;
});
}
void McpServer::start()
{
if (running.load()) return;
serverThread = std::thread([this]() { running.store(true); server->listen("127.0.0.1", port); running.store(false); });
server->wait_until_ready();
}
void McpServer::stop()
{
if (server && server->is_running()) server->stop();
if (serverThread.joinable()) serverThread.join();
running.store(false);
}
bool McpServer::isRunning() const { return running.load(); }
std::string McpServer::handleInitialize(const std::string& requestBody)
{
auto body = json::parse(requestBody);
sessionId = "pb-" + std::to_string(std::chrono::steady_clock::now().time_since_epoch().count());
json response = {{"jsonrpc","2.0"},{"id",body.value("id",json(nullptr))},
{"result",{{"protocolVersion","2025-06-18"},{"capabilities",{{"tools",{{"listChanged",false}}}}},
{"serverInfo",{{"name","PluginBridge"},{"version","0.5.2"}}}}}};
return response.dump();
}
std::string McpServer::handleToolsList(const std::string& requestBody)
{
auto body = json::parse(requestBody);
json response = {{"jsonrpc","2.0"},{"id",body.value("id",json(nullptr))},
{"result",{{"tools",json::array({
{{"name","list_plugins"},{"description","Show loaded plugin info"},{"inputSchema",{{"type","object"},{"properties",json::object()}}}},
{{"name","search_param"},{"description","Search params by keyword"},{"inputSchema",{{"type","object"},{"properties",{{"keyword",{{"type","string"}}}}},{"required",json::array({"keyword"})}}}},
{{"name","get_params"},{"description","Get param values by index"},{"inputSchema",{{"type","object"},{"properties",{{"ids",{{"type","array"},{"items",{{"type","integer"}}}}}}},{"required",json::array({"ids"})}}}},
{{"name","set_params"},{"description","Set params (batch, 0.0-1.0)"},{"inputSchema",{{"type","object"},{"properties",{{"values",{{"type","object"}}}}},{"required",json::array({"values"})}}}},
{{"name","get_analysis"},{"description","Real-time audio analysis: LUFS, true peak, 7-band balance, stereo width, brightness. Returns compact string."},{"inputSchema",{{"type","object"},{"properties",json::object()}}}}
})}}}};
return response.dump();
}
std::string McpServer::handleToolsCall(const std::string& requestBody)
{
auto body = json::parse(requestBody);
auto params = body["params"];
std::string toolName = params["name"];
auto args = params.value("arguments", json::object());
std::string resultText;
bool isError = false;
if (!helper.isHelperRunning())
{
resultText = "helper not connected";
isError = true;
}
else if (toolName == "list_plugins")
{
resultText = "[\"loaded\"]";
}
else if (toolName == "search_param")
{
json cmd = {{"cmd","search_param"},{"keyword",args.value("keyword","")}};
auto resp = helper.sendCommand(cmd.dump());
try { auto j = json::parse(resp); resultText = j.value("ok",false) ? j["results"].dump() : "search failed"; }
catch (...) { resultText = "parse error"; isError = true; }
}
else if (toolName == "get_params")
{
json cmd = {{"cmd","get_params"},{"ids",args.value("ids",json::array())}};
auto resp = helper.sendCommand(cmd.dump());
try { auto j = json::parse(resp); resultText = j.value("ok",false) ? j["values"].dump() : "get failed"; }
catch (...) { resultText = "parse error"; isError = true; }
}
else if (toolName == "set_params")
{
json cmd = {{"cmd","set_params"},{"values",args.value("values",json::object())}};
auto resp = helper.sendCommand(cmd.dump());
try { auto j = json::parse(resp); resultText = j.value("ok",false) ? "ok" : "set failed"; }
catch (...) { resultText = "parse error"; isError = true; }
}
else if (toolName == "get_analysis")
{
if (getAnalysisCallback)
resultText = getAnalysisCallback();
else
resultText = "analyser not ready";
}
else
{
resultText = "unknown tool: " + toolName;
isError = true;
}
json response = {{"jsonrpc","2.0"},{"id",body.value("id",json(nullptr))},
{"result",{{"content",json::array({{{"type","text"},{"text",resultText}}})},{"isError",isError}}}};
return response.dump();
}
|