Feature: Add host management and connection management MCP tools

Expose the online-list TLS column through list_online_hosts, search_hosts
and get_host_detail. The value comes from the flag the client reports at
login, so old clients always read false.

Add list_network_connections and kill_network_connection on top of the
connection manager sub-connection protocol. The kill path matches the
original 110-byte record instead of rebuilding addresses, supports a dry
run, refuses UDP/IPv6 targets, and verifies that the quadruple really
disappeared instead of assuming success.

Add a host_* family backed by the machine manager protocol: ten read
tools covering every tab of the host management dialog, and four
recoverable write tools for process freeze/thaw, window restore/hide/
maximize/minimize, service start/stop/pause/continue/restart, and
scheduled task run/stop. Every write re-reads the snapshot before sending
and re-checks afterwards, reporting -32009 for stale targets rather than
acting blindly. The family is gated by McpMachine, the risky subset by
McpMachineDangerous, and writes additionally require read-only mode to
be off.

Route managed sub-connections from MessageHandle and OfflineProc, keep
the single-in-flight rule by sharing the pending table with a request
kind, and move the shared JSON/lock/encoding helpers into McpInternal so
both tool families use one implementation. Settings dialog, design docs
and en_US/zh_TW/de_DE translations are updated accordingly.

Co-Authored-By: deepseek-flash
This commit is contained in:
yuanyuanxianganddeepseek-flash committed 2026-10-21 20:01:41 +02:00
1 parent 6c8336b497
commit 38cd207f5d
17 files changed
+3615 -184

No files matched your search

