Files
SimpleRemoter/docs/Mcp_Design.md
yuanyuanxiang 13052b7cae 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
2026-08-16 14:37:11 +02:00

23 KiB
Raw Blame History

YAMA 服务端 MCP 支持方案

版本1.0 状态Final定稿 适用范围:server/2015Remote/C++/MFC 服务端,生产主流)


1. 背景与目标

1.1 背景

MCPModel Context Protocol是 Anthropic 提出的开放协议,让 AI 助手Claude Code、Claude Desktop 等)通过标准化的工具调用接口访问外部系统的能力。

YAMA 服务端维护了「在线主机列表」这一核心数据,如果能通过 MCP 暴露出去,用户就能在 Claude Code 里直接问「现在有多少台机器在线?」「列出所有在线的 Windows 主机」等,由 AI 调用工具获取实时数据。

1.2 本期范围MVP

只跑通 MCP只实现一个工具,把可扩展架构搭起来:

  • 传输方式Streamable HTTPPOST /mcpJSON-RPC 2.0 over HTTP
  • 工具数量1 个 —— list_online_hosts(获取在线主机列表)
  • 暴露范围:启用开关(默认关闭,经「扩展 → MCP设置」对话框开启+ 绑定地址可配置(默认 127.0.0.1 仅本机)+ token 认证env / THIS_CFG / 随机)
  • 后续:逐步添加工具(进程列表、命令执行、屏幕截图等),架构不动

1.3 非目标(明确不做)

  • 不做 SSE 服务器推送(本期工具调用是 request/response 即可)
  • 不做多用户/角色体系(复用现有 WebService 认证或静态 token
  • 不做写操作工具(操控主机的工具属于后续阶段,需单独评审安全边界)
  • 不碰现有 Web 控制台的 WebSocket 服务

2. 现状分析

C++ 服务端已有大量可直接复用的资产MCP 的落地成本因此大幅降低。

现成资产 位置 在 MCP 中的角色
HTTP 服务器 httplib.hheader-onlyfile_server.h:34 已用) MCP 的 HTTP 传输层
JSON 库 jsoncpp/json.hWebService.cpp 已用) JSON-RPC 编解码
Token 认证 WebServiceAuth.hGenerateToken / ValidateToken / ComputeSHA256 可选升级路径MVP 用简单静态 token 比对(见 §3.5
在线主机列表 → JSON CWebService::BuildDeviceListJson()WebService.cpp:1506 工具 list_online_hosts 的核心逻辑
在线主机数据源 CMy2015RemoteDlg::m_HostList2015RemoteDlg.h:345m_cs 保护)+ context::IsLogin() + context::GetClientData() 取数入口
配置系统 THIS_CFG.GetInt/GetStr("settings", ...) MCP 端口/token 配置

2.1 关键结论

  • HTTP、JSON、认证、主机列表 JSON 全部现成MCP 侧不需要重新造轮子。
  • 唯一需要手写的是 MCP 的 JSON-RPC 2.0 协议层,因为 C++ 没有官方 MCP SDK官方仅有 TS/Python/Java/Kotlin/C#/Go

2.2 BuildDeviceListJson 现状

WebService.cpp:1506CWebService::BuildDeviceListJson(const std::string& username) 已实现:

  • 遍历 m_pParentDlg->m_HostList,过滤 ctx->IsLogin()
  • 输出字段:id / name / remark / ip / os / location / rtt / version / activeWindow / online / group / screen / clientType
  • 已处理 GBK/UTF-8 编码分叉(activeWindow 字段按客户端能力位选择编码页,见 WebService.cpp:1581 附近的 GetClientEncoding

该方法是 privateWebService.h:160,位于 private 段。MCP 侧需通过公开入口复用(见 §3.4)。


3. 方案设计

3.1 总体架构

2015Remote.exeMFC 服务端进程)
├─ IOCP TCP 服务(被控端连接,现有)
├─ WebServicews::ServerWeb 控制台现有8080
└─ McpServerhttplib新增默认禁用经「MCP设置」开启后监听 127.0.0.1:6544
      POST /mcp → JSON-RPC 分发
                    ├─ initialize
                    ├─ tools/list
                    └─ tools/call
                          └─ list_online_hosts → 复用 BuildHostJson公共函数§3.4

