#include "stdafx.h" #include "McpServer.h" #include "jsoncpp/json.h" #include "HostJson.h" // BuildHostJson(单台主机序列化公共函数) #include "context.h" // context 接口 #include "2015RemoteDlg.h" // CMy2015RemoteDlg 成员(m_HostList/m_cs/m_ClientMap)+ VERSION_STR #include #ifndef _WIN64 #ifdef _DEBUG #pragma comment(lib, "jsoncpp/jsoncppd.lib") #else #pragma comment(lib, "jsoncpp/jsoncpp.lib") #endif #else #ifdef _DEBUG #pragma comment(lib, "jsoncpp/jsoncpp_x64d.lib") #else #pragma comment(lib, "jsoncpp/jsoncpp_x64.lib") #endif #endif namespace { // Json::Value → 紧凑 JSON 字符串 std::string JsonToString(const Json::Value& v) { Json::StreamWriterBuilder b; b["indentation"] = ""; return Json::writeString(b, v); } // JSON-RPC 2.0 成功响应 std::string BuildResult(const Json::Value& id, const Json::Value& result) { Json::Value resp(Json::objectValue); resp["jsonrpc"] = "2.0"; resp["id"] = id; resp["result"] = result; return JsonToString(resp); } // JSON-RPC 2.0 错误响应 std::string BuildError(const Json::Value& id, int code, const std::string& msg) { Json::Value resp(Json::objectValue); resp["jsonrpc"] = "2.0"; resp["id"] = id; Json::Value err(Json::objectValue); err["code"] = code; err["message"] = msg; resp["error"] = err; return JsonToString(resp); } // initialize 握手(MCP 规范:protocolVersion + capabilities + serverInfo) std::string BuildInitializeResult(const Json::Value& id) { Json::Value result(Json::objectValue); result["protocolVersion"] = "2025-06-18"; Json::Value caps(Json::objectValue); caps["tools"] = Json::Value(Json::objectValue); result["capabilities"] = caps; Json::Value serverInfo(Json::objectValue); serverInfo["name"] = "yama"; serverInfo["version"] = VERSION_STR; result["serverInfo"] = serverInfo; return BuildResult(id, result); } // ping 健康检查:返回空 result std::string BuildPingResult(const Json::Value& id) { return BuildResult(id, Json::Value(Json::objectValue)); } // list_online_hosts 的 outputSchema(见 docs/Mcp_Design.md §3.3.2) Json::Value BuildHostOutputSchema() { Json::Value props(Json::objectValue); Json::Value hostsProp(Json::objectValue); hostsProp["type"] = "array"; Json::Value items(Json::objectValue); items["type"] = "object"; Json::Value itemProps(Json::objectValue); const char* strFields[] = { "id", "name", "remark", "ip", "os", "location", "rtt", "version", "activeWindow", "group", "screen", "clientType" }; for (const char* f : strFields) { Json::Value p(Json::objectValue); p["type"] = "string"; itemProps[f] = p; } Json::Value onlineProp(Json::objectValue); onlineProp["type"] = "boolean"; itemProps["online"] = onlineProp; items["properties"] = itemProps; hostsProp["items"] = items; props["hosts"] = hostsProp; Json::Value schema(Json::objectValue); schema["type"] = "object"; schema["properties"] = props; Json::Value required(Json::arrayValue); required.append("hosts"); schema["required"] = required; return schema; } // tools/list std::string BuildToolsListResult(const Json::Value& id) { Json::Value result(Json::objectValue); Json::Value tools(Json::arrayValue); Json::Value tool(Json::objectValue); tool["name"] = "list_online_hosts"; // 说明文字为 UTF-8:项目 /execution-charset:.936 会把普通窄字面量编译成 GBK, // 故用 u8 前缀确保输出到 JSON 的字节是 UTF-8。 tool["description"] = u8"获取当前所有在线主机的列表,包含计算机名、IP、操作系统、版本、备注、分组、活动窗口、延迟等实时信息。"; Json::Value inputSchema(Json::objectValue); inputSchema["type"] = "object"; inputSchema["properties"] = Json::Value(Json::objectValue); inputSchema["required"] = Json::Value(Json::arrayValue); tool["inputSchema"] = inputSchema; tool["outputSchema"] = BuildHostOutputSchema(); tools.append(tool); result["tools"] = tools; return BuildResult(id, result); } // tools/call(list_online_hosts) std::string BuildToolsCall(const Json::Value& root, CMy2015RemoteDlg* parent) { const Json::Value& id = root["id"]; const Json::Value& params = root.isMember("params") ? root["params"] : Json::Value(Json::objectValue); std::string toolName; if (params.isObject() && params.isMember("name") && params["name"].isString()) { toolName = params["name"].asString(); } if (toolName != "list_online_hosts") { return BuildError(id, -32602, "Unknown tool: " + (toolName.empty() ? std::string("(empty)") : toolName)); } Json::Value hosts(Json::arrayValue); int count = 0; if (parent) { // 与 WebService 侧一致的锁内遍历,复用 BuildHostJson 序列化(方案 C)。 EnterCriticalSection(&parent->m_cs); for (context* ctx : parent->m_HostList) { if (!ctx || !ctx->IsLogin()) continue; hosts.append(BuildHostJson(ctx, parent->m_ClientMap)); ++count; } LeaveCriticalSection(&parent->m_cs); } Json::Value result(Json::objectValue); Json::Value structuredContent(Json::objectValue); structuredContent["hosts"] = hosts; result["structuredContent"] = structuredContent; Json::Value content(Json::arrayValue); Json::Value item(Json::objectValue); item["type"] = "text"; item["text"] = std::string(u8"共 ") + std::to_string(count) + std::string(u8" 台主机在线。"); content.append(item); result["content"] = content; result["isError"] = false; return BuildResult(id, result); } } // namespace ////////////////////////////////////////////////////////////////////////// // CMcpServer Implementation ////////////////////////////////////////////////////////////////////////// CMcpServer& CMcpServer::Instance() { static CMcpServer instance; return instance; } CMcpServer::CMcpServer() { m_server.Post("/mcp", [this](const httplib::Request& req, httplib::Response& res) { HandleMcp(req, res); }); } CMcpServer::~CMcpServer() { Stop(); // 兜底:确保监听线程 join,避免 std::thread 析构触发 terminate } bool CMcpServer::Start(const std::string& bind, int port) { if (m_running.load()) return true; // 已在运行 m_thread = std::thread([this, bind, port]() { m_server.listen(bind, port); }); // 给 listen 一点时间绑定端口;httplib::Server::is_running() 在 listen 内部置位。 std::this_thread::sleep_for(std::chrono::milliseconds(100)); m_running.store(m_server.is_running()); return m_running.load(); } void CMcpServer::Stop() { m_server.stop(); if (m_thread.joinable()) { m_thread.join(); } m_running.store(false); } void CMcpServer::HandleMcp(const httplib::Request& req, httplib::Response& res) { res.set_header("Content-Type", "application/json"); // 静态 token 校验:Authorization: Bearer (Start 前经 SetToken 保证非空) if (req.get_header_value("Authorization") != ("Bearer " + m_token)) { res.status = 401; res.set_content(BuildError(Json::nullValue, -32000, "Unauthorized"), "application/json"); return; } // 解析 JSON-RPC 请求体 Json::Value root; Json::CharReaderBuilder rbuilder; std::string errs; std::istringstream iss(req.body); if (!Json::parseFromStream(rbuilder, iss, &root, &errs) || !root.isObject()) { res.set_content(BuildError(Json::nullValue, -32700, "Parse error"), "application/json"); return; } // 通知(无 id)→ 不返回 JSON-RPC 响应(如 notifications/initialized) if (!root.isMember("id")) { res.status = 202; res.set_content("", "application/json"); return; } // 结构校验:缺 method if (!root.isMember("method") || !root["method"].isString()) { res.set_content(BuildError(root["id"], -32600, "Invalid Request"), "application/json"); return; } std::string method = root["method"].asString(); if (method == "initialize") { res.set_content(BuildInitializeResult(root["id"]), "application/json"); return; } if (method == "ping") { res.set_content(BuildPingResult(root["id"]), "application/json"); return; } if (method == "tools/list") { res.set_content(BuildToolsListResult(root["id"]), "application/json"); return; } if (method == "tools/call") { res.set_content(BuildToolsCall(root, m_parent), "application/json"); return; } // 未实现的方法 res.set_content(BuildError(root["id"], -32601, "Method not found"), "application/json"); } // rand_s:Windows CRT 加密安全随机源(基于系统 CSPRNG)。其声明需在 前 // 定义 _CRT_RAND_S;为避免依赖 PCH 的包含顺序,这里手动声明其导出原型(errno_t == int)。 extern "C" int __cdecl rand_s(unsigned int* randomValue); std::string GenerateRandomToken() { static const char hex[] = "0123456789abcdef"; std::string out; out.reserve(32); for (int i = 0; i < 16; ++i) { unsigned int v = 0; if (rand_s(&v) != 0) { // rand_s 失败(罕见):退化为时间 + 地址熵,保证仍返回非空 token。 v = (unsigned int)(GetTickCount() ^ (ULONG_PTR)&out); } out.push_back(hex[(v >> 4) & 0xF]); out.push_back(hex[v & 0xF]); } return out; }