Files
crossdesk/apps/mcp/README.md
T

16 KiB
Raw Blame History

CrossDesk MCP

让本机 Agent 通过 MCP 观察并操作远端 CrossDesk,同时为用户提供 MCP Apps 或浏览器监看面板。控制端不需要安装 CrossDesk 桌面客户端;被控端仍需运行兼容的 CrossDesk,并连接支持 cli 注册的信令服务器。

此目录实现 MCP 包 @chunkdash/crossdesk-mcp 0.1.0,绑定原生 CLI 包 0.2.0。源码实现和本地打包不代表这些版本已经发布到 npm。 发布后的安装示例需要以实际可用版本为准。

从源码运行

仓库根目录构建原生程序(已有工作区可保留原有桌面构建选项):

sh apps/cli/scripts/prepare-deps.sh
xmake f -y --CROSSDESK_BUILD_DESKTOP=n --CROSSDESK_BUILD_CLI=y --USE_CUDA=n -m release
xmake build crossdesk-cli
cd apps/mcp
npm ci
npm run build

Node.js 要求 22 或以上。在 Agent 宿主中配置本地 stdio 进程,例如 macOS arm64 源码构建:

{
  "mcpServers": {
    "crossdesk": {
      "command": "node",
      "args": [
        "/absolute/path/crossdesk/apps/mcp/dist/main.js",
        "serve", "--stdio", "--panel=auto",
        "--native-binary", "/absolute/path/crossdesk/build/macosx/arm64/release/crossdesk-cli"
      ]
    }
  }
}

Windows 和 Linux 使用各自构建产物路径;--native-binary 必须指向可信原生可执行程序,不接受 shell 命令。也可以通过 CROSSDESK_NATIVE_BINARY 显式指定开发产物。未指定时只从包内的固定版本平台依赖解析,不搜索 PATH。服务启动不连接远端、不打开浏览器;连接时才检查 doctor 协议并启动原生子进程。

发布包的配置形式:

{
  "mcpServers": {
    "crossdesk": {
      "command": "npx",
      "args": ["-y", "@chunkdash/crossdesk-mcp@<已发布版本>", "serve", "--stdio", "--panel=auto"]
    }
  }
}

平台依赖覆盖 macOS arm64/x64、glibc Linux arm64/x64 和 Windows x64。安装时保留 optional dependencies;不需要另装全局 CLI。原生运行要求沿用 CLI 分发文档。npm ci 仅安装此源码目录的开发依赖,开发运行需使用 --native-binary;生成的发布包才包含固定平台依赖。

用户连接流程

  1. 对 Agent 说“打开 CrossDesk”。crossdesk_open 创建 view_id,返回面板选择和等待用户状态。
  2. 在面板选择已保存设备,或输入 9 位设备 ID;密码填写在面板里。可选择仅查看或允许 Agent 控制。
  3. “保存设备”“记住密码”“允许以后自动连接”分别控制设备簿、系统凭据库、持久权限。后两项要求保存设备,默认均关闭。保存密码不自动授予控制权限。
  4. 有待执行的远程任务时,Agent 在打开面板后立即调用 crossdesk_get_session({view_id, wait_ms: 25000}) 等待连接。连接就绪后,工具直接返回会话 ID 和权限状态,Agent 继续任务;用户无需回到对话发送连接确认,也无需点击继续按钮。浏览器和 Apps 面板使用同一流程。
  5. Agent 获取独立截图,用截图的 observation_id 和图像坐标提交动作。面板预览不消耗模型截图序列。

crossdesk_open 立即返回,让 Apps 宿主有机会渲染面板;等待连接在后续 crossdesk_get_session 调用中完成。按 view_id 查询默认最多等待 25 秒,wait_ms: 0 则立即查询;未就绪时返回 wait_timed_out: true,Agent 在原任务仍待执行时继续等待。按 session_id 查询仍立即返回。取消等待只取消本次查询,不断开用户正在建立或使用的连接。用户主动断开后返回 closed,Agent 停止等待,不自动重连。

自动继续依赖宿主保持当前 Agent 任务及工具调用流程。它不通过网页伪造用户消息,也不承诺从已经结束的对话中主动唤醒 Agent;此类唤醒由宿主负责。

