Feature: Add exec_command MCP tool
Add a one-shot exec_command MCP tool that runs a command on a remote Windows host and returns stdout plus exit code, reusing the Web terminal link (main-connection COMMAND_SHELL, a shell sub-connection, and a sentinel command line located via rfind to tolerate ConPTY echo). The sentinel marker is embedded in the command line ConPTY echoes back, so it is only treated as hit when it starts a line (preceded by a newline or the buffer start); this keeps the echoed marker from being mistaken for the real sentinel when the echo packet arrives before the output. Execution is gated at Web remote-desktop sensitivity: a read-only mode (McpReadonly, default on, hides the tool) and a command whitelist (McpCmdWhitelist) with built-in read-only prefixes. Shell metacharacters (& | < > ^) are rejected before whitelist matching, and each execution is recorded in the server audit log. Extend the MCP settings dialog with the read-only checkbox and a multi-line whitelist box (commas and newlines both accepted, normalized to a comma-separated list on save), and add English and Traditional Chinese mappings for the new UI and audit-log strings. Co-Authored-By: deepseek-v4-pro
This commit is contained in:
@@ -2190,6 +2190,9 @@ BOOL CMy2015RemoteDlg::OnInitDialog()
|
||||
|
||||
McpServer().SetParentDlg(this);
|
||||
McpServer().SetToken(mcpToken);
|
||||
// 安全配置:只读默认 1(exec_command 默认禁用);白名单空则用内置只读前缀。
|
||||
McpServer().SetReadonly(THIS_CFG.GetInt("settings", "McpReadonly", 1) != 0);
|
||||
McpServer().SetCmdWhitelist(THIS_CFG.GetStr("settings", "McpCmdWhitelist", ""));
|
||||
if (!McpServer().Start(mcpBind, mcpPort)) {
|
||||
Mprintf("McpServer start failed on %s:%d\n", mcpBind.c_str(), mcpPort);
|
||||
} else {
|
||||
@@ -5505,6 +5508,18 @@ VOID CMy2015RemoteDlg::MessageHandle(CONTEXT_OBJECT* ContextObject)
|
||||
return;
|
||||
}
|
||||
|
||||
// ===== MCP exec_command 的 shell 子上下文:被 MCP 接管时,数据走 OnTerminalData =====
|
||||
// 与上方 Web 终端同构:MCP 接管的 shell 子上下文不开 MFC 对话框,hDlg 为 NULL,
|
||||
// 每个数据包都落到 MessageHandle 顶部分支,转发给 McpServer 做哨兵检测。
|
||||
if (McpServer().IsTerminalContext(ContextObject)) {
|
||||
if (len == 1 && cmd == TOKEN_TERMINAL_CLOSE) {
|
||||
McpServer().OnTerminalClosed(ContextObject);
|
||||
} else {
|
||||
McpServer().OnTerminalData(ContextObject, szBuffer, len);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// 【L】:主机上下线和授权
|
||||
// 【x】:对话框相关功能
|
||||
switch (cmd) {
|
||||
@@ -6408,6 +6423,11 @@ VOID CMy2015RemoteDlg::MessageHandle(CONTEXT_OBJECT* ContextObject)
|
||||
// hDlg 留 NULL:后续数据继续走 MessageHandle 顶部的 IsTerminalContext 分支
|
||||
break;
|
||||
}
|
||||
// MCP exec_command 触发的 shell 子上下文:交给 McpServer 接管(老 cmd 管道,GBK)。
|
||||
if (McpServer().IsTermPending(devId)) {
|
||||
McpServer().RegisterTerminalContext(devId, ContextObject, /*isPty*/false);
|
||||
break;
|
||||
}
|
||||
g_2015RemoteDlg->SendMessage(WM_OPENSHELLDIALOG, 0, (LPARAM)ContextObject);
|
||||
break;
|
||||
}
|
||||
@@ -6418,6 +6438,11 @@ VOID CMy2015RemoteDlg::MessageHandle(CONTEXT_OBJECT* ContextObject)
|
||||
WebService().RegisterTerminalContext(devId, ContextObject, /*isPty*/true);
|
||||
break;
|
||||
}
|
||||
// MCP exec_command 触发的 PTY 子上下文:交给 McpServer 接管(ConPTY,UTF-8)。
|
||||
if (McpServer().IsTermPending(devId)) {
|
||||
McpServer().RegisterTerminalContext(devId, ContextObject, /*isPty*/true);
|
||||
break;
|
||||
}
|
||||
|
||||
// 三个前置条件,缺任何一个都回退到经典终端,并把原因贴到信息列表。
|
||||
// SYSTEM 场景:WebView2 不支持 LocalSystem token,会出现"窗口能弹但页面空白",
|
||||
|
||||
Reference in New Issue
Block a user