Feature: Add MCP (Model Context Protocol) server integration
Add an optional MCP server (JSON-RPC 2.0 over Streamable HTTP) exposing an online-host listing tool, protected by a Bearer token. Disabled by default; configured via a new "Extensions > MCP Settings" dialog. - McpServer: httplib + JSON-RPC 2.0 dispatch (initialize/ping/tools/list/tools/call) - McpSettingsDlg: runtime-created dialog for enable/port/bind/token - HostJson: extract single-host JSON serialization shared with WebService - FRP: expose MCP port (union with listening/Web ports) when bound to 0.0.0.0 - i18n: en_US / zh_TW translations Co-Authored-By: deepseek-v4-pro
This commit is contained in:
@@ -6,6 +6,7 @@
|
||||
#include "2015RemoteDlg.h"
|
||||
#include "Server.h" // For CONTEXT_OBJECT
|
||||
#include "jsoncpp/json.h"
|
||||
#include "HostJson.h" // BuildHostJson(单台主机序列化公共函数)
|
||||
#include "WebPage.h"
|
||||
#include "SimpleWebSocket.h"
|
||||
#include "common/commands.h"
|
||||
@@ -88,31 +89,6 @@ static void ClearNonce(void* ws_ptr) {
|
||||
s_ClientNonces.erase(ws_ptr);
|
||||
}
|
||||
|
||||
// Helper: Convert ANSI (GBK) string to UTF-8
|
||||
static std::string AnsiToUtf8(const CString& str) {
|
||||
if (str.IsEmpty()) return "";
|
||||
#ifdef _UNICODE
|
||||
// Unicode build: CString is already UTF-16, convert to UTF-8
|
||||
int len = WideCharToMultiByte(CP_UTF8, 0, str, -1, NULL, 0, NULL, NULL);
|
||||
if (len <= 0) return "";
|
||||
std::string result(len - 1, '\0');
|
||||
WideCharToMultiByte(CP_UTF8, 0, str, -1, &result[0], len, NULL, NULL);
|
||||
return result;
|
||||
#else
|
||||
// MBCS build: CString is ANSI (GBK), need to convert to UTF-16 first, then to UTF-8
|
||||
int wlen = MultiByteToWideChar(CP_ACP, 0, str, -1, NULL, 0);
|
||||
if (wlen <= 0) return "";
|
||||
std::wstring wstr(wlen - 1, L'\0');
|
||||
MultiByteToWideChar(CP_ACP, 0, str, -1, &wstr[0], wlen);
|
||||
|
||||
int u8len = WideCharToMultiByte(CP_UTF8, 0, wstr.c_str(), -1, NULL, 0, NULL, NULL);
|
||||
if (u8len <= 0) return "";
|
||||
std::string result(u8len - 1, '\0');
|
||||
WideCharToMultiByte(CP_UTF8, 0, wstr.c_str(), -1, &result[0], u8len, NULL, NULL);
|
||||
return result;
|
||||
#endif
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// CWebService Implementation
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
@@ -1544,71 +1520,9 @@ std::string CWebService::BuildDeviceListJson(const std::string& username) {
|
||||
if (!allowed) continue; // Skip device not in allowed groups
|
||||
}
|
||||
|
||||
Json::Value device;
|
||||
// Use string for ID to avoid JavaScript number precision loss
|
||||
device["id"] = std::to_string(ctx->GetClientID());
|
||||
|
||||
CString name = ctx->GetClientData(ONLINELIST_COMPUTER_NAME);
|
||||
device["name"] = AnsiToUtf8(name);
|
||||
|
||||
// 用户在 MFC 端给这台主机起的备注(菜单"修改备注"写入 MAP_NOTE)
|
||||
// 例如 hostname="A6" + remark="我的Windows" → web 显示"A6 (我的Windows)"
|
||||
if (m_pParentDlg->m_ClientMap) {
|
||||
CString remark = m_pParentDlg->m_ClientMap->GetClientMapData(
|
||||
ctx->GetClientID(), MAP_NOTE);
|
||||
if (!remark.IsEmpty()) {
|
||||
device["remark"] = AnsiToUtf8(remark);
|
||||
}
|
||||
}
|
||||
|
||||
CString ip = ctx->GetClientData(ONLINELIST_IP);
|
||||
device["ip"] = AnsiToUtf8(ip);
|
||||
|
||||
CString os = ctx->GetClientData(ONLINELIST_OS);
|
||||
device["os"] = AnsiToUtf8(os);
|
||||
|
||||
CString location = ctx->GetClientData(ONLINELIST_LOCATION);
|
||||
device["location"] = AnsiToUtf8(location);
|
||||
|
||||
CString rtt = ctx->GetClientData(ONLINELIST_PING);
|
||||
device["rtt"] = AnsiToUtf8(rtt);
|
||||
|
||||
CString version = ctx->GetClientData(ONLINELIST_VERSION);
|
||||
device["version"] = AnsiToUtf8(version);
|
||||
|
||||
// 活动窗口编码由客户端能力位决定:新客户端是 UTF-8,老客户端是 CP_ACP(默认 936)。
|
||||
// 不能像其它字段那样无脑 AnsiToUtf8——会把新客户端的 UTF-8 字节再当 GBK 双重编码。
|
||||
CString activeWindow = ctx->GetClientData(ONLINELIST_LOGINTIME);
|
||||
std::string activeWindowU8;
|
||||
if (!activeWindow.IsEmpty()) {
|
||||
UINT cp = GetClientEncoding(ctx);
|
||||
int wlen = MultiByteToWideChar(cp, 0, activeWindow, -1, NULL, 0);
|
||||
if (wlen > 1) {
|
||||
std::wstring w(wlen - 1, L'\0');
|
||||
MultiByteToWideChar(cp, 0, activeWindow, -1, &w[0], wlen);
|
||||
int u8len = WideCharToMultiByte(CP_UTF8, 0, w.c_str(), -1, NULL, 0, NULL, NULL);
|
||||
if (u8len > 1) {
|
||||
activeWindowU8.resize(u8len - 1);
|
||||
WideCharToMultiByte(CP_UTF8, 0, w.c_str(), -1, &activeWindowU8[0], u8len, NULL, NULL);
|
||||
}
|
||||
}
|
||||
}
|
||||
device["activeWindow"] = activeWindowU8;
|
||||
device["online"] = true;
|
||||
|
||||
// Add device group to response
|
||||
device["group"] = deviceGroup;
|
||||
|
||||
// Get screen info from client's reported resolution
|
||||
// Format: "n:MxN" where n=monitor count, M=width, N=height
|
||||
CString resolution = ctx->GetAdditionalData(RES_RESOLUTION);
|
||||
if (!resolution.IsEmpty()) {
|
||||
device["screen"] = AnsiToUtf8(resolution); // e.g. "2:3840x1080"
|
||||
}
|
||||
|
||||
CString clientType = ctx->GetAdditionalData(RES_CLIENT_TYPE);
|
||||
device["clientType"] = AnsiToUtf8(clientType); // e.g. "MAC", "LNX", "EXE"
|
||||
|
||||
// 单台主机序列化委托给公共函数 BuildHostJson(与 MCP 侧复用,
|
||||
// 字段与编码转换逻辑保持一致,见 docs/Mcp_Design.md §3.4 方案 C)。
|
||||
Json::Value device = BuildHostJson(ctx, m_pParentDlg->m_ClientMap);
|
||||
res["devices"].append(device);
|
||||
}
|
||||
LeaveCriticalSection(&m_pParentDlg->m_cs);
|
||||
|
||||
Reference in New Issue
Block a user