人工暂停由原生层锁定,模型不能通过 set_control(true) 恢复;只有当前授权面板的恢复按钮可以恢复。仅查看连接不能升级为控制连接,需重新连接并授权。关闭/隐藏面板仅停止预览,远程会话保留;断开按钮或 crossdesk_disconnect 才结束会话。

同一 MCP 进程默认只保留一个活动远程会话。相同配置目录中的进程用设备锁避免同时连接同一目标;它不协调另一个系统用户、另一份配置目录或独立桌面客户端。

面板策略

--panel 优先于设备配置中的 panel,默认 auto。

模式 行为
auto 当前请求声明 MCP Apps MIME 能力时提供 Apps;其他情况打开本机浏览器
apps 只使用 MCP Apps;能力不匹配返回 PANEL_UNSUPPORTED
browser 只使用本地浏览器,不附加 Apps 工具资源元数据
off 不启动网页服务,保留截图和操作工具,凭据通过独立 setup 配置

使用 extensions["io.modelcontextprotocol/ui"].mimeTypes 中的 text/html;profile=mcp-app 判断能力。旧协议读取 initialize 能力;2026-07-28 协议读取当前请求的能力,不按宿主产品名猜测支持情况。Apps 的卡片、侧栏或窗口形式由宿主决定。

Apps 资源为 ui://crossdesk/viewer.html,工具只在允许使用 Apps 且当前请求支持时暴露 UI 元数据和面板专用接口。页面回报 crossdesk_ui_ready 后才标记就绪;10 秒未回报显示 not_confirmed,不自动弹第二个浏览器。用户明确要求回退时调用 crossdesk_open_browser;该工具不附带 Apps 元数据,apps/off 策略拒绝覆盖。

浏览器面板只监听随机端口的 127.0.0.1,与 MCP 服务同一进程。它不是 MCP Streamable HTTP,也不向局域网提供 MCP 接入。MCP、CLI、用户浏览器应在同一控制端电脑上运行;SSH 容器/远程宿主的浏览器转发不属于此本地接入模式。

页面标题为 CrossDesk MCP,仅保留正式应用标志,使用文字按钮和简洁状态提示。布局填满浏览器或宿主面板的可用宽高,远程画面保持原比例自动缩放;连接设置在高度不足时独立滚动。连接后侧栏显示设备与授权信息,暂停、恢复和断开按钮位于画面工具栏。「放大画面」收起侧栏,按 Esc 还原;窄屏默认显示连接表单,连接后显示画面,可用「连接信息」展开设备详情。界面资源均可离线使用,保存设备与持久授权仍由用户分别选择。

首次打开使用 URL fragment 中的短期一次性 bootstrap,页面立即清除 fragment,并换取 HttpOnly/SameSite cookie 和 CSRF 令牌。模型只收到无特权的网页地址。复制无凭据地址到另一浏览器不能获得会话控制权。

无法自动打开浏览器时,在自己的终端运行工具结果 pairing_command 中的命令和参数。例如:

crossdesk-mcp setup --open-panel --view <view_id> --config-dir <相同的配置目录>

通过源码运行时用 node /absolute/path/apps/mcp/dist/main.js 替换 crossdesk-mcp。配对通过本用户配置目录中的受限运行描述符和本地 IPC 完成,不允许匿名 HTTP 请求签发控制凭证。setup 使用用户终端的桌面环境打开浏览器;仍无法打开时,只在该交互终端显示 60 秒有效的一次性地址,不将它返回 MCP 或发送到聊天。面板最多保留 32 个视图;再次打开时会清理超过 30 分钟未使用的视图。浏览器会话最长 12 小时,失效后重新打开。

无面板配置与凭据

在用户自己的交互终端执行(不能在宿主 stdio 通道中交互输入密码):

crossdesk-mcp setup

配置默认存放于 ~/.config/crossdesk/mcp/,可用 --config-dir 或 CROSSDESK_MCP_CONFIG_DIR 覆盖。devices.json 包含设备、面板/视频选项及凭据引用;密码保存到 macOS Keychain、Windows 凭据库或 Linux Secret Service。凭据库不可用就返回明确错误,没有明文文件降级。面板的临时连接仍可使用用户现场输入的密码。持久保存失败会保留已建立连接,并在面板显示错误。