核心决策:独立起一个 httplib server不挂到现有 ws::Server

理由:

  • 现有 ws::ServerHttpHandler 只有 path 参数、无 POST bodySimpleWebSocket.h:345),强挂 MCP 需改动其底层,侵入大、有回归风险。
  • 独立 server 零耦合、零风险,且天然满足「仅 127.0.0.1」的绑定需求。

3.2 新增文件清单

文件 职责 估算量
McpServer.h / McpServer.cpp httplib 封装、POST /mcp 路由、token 校验、生命周期 ~120 行
McpProtocol.h / McpProtocol.cpp JSON-RPC 2.0 协议层:initialize / tools/list / tools/call 分发 ~250 行
McpTools.h / McpTools.cpp 工具注册表 + list_online_hosts 实现 ~80 行
McpSettingsDlg.h / McpSettingsDlg.cpp 「MCP设置」配置对话框启用/端口/绑定/token + 重启提醒) ~150 行
修改 2015RemoteDlg.cpp / .h 启动/停止 McpServer + 菜单项接线(ID_MCP_SETTINGSOnMcpSettings ~30 行
修改 2015Remote.rc / resource.h / UIBranding.h / FeatureFlags.h ID_MCP_SETTINGS 菜单项 + IDD_DIALOG_MCP_SETTINGS 对话框资源 + HIDE_MENU_MCP_SETTINGS + MF_MCP_SETTINGS 许可位 少量

头文件/实现可合并精简,实际以「一个 McpServer 类 + 一个工具注册表 + 一个协议分发函数」为最小形态,文件拆分仅为可读性。

3.3 协议层设计JSON-RPC 2.0

MCP 是 JSON-RPC 2.0。MVP 只需实现以下方法:

3.3.1 initialize(握手)

请求:

{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2025-06-18","capabilities":{},"clientInfo":{"name":"claude","version":"..."}}}

响应:

{"jsonrpc":"2.0","id":1,"result":{
  "protocolVersion":"2025-06-18",
  "capabilities":{"tools":{}},
  "serverInfo":{"name":"yama","version":"<VERSION_STR>"}
}}

3.3.2 tools/list

请求:{"jsonrpc":"2.0","id":2,"method":"tools/list","params":{}}

响应:

{"jsonrpc":"2.0","id":2,"result":{"tools":[{
  "name":"list_online_hosts",
  "description":"获取当前所有在线主机的列表包含计算机名、IP、操作系统、版本、备注、分组、活动窗口、延迟等实时信息。",
  "inputSchema":{"type":"object","properties":{},"required":[]},
  "outputSchema":{
    "type":"object",
    "properties":{
      "hosts":{
        "type":"array",
        "items":{
          "type":"object",
          "properties":{
            "id":{"type":"string"},
            "name":{"type":"string"},
            "remark":{"type":"string"},
            "ip":{"type":"string"},
            "os":{"type":"string"},
            "location":{"type":"string"},
            "rtt":{"type":"string"},
            "version":{"type":"string"},
            "activeWindow":{"type":"string"},
            "online":{"type":"boolean"},
            "group":{"type":"string"},
            "screen":{"type":"string"},
            "clientType":{"type":"string"}
          }
        }
      }
    },
    "required":["hosts"]
  }
}]}}

3.3.3 tools/call

请求:

{"jsonrpc":"2.0","id":3,"method":"tools/call","params":{"name":"list_online_hosts","arguments":{}}}

响应(结构化输出:structuredContent 承载主机数组,content 附带可读摘要):

{"jsonrpc":"2.0","id":3,"result":{
  "structuredContent":{
    "hosts":[
      {"id":"123","name":"DESKTOP-ABC","remark":"我的Windows","ip":"192.168.0.92",
       "os":"Windows 10","location":"上海市","rtt":"12","version":"...",
       "activeWindow":"WeChat","online":true,"group":"default","screen":"1:1920x1080","clientType":"EXE"}
    ]
  },
  "content":[{"type":"text","text":"共 1 台主机在线。"}],
  "isError":false
}}

3.3.4 可选实现

  • ping:返回空 result便于健康检查
  • notifications/initialized:客户端通知,可忽略

3.3.5 错误响应JSON-RPC 2.0 标准)

协议层必须处理以下错误,返回标准 error 结构(id 回填请求 id

场景 code message
请求体不是合法 JSON -32700 Parse error
请求结构不合法(缺 jsonrpc/method 等) -32600 Invalid Request
方法未实现 -32601 Method not found
参数非法 -32602 Invalid params
内部错误 -32603 Internal error

示例(方法未实现):

{"jsonrpc":"2.0","id":1,"error":{"code":-32601,"message":"Method not found"}}

3.3.6 传输层约定Streamable HTTP 最小子集)

  • 客户端 POST /mcp,请求头 Content-Type: application/jsonAccept: application/json, text/event-stream
  • 服务端返回 Content-Type: application/jsonMVP 工具调用为 request/response无需 SSE 流式)。
  • initialize 响应可回填 Mcp-Session-Id 头用于会话管理MVP 单会话可省略,实现时对照最新 MCP 规范确认。
  • 仅接受 POST /mcp,其余路径/方法返回 404/405。

