Feature: MCP 远程控制(remote_open/close/keyboard/mouse/clipboard) #4
@@ -1136,10 +1136,13 @@ Json::Value BuildRemoteKeyboardInputSchema();
|
|||||||
Json::Value BuildRemoteKeyboardOutputSchema();
|
Json::Value BuildRemoteKeyboardOutputSchema();
|
||||||
Json::Value BuildRemoteMouseInputSchema();
|
Json::Value BuildRemoteMouseInputSchema();
|
||||||
Json::Value BuildRemoteMouseOutputSchema();
|
Json::Value BuildRemoteMouseOutputSchema();
|
||||||
|
Json::Value BuildRemoteClipboardInputSchema();
|
||||||
|
Json::Value BuildRemoteClipboardOutputSchema();
|
||||||
std::string BuildRemoteOpen(const Json::Value& id, const Json::Value& args, CMy2015RemoteDlg* parent);
|
std::string BuildRemoteOpen(const Json::Value& id, const Json::Value& args, CMy2015RemoteDlg* parent);
|
||||||
std::string BuildRemoteClose(const Json::Value& id, const Json::Value& args, CMy2015RemoteDlg* parent);
|
std::string BuildRemoteClose(const Json::Value& id, const Json::Value& args, CMy2015RemoteDlg* parent);
|
||||||
std::string BuildRemoteKeyboard(const Json::Value& id, const Json::Value& args, CMy2015RemoteDlg* parent);
|
std::string BuildRemoteKeyboard(const Json::Value& id, const Json::Value& args, CMy2015RemoteDlg* parent);
|
||||||
std::string BuildRemoteMouse(const Json::Value& id, const Json::Value& args, CMy2015RemoteDlg* parent);
|
std::string BuildRemoteMouse(const Json::Value& id, const Json::Value& args, CMy2015RemoteDlg* parent);
|
||||||
|
std::string BuildRemoteClipboard(const Json::Value& id, const Json::Value& args, CMy2015RemoteDlg* parent);
|
||||||
|
|
||||||
// tools/list
|
// tools/list
|
||||||
std::string BuildToolsListResult(const Json::Value& id) {
|
std::string BuildToolsListResult(const Json::Value& id) {
|
||||||
@@ -1379,6 +1382,14 @@ std::string BuildToolsListResult(const Json::Value& id) {
|
|||||||
tool["outputSchema"] = BuildRemoteMouseOutputSchema();
|
tool["outputSchema"] = BuildRemoteMouseOutputSchema();
|
||||||
tools.append(tool);
|
tools.append(tool);
|
||||||
}
|
}
|
||||||
|
{
|
||||||
|
Json::Value tool(Json::objectValue);
|
||||||
|
tool["name"] = "remote_clipboard";
|
||||||
|
tool["description"] = u8"把文本写入远程主机的剪贴板(UTF-8 → GBK/ANSI,非 GBK 字符如 emoji 会丢失)。仅设置剪贴板,粘贴需随后用 remote_keyboard 注入 Ctrl+V。";
|
||||||
|
tool["inputSchema"] = BuildRemoteClipboardInputSchema();
|
||||||
|
tool["outputSchema"] = BuildRemoteClipboardOutputSchema();
|
||||||
|
tools.append(tool);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
result["tools"] = tools;
|
result["tools"] = tools;
|
||||||
@@ -3408,6 +3419,110 @@ std::string BuildRemoteMouse(const Json::Value& id, const Json::Value& args, CMy
|
|||||||
return BuildResult(id, result);
|
return BuildResult(id, result);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ===== P5:remote_clipboard(写远程剪贴板,UTF-8 → GBK 直发,见设计 §6.4)=====
|
||||||
|
|
||||||
|
Json::Value BuildRemoteClipboardInputSchema() {
|
||||||
|
Json::Value props(Json::objectValue);
|
||||||
|
Json::Value idProp(Json::objectValue);
|
||||||
|
idProp["type"] = "string";
|
||||||
|
idProp["description"] = u8"主机 id,取 list_online_hosts / search_hosts 返回的 id 字段";
|
||||||
|
props["id"] = idProp;
|
||||||
|
|
||||||
|
Json::Value sid(Json::objectValue);
|
||||||
|
sid["type"] = "string";
|
||||||
|
sid["description"] = u8"remote_open 返回的 session_id";
|
||||||
|
props["session_id"] = sid;
|
||||||
|
|
||||||
|
Json::Value text(Json::objectValue);
|
||||||
|
text["type"] = "string";
|
||||||
|
text["description"] = u8"要写入剪贴板的文本(UTF-8;非 GBK 字符如 emoji 会丢失)";
|
||||||
|
props["text"] = text;
|
||||||
|
|
||||||
|
Json::Value schema(Json::objectValue);
|
||||||
|
schema["type"] = "object";
|
||||||
|
schema["properties"] = props;
|
||||||
|
Json::Value required(Json::arrayValue);
|
||||||
|
required.append("id");
|
||||||
|
required.append("session_id");
|
||||||
|
required.append("text");
|
||||||
|
schema["required"] = required;
|
||||||
|
return schema;
|
||||||
|
}
|
||||||
|
|
||||||
|
Json::Value BuildRemoteClipboardOutputSchema() {
|
||||||
|
Json::Value props(Json::objectValue);
|
||||||
|
Json::Value schema(Json::objectValue);
|
||||||
|
schema["type"] = "object";
|
||||||
|
schema["properties"] = props;
|
||||||
|
return schema;
|
||||||
|
}
|
||||||
|
|
||||||
|
// tools/call:remote_clipboard(写远程剪贴板;成功返回空对象)
|
||||||
|
std::string BuildRemoteClipboard(const Json::Value& id, const Json::Value& args, CMy2015RemoteDlg* parent) {
|
||||||
|
CMcpServer& mcp = CMcpServer::Instance();
|
||||||
|
mcp.SweepIdleScreenCtrl(kScreenCtrlIdleTimeoutSec);
|
||||||
|
|
||||||
|
uint64_t devId = 0;
|
||||||
|
context* ctx = nullptr;
|
||||||
|
std::string errJson;
|
||||||
|
if (!ResolveScreenCtrlSessionHost(id, args, parent, devId, ctx, errJson))
|
||||||
|
return errJson;
|
||||||
|
|
||||||
|
std::string sessionId = GetStringArg(args, "session_id");
|
||||||
|
if (sessionId.empty())
|
||||||
|
return BuildError(id, -32602, "Missing required parameter: session_id");
|
||||||
|
|
||||||
|
std::string text = GetStringArg(args, "text");
|
||||||
|
if (text.empty())
|
||||||
|
return BuildError(id, -32602, "Missing required parameter: text");
|
||||||
|
if (text.size() > 262144) // 256 KB 上限(CF_TEXT 单包,防内存/传输失控)
|
||||||
|
return BuildError(id, -32602, "text too long (max 262144 bytes)");
|
||||||
|
|
||||||
|
// UTF-8 → GBK:客户端 UpdateClientClipboard 以 CF_TEXT(ANSI/GBK)落剪贴板(设计 §6.4)。
|
||||||
|
// 非 GBK 字符(emoji 等)在此被替换为 '?',MVP 已知限制。
|
||||||
|
std::string ansi = ToAnsi(text, 936);
|
||||||
|
|
||||||
|
// 校验 session_id/busy 并置 busy,取屏幕子连接(注入与剪贴板共用同一子连接串行化)。
|
||||||
|
context* subCtx = nullptr;
|
||||||
|
int screenW = 0, screenH = 0;
|
||||||
|
int r = mcp.BeginScreenCtrlAction(devId, sessionId, subCtx, screenW, screenH);
|
||||||
|
if (r == 2) return BuildError(id, -32003, "Device busy: another injection is in flight for this session");
|
||||||
|
if (r == 1) return BuildError(id, -32002, "Remote control session not found, not ready, or closed: " + sessionId);
|
||||||
|
|
||||||
|
// 组包 [COMMAND_SCREEN_SET_CLIPBOARD][GBK text],经屏幕子连接发送(客户端自行补 '\0')。
|
||||||
|
const int len = (int)(1 + ansi.size());
|
||||||
|
std::vector<BYTE> packet((size_t)len);
|
||||||
|
packet[0] = COMMAND_SCREEN_SET_CLIPBOARD;
|
||||||
|
memcpy(packet.data() + 1, ansi.data(), ansi.size());
|
||||||
|
bool ok = subCtx->Send2Client(packet.data(), (ULONG)len) != FALSE;
|
||||||
|
|
||||||
|
mcp.EndScreenCtrlAction(devId, sessionId);
|
||||||
|
|
||||||
|
if (!ok)
|
||||||
|
return BuildError(id, -32004, "Failed to send clipboard text");
|
||||||
|
|
||||||
|
// 审计(不可关闭;记字节数,不落全文以免审计日志膨胀)
|
||||||
|
if (parent) {
|
||||||
|
std::string audit = "host " + std::to_string(devId) + " remote-clipboard: session " + sessionId
|
||||||
|
+ " input_bytes=" + std::to_string(text.size());
|
||||||
|
parent->PostMessageA(WM_SHOWERRORMSG,
|
||||||
|
(WPARAM)new CString(ToAnsi(audit, 936).c_str()),
|
||||||
|
(LPARAM)new CString(_TR("MCP远程控制")));
|
||||||
|
}
|
||||||
|
|
||||||
|
Json::Value result(Json::objectValue);
|
||||||
|
result["structuredContent"] = Json::Value(Json::objectValue);
|
||||||
|
Json::Value content(Json::arrayValue);
|
||||||
|
Json::Value item(Json::objectValue);
|
||||||
|
item["type"] = "text";
|
||||||
|
item["text"] = std::string(u8"ok");
|
||||||
|
content.append(item);
|
||||||
|
result["content"] = content;
|
||||||
|
result["isError"] = false;
|
||||||
|
|
||||||
|
return BuildResult(id, result);
|
||||||
|
}
|
||||||
|
|
||||||
// tools/call 分派
|
// tools/call 分派
|
||||||
std::string BuildToolsCall(const Json::Value& root, CMy2015RemoteDlg* parent) {
|
std::string BuildToolsCall(const Json::Value& root, CMy2015RemoteDlg* parent) {
|
||||||
const Json::Value& id = root["id"];
|
const Json::Value& id = root["id"];
|
||||||
@@ -3440,6 +3555,7 @@ std::string BuildToolsCall(const Json::Value& root, CMy2015RemoteDlg* parent) {
|
|||||||
if (toolName == "remote_close") return BuildRemoteClose(id, args, parent);
|
if (toolName == "remote_close") return BuildRemoteClose(id, args, parent);
|
||||||
if (toolName == "remote_keyboard") return BuildRemoteKeyboard(id, args, parent);
|
if (toolName == "remote_keyboard") return BuildRemoteKeyboard(id, args, parent);
|
||||||
if (toolName == "remote_mouse") return BuildRemoteMouse(id, args, parent);
|
if (toolName == "remote_mouse") return BuildRemoteMouse(id, args, parent);
|
||||||
|
if (toolName == "remote_clipboard") return BuildRemoteClipboard(id, args, parent);
|
||||||
|
|
||||||
return BuildError(id, -32602,
|
return BuildError(id, -32602,
|
||||||
"Unknown tool: " + (toolName.empty() ? std::string("(empty)") : toolName));
|
"Unknown tool: " + (toolName.empty() ? std::string("(empty)") : toolName));
|
||||||
|
|||||||
Reference in New Issue
Block a user