也可在 setup 中指定外部密码环境变量名称,服务读取该变量作为凭据提供器。示例配置(没有密码值):

{
  "version": 1,
  "panel": "off",
  "server": "api.crossdesk.cn",
  "port": 9099,
  "devices": [{
    "alias": "work-pc",
    "device_id": "123456789",
    "server": "api.crossdesk.cn",
    "port": 9099,
    "authorization": "control",
    "credential": "environment",
    "environment_variable": "MY_CROSSDESK_PASSWORD"
  }]
}

授权范围和可用凭据缺一不可。模型不能提供任意 device_id 绕过面板确认;模型侧 crossdesk_connect 仅接受已授权设备别名。不要把密码发给 Agent,也不要把包含密码的命令行写进宿主配置。

Apps 依赖宿主遵守 app-only 工具可见性和 _meta 的模型不可见约定;服务另检查每个视图的随机 UI 能力令牌和 generation。app-only 标记本身不是可信身份凭据。能够读取同用户进程/文件或执行任意本机代码的 Agent 不受此边界隔离。

模型工具

工具 作用
crossdesk_open 打开设备选择/连接面板,返回 view_id
crossdesk_open_browser 用户显式请求的浏览器入口/回退
crossdesk_list_devices 本机设备簿和授权摘要,不提供云发现
crossdesk_connect 通过别名连接已经授权且有凭据的设备
crossdesk_get_session session_id、view_id 严格二选一;按 view_id 可等待连接,wait_ms 范围 0–25000
crossdesk_screenshot 返回 PNG image content 和观测元数据
crossdesk_action 结构化输入,可附带动作之后的截图
crossdesk_set_control 在已授权范围内启停 Agent 输入
crossdesk_select_display 切换显示器,使旧观测失效
crossdesk_disconnect 断开并释放输入状态、子进程和设备锁

动作包括 move、click、double_click、scroll、drag、key_press、hotkey、type_text。不提供 shell、任意键按下保持、剪贴板或文件操作。按键使用 Windows VK 对应的标准名称:A–Z、0–9、F1–F12、CTRL、ALT、SHIFT、META、ENTER、ESC、方向键等;应用快捷键仍需根据远端系统选择。

截图默认最长边 1280,最大 2048;允许指定源帧像素区域。返回 source_size、source_rect、image_size、generation、geometry_epoch、frame_id、frame_age_ms 和 observation_id。动作坐标使用返回图像坐标,不使用浏览器 CSS 像素。观测保留最多 32 个,30 秒过期;切换显示器、连接代际或图像几何变化立即失效。截图使用不超过 2 秒的帧,PNG 不超过 2 MiB,必要时继续缩小输出并返回真实尺寸。

operation_id 在当前原生会话中缓存最近 128 个操作,至少在未被容量淘汰的 5 分钟窗口内支持同参数查询;不同参数复用 ID 返回冲突。submitted 仅说明本地发送完成,远端没有执行 ACK。超时、断网、取消或发送失败可能返回 outcome_unknown/side_effect_possible,禁止自动重放。observe_after 默认开启,后续图像只证明取得更新帧,不证明操作执行成功。

实时视频与按需截图

实时视频提供两条路径,默认 browser,两条路径均由原生 CLI 的本机 WebSocket 直接发送给浏览器。Node 只处理会话、授权票据和 MCP 工具,不读取或转发实时视频字节;不再创建视频管道,不经过 MCP stdio 或模型上下文,不逐帧转 PNG、不重新编码视频。

video.decode_path 画面路径
browser MiniRTC 输出原始 H.264 Annex B → 浏览器 WebCodecs 解码 → canvas
minirtc MiniRTC 解码 → 紧凑 NV12 原始帧 → 浏览器 VideoFrame / canvas;浏览器不再解码 H.264

video.decoder 默认 auto:优先使用可用硬件解码器,不支持时使用软件。software 禁用本程序的硬件选择与硬件回退。浏览器路径使用 WebCodecs 的 prefer-hardware / prefer-software;这些值是浏览器 API 的偏好,浏览器可以忽略,不能据此宣称已确认物理硬件或软件后端。原生路径由 MiniRTC 的解码器工厂选择,本机 macOS 使用 VideoToolbox 或 OpenH264;其他平台使用 MiniRTC 已支持的后端。状态中的 native_video_decoder 返回 MiniRTC 报告的后端名称(统计可用前为空),不代表浏览器后端。