3.4 工具设计:list_online_hosts

取数方案(已定:抽公共函数)

BuildDeviceListJson 目前是 CWebService 的 private 方法,且依赖 m_pParentDlg。为满足「MCP 与 WebService 是平级数据消费者、不互相依赖」的原则,采用方案 C抽公共函数

// 公共函数:提取单台在线主机的完整数据(含 GBK/UTF-8 编码处理),返回 UTF-8 的 Json::Value
Json::Value BuildHostJson(context* ctx, _ClientList* clientMap);
  • CWebService::BuildDeviceListJson 内部改调该公共函数Web 侧签名与输出不变(零回归)。
  • McpServer 侧持 CMy2015RemoteDlg*SetParentDlg),在 m_cs 锁内遍历 m_HostListIsLogin() 过滤,对每台主机调 BuildHostJson,组装为结构化输出。
  • 职责边界:用户名/分组过滤(usernameallowed_groups)、m_cs 加锁、以及外层结构Web 的 {"cmd","devices"} vs MCP 的 {"hosts"})属于调用方,不进公共函数;BuildHostJson 只负责「单台主机 → Json::Value」的字段序列化与编码转换。其中 remark 依赖 m_ClientMap->GetClientMapData(id, MAP_NOTE),故 clientMap 作为参数传入。

选型说明:方案 AMCP 依赖 CWebService 单例)依赖方向错误,且 WebSvrPort=0m_pParentDlg 为空导致取不到数;方案 BMCP 自持父对话框复制逻辑)违反 DRY、重复易错的编码处理。故取 C。

返回内容(全量,含实时信息)

保留 BuildDeviceListJson 的全部字段,不做精简以支持「AI 协助监督客户端运行状态」的场景:

  • 基础标识:id / name / remark / ip / os / location / group / version / clientType / screen
  • 实时信息:rtt(延迟)、activeWindow(当前活动窗口,即被控端正在做什么)

输出结构 {"hosts": [...]},每台主机一个对象,字段与 §3.3.2 的 outputSchema 一致。

3.5 安全设计

  • 绑定地址:默认 127.0.0.1(仅本机回环,其他机器无法访问);可经 McpBind 配置为 0.0.0.0(所有网卡)或内网 IP192.168.0.92)以支持远程调用。
    • 非回环绑定时 token 经明文 HTTP 传输,需强 token并建议配合防火墙限制源 IP 或启用 HTTPS项目已有 WebHTTPS 实践)。
  • 静态 token 校验:每个 POST /mcp 请求校验 Authorization: Bearer <token> header不匹配返回 HTTP 401。
  • token 来源(优先顺序):环境变量 YAMA_MCP_TOKENTHIS_CFGMcpToken → 两者皆空则随机生成
    • 前两者任一非空即作为固定 token仅当两者皆空时才随机生成仅本次进程运行期间有效进程重启后重新生成不同的 token
    • 随机生成时必须将 token 输出到日志Mprintf),否则用户无从获知、无法在 Claude Code 侧配置。
    • 环境变量名 YAMA_MCP_TOKEN 建议同 BRAND_WEB_ENV_VAR 一样在 UIBranding.h 定义为宏(如 BRAND_MCP_ENV_VAR),避免散落硬编码字符串。
  • 只读边界MVP 仅一个只读工具,不暴露任何写/操控能力。
  • HTTP 方法限制:仅接受 POST /mcp,其余返回 404/405。

