Download Source/Plugin/McpServer.cpp from RAM2118/pluginbridge: direct link, hf CLI and curl.
- Browser
- Download file 7.75 kB
-
https://huggingface.co/RAM2118/pluginbridge/resolve/ddb115bf829f00a019effbe2c307028edd9a0d27/Source/Plugin/McpServer.cpp
- Command line
-
hf download hf://RAM2118/pluginbridge@ddb115bf829f00a019effbe2c307028edd9a0d27/Source/Plugin/McpServer.cpp
-
curl -L -o McpServer.cpp https://huggingface.co/RAM2118/pluginbridge/resolve/ddb115bf829f00a019effbe2c307028edd9a0d27/Source/Plugin/McpServer.cpp
7.75 kB
| using json = nlohmann::json; | |
| McpServer::McpServer(int port, PluginBridgeProcessor& processor) | |
| : server(std::make_unique<httplib::Server>()), port(port), processor(processor) | |
| { | |
| 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}, | |
| {"plugin", processor.isPluginLoaded() ? processor.getLoadedPluginName().toStdString() : "none"}, | |
| {"params", processor.getLoadedPluginParamCount()} | |
| }; | |
| 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, Mcp-Session-Id"); | |
| 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.3.0"}}} | |
| }} | |
| }; | |
| 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"},{"description","e.g. 'gain', 'band 1', 'cutoff'"}}} | |
| }},{"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"},{"description","index→value map"}}} | |
| }},{"required",json::array({"values"})}}}}, | |
| {{"name","get_analysis"},{"description","Real-time audio analysis (future)"}, | |
| {"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 (!processor.isPluginLoaded()) | |
| { | |
| resultText = "no plugin loaded"; | |
| isError = true; | |
| } | |
| else if (toolName == "list_plugins") | |
| { | |
| json info = { | |
| {"name", processor.getLoadedPluginName().toStdString()}, | |
| {"params", processor.getLoadedPluginParamCount()}, | |
| {"path", processor.getLastPluginPath().toStdString()} | |
| }; | |
| resultText = info.dump(); | |
| } | |
| else if (toolName == "search_param") | |
| { | |
| auto keyword = args.value("keyword", ""); | |
| auto results = processor.searchParams(juce::String(keyword)); | |
| json arr = json::array(); | |
| for (auto& p : results) | |
| { | |
| arr.push_back({ | |
| {"i", p.index}, | |
| {"id", p.id.toStdString()}, | |
| {"n", p.name.toStdString()}, | |
| {"v", std::round(p.value * 1000.0f) / 1000.0f}, | |
| {"t", p.displayText.toStdString()} | |
| }); | |
| } | |
| resultText = arr.dump(); | |
| } | |
| else if (toolName == "get_params") | |
| { | |
| std::vector<int> ids; | |
| for (auto& id : args["ids"]) | |
| ids.push_back(id.get<int>()); | |
| auto results = processor.getParams(ids); | |
| json values = json::object(); | |
| for (auto& [idx, val] : results) | |
| values[std::to_string(idx)] = std::round(val * 1000.0f) / 1000.0f; | |
| resultText = values.dump(); | |
| } | |
| else if (toolName == "set_params") | |
| { | |
| std::map<int, float> values; | |
| for (auto& [key, val] : args["values"].items()) | |
| values[std::stoi(key)] = val.get<float>(); | |
| bool ok = processor.setParams(values); | |
| resultText = ok ? "ok" : "set failed"; | |
| } | |
| else if (toolName == "get_analysis") | |
| { | |
| resultText = "silent"; // Sprint 4 | |
| } | |
| 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(); | |
| } | |