在 ~/.config/crossdesk/mcp/devices.json 的根对象中加入:

"video": {
  "decode_path": "minirtc",
  "decoder": "auto"
}

也可在 Agent 宿主的 MCP 启动参数中配置,命令行优先于文件:

crossdesk-mcp serve --stdio --video-path browser --video-decoder auto
crossdesk-mcp serve --stdio --video-path minirtc --video-decoder software

修改后重启 MCP,再建立连接。选项适用于整个 MCP 进程,两条路径均将解码选项传给原生端;浏览器路径中的原生解码用于截图缓存。截图工具或动作后的截图请求才触发 PNG 编码。这里配置的是控制端解码器,不更改远端编码器。原有配置文件无需新增字段即可继续使用。

Agent 模式默认请求高帧率档位,分辨率上限 1920 × 1080;实际分辨率和帧率由远端能力、网络和本机解码性能决定。面板显示实际尺寸与呈现 FPS。浏览器路径需要 WebCodecs H.264 解码能力;MiniRTC 路径仅需浏览器支持从 NV12 创建 VideoFrame 并绘制。能力不足时明确报错,不自动退回 PNG 轮询。NV12 不压缩,1080p / 30 FPS 约需 93 MB/s 本机传输,4K / 30 FPS 约需 373 MB/s;它不增加远端到控制端的 H.264 网络码率。移除 Node 转发没有消除 NV12 自身的本机传输与浏览器像素处理开销;这不是 GPU 零拷贝。

视频 WebSocket 在原生 CLI 的独立 I/O 线程中运行,只监听随机 127.0.0.1 端口,与 Node 面板 HTTP 端口不同。浏览器面板通过 cookie/CSRF 授权获取视频票据,Apps 通过 app-only crossdesk_video_open 和视图令牌获取。票据 15 秒内一次性有效,绑定视图、会话和代际,通过 WebSocket 第一条消息提交,不写入 URL、日志或模型可见工具结果。WebSocket 只提供画面、收帧 ACK 和关键帧恢复请求,不能执行远端输入操作。最多 8 个活动观看者。H.264 丢帧后等待关键帧恢复;NV12 每帧独立,每个观看者最多一帧未确认,原生发送队列只保留最新画面;H.264 最多 8 帧未确认并受字节阈值限制。10 秒未确认最早一帧则关闭该视频通道,不阻塞其他观看者或 MCP 控制。视图撤销、显示器/连接代际变化、断开和进程退出会关闭对应视频连接;暂停 Agent 控制仍继续播放。页面隐藏或卸载时释放视频订阅,恢复可见时重新订阅。

浏览器页面和 MCP Apps 资源的 CSP 允许原生服务使用的 ws://127.0.0.1:*(仅动态端口),HTTP 控制仍限制到 Node 面板的确切地址。原生服务另外校验确切 Host、Origin 和一次性票据。宿主仍可能限制 loopback、WebSocket、WebCodecs 或 iframe 权限;视频失败时提供重试和浏览器入口,--panel=apps 的严格策略仍禁止浏览器回退。浏览器和 Apps 共用同一播放器,具体宿主需要独立验收。

打包

npm run build
npm run pack:npm -- --out-dir /absolute/path/artifacts

脚本生成可发布 tarball,包含面板、运行代码、源代码、许可和固定版本的五个平台可选依赖。CI 的 MCP package 工作流仅构建并上传 mcp-package,不发布 npm。原生包继续由 CLI 工作流构建,doctor 必须同时声明自动化协议和 automation_video_protocols: ["h264-annexb/1", "nv12/1"] 和 automation_video_decoders: ["auto", "software"],以及 automation_video_transport: "loopback-websocket/1";先发布对应 0.2.0 平台包,再发布 MCP tarball。尚未进行真实跨平台发布/远端验收时,不应把构建成功等同于各宿主和设备上的联调完成。

从发布 tarball 重建 TypeScript/面板时,将包中的 build-package.json 复制为 package.json,运行 npm ci && npm run build。原生源码和依赖来源随平台包提供,详见 源码来源。