3.6 配置设计

沿用现有 THIS_CFGsettings 节):

配置键 默认值 说明
McpEnabled 0 MCP 总开关:0 = 禁用(默认),1 = 启用。经「扩展 → MCP设置」对话框设置
McpPort 6544 MCP 监听端口;仅当 McpEnabled=1 时生效;未配置用默认 6544
McpBind 127.0.0.1 监听地址;默认仅本机;可设 0.0.0.0(所有网卡)或内网 IP192.168.0.92)以支持远程
McpToken UI 默认随机值) 静态 token对话框内「token 必填、默认随机值」——首次打开若为空则预填随机并随保存持久化。运行时优先级env YAMA_MCP_TOKENMcpToken → 皆空则随机生成(见 §3.5

与旧版「McpPort=0 即禁用」不同:启用与否改由独立的 McpEnabled 开关控制,McpPort 回归纯端口语义,二者解耦。

3.7 生命周期接入

2015RemoteDlg.cpp Web 服务启动块(约 2048-2081)之后,新增 MCP 启动逻辑。McpServerCWebService 同为单例:提供 static McpServer& Instance() 与全局 inline McpServer& McpServer() 访问器(仿 WebService(),见 WebService.h 末尾的全局访问器写法)。

auto mcpEnabled = THIS_CFG.GetInt("settings", "McpEnabled", 0);   // 默认禁用
auto mcpPort    = THIS_CFG.GetInt("settings", "McpPort", 6544);
auto mcpBind    = THIS_CFG.GetStr("settings", "McpBind", "127.0.0.1");
if (mcpEnabled) {
    // token 优先顺序env YAMA_MCP_TOKEN → THIS_CFG McpToken → 皆空则随机(仅本次进程有效)
    const char* envTok = getenv("YAMA_MCP_TOKEN");
    std::string mcpToken;
    if (envTok && *envTok) {
        mcpToken = envTok;
    } else {
        mcpToken = THIS_CFG.GetStr("settings", "McpToken", "");
    }
    if (mcpToken.empty()) {
        mcpToken = GenerateRandomToken();  // 32 hex 随机串
        Mprintf("[McpServer] YAMA_MCP_TOKEN / McpToken 均未设置,本次进程随机 token%s\n", mcpToken.c_str());
    }
    McpServer().SetParentDlg(this);   // 方案 CMCP 需遍历 m_HostList须持父对话框指针
    McpServer().SetToken(mcpToken);
    if (!McpServer().Start(mcpBind, mcpPort)) {
        Mprintf("McpServer start failed on %s:%d\n", mcpBind.c_str(), mcpPort);
    }
}

并在退出路径(OnDestroy / ExitInstance 对应位置)调用 McpServer().Stop()

3.8 「MCP设置」配置对话框

MCP 默认不启用,通过主对话框「扩展 → MCP设置」菜单项打开配置对话框来开启。菜单项位置2015Remote.rc 扩展菜单(POPUP "扩展(&X)")内、「地理信息(&L)」子菜单(纯真数据库 / IP2RegionEND 之后、「插件设置」之前。

3.8.1 菜单与资源接线

  1. resource.h:新增命令 ID取当前空闲段经查 33077 空闲):
    #define ID_MCP_SETTINGS   33077
    
  2. 2015Remote.rc:在「地理信息(&L)」子菜单 END 之后、插件设置 之前插入:
    MENUITEM "MCP设置(&M)...",                  ID_MCP_SETTINGS
    
    并新增对话框资源 IDD_DIALOG_MCP_SETTINGS(「启用 MCP」复选框 + 端口/绑定地址/token 三个编辑框 + 确定/取消按钮)。
  3. UIBranding.h:扩展菜单隐藏开关新增一行(默认 0 显示):
    #define HIDE_MENU_MCP_SETTINGS   0   // MCP设置
    
  4. FeatureFlags.h:新增运行时许可位,占用 MenuFlags 保留段 [43-63] 的首位(紧跟 MF_REQUEST_AUTH 之后):
    #define MF_MCP_SETTINGS       (1ULL << 43)  // HIDE_MENU_MCP_SETTINGS
    
  5. 2015RemoteDlg.cpp#include "McpSettingsDlg.h",然后消息映射 + 处理器 + 剪枝:
    ON_COMMAND(ID_MCP_SETTINGS, &CMy2015RemoteDlg::OnMcpSettings)
    // ...
    void CMy2015RemoteDlg::OnMcpSettings()
    {
        CMcpSettingsDlg dlg(this);
        dlg.DoModal();
    }
    
    并在扩展菜单剪枝块(pExtMenu,约 2015RemoteDlg.cpp:1225 之后)追加:
    if (SHOULD_HIDE_MENU(HIDE_MENU_MCP_SETTINGS, MF_MCP_SETTINGS))
        pExtMenu->DeleteMenu(ID_MCP_SETTINGS, MF_BYCOMMAND);
    

实现注意2015Remote.rc 为 UTF-16 编码,菜单与对话框资源须由 VS 资源编辑器维护(手工改文本易破坏编码与资源 ID 引用)。IDD_DIALOG_MCP_SETTINGS 需在 resource.h 分配独立空闲的 IDD_ 数值(与 ID_MCP_SETTINGS 分开);对话框内控件 IDIDC_CHK_ENABLE_MCP / IDC_EDIT_MCP_PORT / IDC_EDIT_MCP_BIND / IDC_EDIT_MCP_TOKEN)同理。