+35 -4
View File
@@ -106,7 +106,7 @@ INT_PTR CMcpSettingsDlg::DoModal()
{
USES_CONVERSION;
CString title = _TR("MCP设置");
BuildDialogTemplate(m_Template, T2CW(title), 320, 420);
BuildDialogTemplate(m_Template, T2CW(title), 320, 480);
InitModalIndirect((LPCDLGTEMPLATE)m_Template.data());
return CDialog::DoModal();
}
@@ -148,6 +148,12 @@ BOOL CMcpSettingsDlg::OnInitDialog()
m_btnManage.Create(_TR("启用管理操作(进程/窗口/服务/电源)"),
WS_CHILD | WS_VISIBLE | WS_TABSTOP | BS_AUTOCHECKBOX,
r0, this, IDC_MCP_MANAGE);
m_btnMachine.Create(_TR("启用主机管理(进程/窗口/服务/任务/网络状态)"),
WS_CHILD | WS_VISIBLE | WS_TABSTOP | BS_AUTOCHECKBOX,
r0, this, IDC_MCP_MACHINE);
m_btnMachineDangerous.Create(_TR("启用危险主机操作(冻结/解冻进程、停止/重启服务)"),
WS_CHILD | WS_VISIBLE | WS_TABSTOP | BS_AUTOCHECKBOX,
r0, this, IDC_MCP_MACHINE_DANGEROUS);
m_lblWhitelist.Create(_TR("命令白名单"), WS_CHILD | WS_VISIBLE, r0, this, (UINT)-1);
m_editWhitelist.Create(WS_CHILD | WS_VISIBLE | WS_BORDER | WS_TABSTOP |
ES_MULTILINE | ES_AUTOVSCROLL | ES_WANTRETURN | WS_VSCROLL,
@@ -172,6 +178,8 @@ BOOL CMcpSettingsDlg::OnInitDialog()
m_btnRemoteControl.SendMessage(WM_SETFONT, (WPARAM)hFont, MAKELPARAM(TRUE, 0));
m_btnFileTransfer.SendMessage(WM_SETFONT, (WPARAM)hFont, MAKELPARAM(TRUE, 0));
m_btnManage.SendMessage(WM_SETFONT, (WPARAM)hFont, MAKELPARAM(TRUE, 0));
m_btnMachine.SendMessage(WM_SETFONT, (WPARAM)hFont, MAKELPARAM(TRUE, 0));
m_btnMachineDangerous.SendMessage(WM_SETFONT, (WPARAM)hFont, MAKELPARAM(TRUE, 0));
m_lblWhitelist.SendMessage(WM_SETFONT, (WPARAM)hFont, MAKELPARAM(TRUE, 0));
m_editWhitelist.SendMessage(WM_SETFONT, (WPARAM)hFont, MAKELPARAM(TRUE, 0));
m_btnOK.SendMessage(WM_SETFONT, (WPARAM)hFont, MAKELPARAM(TRUE, 0));
@@ -189,6 +197,8 @@ BOOL CMcpSettingsDlg::OnInitDialog()
int remoteControl = THIS_CFG.GetInt("settings", "McpRemoteControl", 0);
int fileTransfer = THIS_CFG.GetInt("settings", "McpFileTransfer", 0);
int manage = THIS_CFG.GetInt("settings", "McpManage", 0);
int machine = THIS_CFG.GetInt("settings", "McpMachine", 0);
int machineDangerous = THIS_CFG.GetInt("settings", "McpMachineDangerous", 0);
std::string whitelist = THIS_CFG.GetStr("settings", "McpCmdWhitelist", "");
m_btnEnable.SetCheck(enabled ? BST_CHECKED : BST_UNCHECKED);
@@ -200,6 +210,8 @@ BOOL CMcpSettingsDlg::OnInitDialog()
m_btnRemoteControl.SetCheck(remoteControl ? BST_CHECKED : BST_UNCHECKED);
m_btnFileTransfer.SetCheck(fileTransfer ? BST_CHECKED : BST_UNCHECKED);
m_btnManage.SetCheck(manage ? BST_CHECKED : BST_UNCHECKED);
m_btnMachine.SetCheck(machine ? BST_CHECKED : BST_UNCHECKED);
m_btnMachineDangerous.SetCheck(machineDangerous ? BST_CHECKED : BST_UNCHECKED);
// 白名单存储为逗号分隔,展示为每行一条。
m_editWhitelist.SetWindowText(CString(WhitelistForDisplay(whitelist).c_str()));
@@ -220,6 +232,16 @@ void CMcpSettingsDlg::OnOK()
bool remoteControl = (m_btnRemoteControl.GetCheck() == BST_CHECKED);
bool fileTransfer = (m_btnFileTransfer.GetCheck() == BST_CHECKED);
bool manage = (m_btnManage.GetCheck() == BST_CHECKED);
bool machine = (m_btnMachine.GetCheck() == BST_CHECKED);
bool machineDangerous = (m_btnMachineDangerous.GetCheck() == BST_CHECKED);
// 危险子集依附于主机管理总开关:没开主机管理就没有工具可放开,直接清除并提示。
if (machineDangerous && !machine) {
machineDangerous = false;
m_btnMachineDangerous.SetCheck(BST_UNCHECKED);
MessageBoxL(_TR("危险主机操作依附于主机管理:请先启用主机管理。"), _TR("提示"),
MB_ICONWARNING);
}
// 端口校验:1-65535
int port = atoi(CT2A(sPort));
@@ -247,14 +269,17 @@ void CMcpSettingsDlg::OnOK()
THIS_CFG.SetInt("settings", "McpRemoteControl", remoteControl ? 1 : 0);
THIS_CFG.SetInt("settings", "McpFileTransfer", fileTransfer ? 1 : 0);
THIS_CFG.SetInt("settings", "McpManage", manage ? 1 : 0);
THIS_CFG.SetInt("settings", "McpMachine", machine ? 1 : 0);
THIS_CFG.SetInt("settings", "McpMachineDangerous", machineDangerous ? 1 : 0);
std::string whitelist = CT2A(sWhitelist);
whitelist = NormalizeWhitelist(whitelist);
THIS_CFG.SetStr("settings", "McpCmdWhitelist", whitelist);
// 拆成两段可翻译的单行键,中间用 \r\n 连接(多行键无法在 INI 中表示)
// 拆成多段可翻译的单行键,中间用 \r\n 连接(多行键无法在 INI 中表示)
MessageBox(_TR("MCP 设置已保存。") + _T("\r\n") +
_TR("启用/端口/绑定地址/Token/只读/白名单/持久终端/远程控制/文件传输/管理操作的改动需重启程序生效。") + _T("\r\n") +
_TR("持久终端、远程控制与管理操作仅在只读模式关闭时生效。"),
_TR("启用/端口/绑定地址/Token/只读/白名单/持久终端/远程控制/文件传输/管理操作/主机管理/危险主机操作的改动需重启程序生效。") + _T("\r\n") +
_TR("持久终端、远程控制与管理操作仅在只读模式关闭时生效。") + _T("\r\n") +
_TR("主机管理的写操作需只读模式关闭;冻结/解冻进程、停止/重启服务还需启用危险主机操作。"),
_TR("提示"), MB_ICONINFORMATION);
CDialog::OnOK();
@@ -300,6 +325,12 @@ void CMcpSettingsDlg::LayoutControls(int cx, int cy)
m_btnManage.MoveWindow(margin, y, cx - margin * 2, 22);
y += 30;
m_btnMachine.MoveWindow(margin, y, cx - margin * 2, 22);
y += 30;
m_btnMachineDangerous.MoveWindow(margin, y, cx - margin * 2, 22);
y += 30;
const int whitelistH = 90;
m_lblWhitelist.MoveWindow(margin, y, labelW, rowH);
m_editWhitelist.MoveWindow(margin + labelW, y - 2, cx - margin * 2 - labelW, whitelistH);