3.8.2 对话框字段与默认值

控件 绑定配置键 默认值 说明
「启用 MCP」复选框 McpEnabled 未勾选(0 勾选后持久化 McpEnabled=1
端口 McpPort 6544 数字编辑框,仅当启用时有效
绑定地址 McpBind 127.0.0.1 默认仅本机;可改 0.0.0.0 / 内网 IP
Token McpToken 随机值 必填;为空时预填随机 token

3.8.3 初始化与保存

打开时读 THIS_CFG 回填token 为空则当场生成随机值预填32 hex。保存时落盘并弹重启提醒

BOOL CMcpSettingsDlg::OnInitDialog()
{
    CDialogLangEx::OnInitDialog();
    int enabled  = THIS_CFG.GetInt("settings", "McpEnabled", 0);
    int port     = THIS_CFG.GetInt("settings", "McpPort", 6544);
    std::string bind = THIS_CFG.GetStr("settings", "McpBind", "127.0.0.1");
    std::string tok  = THIS_CFG.GetStr("settings", "McpToken", "");
    if (tok.empty()) tok = GenerateRandomToken();   // 预填随机,随保存持久化
    // 回填到控件m_bEnabled / m_nPort / m_strBind / m_strToken
    return TRUE;
}

void CMcpSettingsDlg::OnBnClickedBtnSave()
{
    // 从控件收集enabled / port / bind / token
    if (enabled && token.empty()) {
        MessageBoxL(_TR("Token 不能为空"), _TR("提示"), MB_ICONWARNING);
        return;
    }
    THIS_CFG.SetInt("settings", "McpEnabled", enabled ? 1 : 0);
    THIS_CFG.SetInt("settings", "McpPort", port);
    THIS_CFG.SetStr("settings", "McpBind", bind);
    THIS_CFG.SetStr("settings", "McpToken", token);
    MessageBoxL(_TR("MCP 设置已保存。\n启用/端口/绑定地址/Token 的改动需重启程序生效。"),
                _TR("提示"), MB_ICONINFORMATION);
    CDialogLangEx::OnOK();
}

3.8.4 与运行时 token 逻辑的衔接

对话框的「默认随机 token」是把随机值写入 McpToken 并持久化,于是下次启动走 env → McpToken 的固定 token 分支;运行时「两者皆空则随机」分支仅在用户从未通过对话框设置过(或手动清空 token时兜底两者不冲突。GenerateRandomToken() 为 MCP 侧新增的小工具函数32 hex 随机串,可用 rand_s / BCryptGenRandom 实现;如需可复用 WebServiceAuth.hComputeSHA256 派生)。


4. 实施步骤

步骤 内容 验收
1 新建 McpProtocolJSON-RPC 分发 + initialize/tools/list/tools/call(含 jsoncpp 解析/序列化) 单元自测:手写请求串,验证响应
2 新建 McpServerhttplib 起 127.0.0.1:6544POST /mcp 路由 + token 校验(含随机生成) curl 请求返回 401/正常
3 抽公共函数 BuildHostJsonCWebService::BuildDeviceListJson 改调之 Web 输出不变,零回归
4 注册 list_online_hosts 工具handler 持父对话框、m_cs 锁内遍历、调 BuildHostJson 组装 tools/call 返回主机列表
5 新增 McpSettingsDlg + 菜单项接线(ID_MCP_SETTINGS / OnMcpSettings / HIDE_MENU_MCP_SETTINGS / IDD_DIALOG_MCP_SETTINGS 资源) 对话框可启用/禁用、配置端口/绑定/token保存落盘并提示重启
6 接入 2015RemoteDlg 启动/停止(按 McpEnabled 门控)+ 配置 服务端启动后端口监听正常
7 Claude Code 接入验证(见 §5 见验收标准

5. 验收标准

  1. 在「MCP设置」对话框启用并重启服务端后127.0.0.1:6544 监听正常,外部地址无法访问;未启用时该端口不监听。
  2. 无 token 的请求返回 HTTP 401错误 token 返回 401正确 token 通过。
  3. curl 模拟 initializetools/listtools/call 全流程,返回符合 JSON-RPC 2.0 与 MCP 规范。
  4. Claude Code 侧:
    claude mcp add yama --transport http http://127.0.0.1:6544/mcp -H "Authorization: Bearer <token>"
    
    claude mcp list 能看到 yama,在对话中问「列出在线主机」能正确触发工具并返回列表。
  5. 现有 Web 控制台、IOCP 主服务功能无回归。

6. 风险与约束

风险 说明 应对
无官方 C++ MCP SDK 需手写 JSON-RPC 协议层 仅实现 3 个方法,量小可控;协议稳定后再考虑抽库
MCP 规范版本演进 protocolVersion 字符串需与客户端匹配 实现时对照最新规范;initialize 返回可协商版本
并发安全 遍历 m_HostList 需持 m_cs MCP 与 WebService 采用相同的「m_cs 锁内遍历」模式
字符编码 GBK/UTF-8 分叉 复用公共函数 BuildHostJson 的编码处理,不自造
token 存储 THIS_CFG 的 token 会明文落盘;随机 token 会打印到日志 本地回环 + 只读,风险可控;敏感场景建议仅用 env 并妥善保管

7. 后续扩展规划(本期不做)

  • 工具:get_host_processes(进程列表)、execute_command(命令执行)、capture_screen(截图)等
  • 写操作工具的安全边界评审(操作对象、权限、审计)
  • 认证升级:复用 WebServiceAuth 的签名 token / 过期机制
  • 分组过滤:list_online_hostsgroup 参数

8. 决策记录

决策记录(全部已定):

  • 启用开关:新增 McpEnabled,默认 0禁用。MCP 默认不启动,须用户在「扩展 → MCP设置」对话框手动开启。
  • 端口McpPort 可配置,未配置默认 6544(纯端口语义,不再兼任启用开关)。
  • token:优先顺序 env YAMA_MCP_TOKENTHIS_CFG.McpToken → 皆空则随机生成(仅本次进程有效,打印到日志)。对话框内 token 必填、默认随机值(预填并随保存持久化到 McpToken)。
  • 绑定地址:默认 127.0.0.1(仅本机);可配置为 0.0.0.0 或内网 IP192.168.0.92)以支持多人协作跨机调用。
  • 配置入口:主对话框「扩展」菜单,位于「地理信息(&L)」子菜单下方新增「MCP设置」菜单项打开独立配置对话框。
  • 生效方式MCP 启用/端口/绑定/token 的改动均需重启服务端生效,对话框保存后弹窗提醒「重启程序生效」。
  • 取数方案C抽公共函数 BuildHostJsonMCP 与 WebService 平级复用)。
  • 返回字段:全量,含实时信息(rtt / activeWindow),支持 AI 监督场景。
  • 序列化structuredContent + outputSchemacontent 附带可读摘要。