Skip to main content
Glama
Ryanu9

RRDP Remote Desktop

by Ryanu9
README.md
# RRDP — VS Code 里的 Windows 远程桌面 + MCP

在 VS Code 编辑器标签页里直接打开 Windows RDP 远程桌面,并内置一个 MCP Server,让 VS Code 自带的 AI(Copilot Chat Agent 模式)以及 Claude Code、Cursor 等客户端可以**看屏幕、点击、输入、按快捷键、在远程机器上执行命令**。

- 纯扩展实现,不需要 Docker / Guacamole / 网关:RDP 协议由 [IronRDP](https://github.com/Devolutions/IronRDP) 的 WASM 客户端在 webview 里完成,扩展进程内置一个很小的 RDCleanPath 代理负责 TCP + TLS。
- 支持 NLA(CredSSP/NTLM)、动态分辨率、文本剪贴板双向同步,文件可在远程和本机之间直接复制粘贴(本机为 Windows 时)。
- 在 Windows 上点进远程画面后键盘鼠标被“锁定”在里面:Win、Win+D、Alt+Tab 等系统快捷键也发给远程机器(和 mstsc 一样),单独按 Ctrl+Alt 释放。
- 会话在后台标签页中保持运行,AI 可以在你看别的文件时继续操作。

## 架构

```
┌──────────── VS Code ─────────────────────────────────────────────────┐
│  Webview 标签页                     扩展进程 (Node)                   │
│  ┌──────────────────────┐   WS    ┌───────────────────┐  TCP+TLS     │
│  │ IronRDP WASM 客户端  │◄──────► │ RDCleanPath 代理  │◄────────────►│── Windows :3389
│  │ canvas / 键鼠 / 剪贴板│         └───────────────────┘              │
│  └──────────▲───────────┘  postMessage RPC                            │
│             └───────────────► 会话管理 ◄── MCP Server 127.0.0.1:47389 │◄── Copilot / Claude Code / Cursor
└──────────────────────────────────────────────────────────────────────┘
```

## 安装

```bash
npm install
npm run package            # 生成 rrdp-<版本>.vsix
code --install-extension rrdp-0.3.0.vsix
```

开发调试:用 VS Code 打开本目录按 **F5**(会先执行 `npm run build`)。

## 使用

1. 左侧活动栏 **Remote Desktop** → **+** 添加连接(主机、`DOMAIN\user` 或 `user@domain`、密码)。密码保存在 VS Code SecretStorage,不写入 settings。
2. 点击连接即在新标签页打开。顶部栏有 Ctrl+Alt+Del、Win 键、断线后的重连按钮。
3. 键盘和鼠标:**点击远程画面后,键盘和鼠标就锁定在远程桌面里**:
   - 所有按键(包括 VS Code 快捷键)都发给远程机器;在 Windows 上连 **Win、Win+D、Win+E、Alt+Tab、Ctrl+Esc、Alt+F4、PrintScreen** 这类本来会被本机 Windows 拦下的系统快捷键也一样(和 mstsc 的“在远程计算机上应用 Windows 组合键”相同)。
   - 鼠标指针不会移出远程画面,方便点到远程的任务栏、屏幕边角。
   - **单独按一下 Ctrl+Alt 就把键盘和鼠标还给 VS Code**(和 VMware 一样),再点画面重新锁定。工具栏右侧会提示当前状态。
   - Ctrl+Alt+Del 和 Win+L 由 Windows 直接处理,永远作用在本机;要给远程发 Ctrl+Alt+Del 请用工具栏按钮。CapsLock/NumLock、音量/媒体键也留在本机。
   - 通过键盘或切换标签页回到远程画面时只锁定键盘,鼠标要再点一下画面才会锁定。
4. 分辨率:不在 `rrdp.connections` 里写 `width/height` 时,远程分辨率等于标签页的**物理像素**大小,并把本机缩放(如 150%)同步给 Windows,所以远程文字和本地一样清晰、一样大;标签页大小变化、窗口拖到其他缩放比例的显示器、VS Code 用 Ctrl +/- 缩放时都会自动跟随。服务器不支持动态分辨率(Windows 8.1 / Server 2012 R2 以前)时,画面按比例缩放显示。
5. 复制粘贴:文本剪贴板双向自动同步(远程复制 → VS Code 剪贴板;在任何程序里复制 → 点回远程画面即可粘贴)。
6. 传文件(通过 RDP 剪贴板,远程不用装任何东西):
   - **上传**:在**本机**资源管理器里选中文件/文件夹按 **Ctrl+C**,点回远程画面,然后在远程的文件夹或桌面里按 **Ctrl+V**,和 mstsc 一样(本机是 Windows 时)。只发布文件列表,远程粘贴时才按需从本机读取,大文件也不占内存。也可以用工具栏 **Upload…**(或命令 “RDP: Upload Files to Remote Clipboard”),或按住 **Shift** 把文件/文件夹拖进远程画面,再在远程 **Ctrl+V**。
   - **下载**:在远程资源管理器里选中文件/文件夹按 **Ctrl+C**,然后在**本机**资源管理器或桌面里按 **Ctrl+V**,和 mstsc 一样(本机是 Windows 时)。复制时只放文件列表,粘贴时才逐个下载,进度和重名处理由资源管理器负责。也可以点工具栏 “**Save to…**” 选择本地文件夹另存(任何系统都可用;同名文件不会被覆盖,会自动加 “ (1)”)。

首次连接某台主机时会记住它的证书指纹(TOFU);之后证书变化会弹窗确认。

## 给 AI 用(MCP)

**VS Code 自带 AI**:扩展通过 `mcpServerDefinitionProviders` 自动注册,无需配置。在 Copilot Chat 选择 Agent 模式,工具列表里会出现 “RRDP Remote Desktop”。

**其他客户端**:命令面板执行 **RDP: Copy MCP Server Config**,可复制:

- Claude Code:`claude mcp add --transport http rrdp http://127.0.0.1:47389/mcp --header "Authorization: Bearer <token>"`
- `mcp.json` / Cursor 等的 JSON 配置

MCP 只监听 `127.0.0.1`,并要求 Bearer token(每台机器随机生成并持久保存)。

### 工具

| 工具 | 作用 |
| --- | --- |
| `rdp_list_sessions` | 列出已打开的会话和已保存的连接 |
| `rdp_connect` / `rdp_disconnect` | 打开 / 关闭一个已保存的连接 |
| `rdp_screenshot` | 截图(默认缩放到宽 ≤1280),可传 `region` 放大局部看小字 |
| `rdp_click` / `rdp_move` / `rdp_drag` / `rdp_scroll` | 鼠标操作(左/右/中键、双击、带修饰键点击) |
| `rdp_type` | 输入任意文字(Unicode 注入,不受键盘布局 / 输入法影响) |
| `rdp_key` | 按键组合,如 `ctrl+c`、`alt+f4`、`win+r`、`ctrl+shift+esc` |
| `rdp_run_command` | 在远程机器上执行 PowerShell / cmd 并**返回文本输出和退出码** |
| `rdp_set_clipboard` / `rdp_get_clipboard` | 读写远程剪贴板 |

**坐标约定**:所有 x/y 都是 `rdp_screenshot` 返回的整屏截图的像素坐标,扩展负责换算成远程桌面坐标。操作类工具可传 `screenshot: true`,操作完直接附带一张新截图。

**`rdp_run_command` 原理**:只走 RDP 通道,远程机器不需要开 WinRM/SSH。脚本先放进远程剪贴板 → `Win+R` 启动隐藏的 PowerShell 读取剪贴板执行 → 输出(UTF-8,含 stderr/Write-Host)带唯一标记写回剪贴板 → 扩展取回。要求:桌面已解锁、没有 UAC 弹窗、未被策略禁用“运行”对话框和剪贴板重定向。执行期间会短暂占用远程剪贴板。

## 设置

| 设置 | 默认 | 说明 |
| --- | --- | --- |
| `rrdp.connections` | `[]` | 连接列表(name、host、port、username、domain、可选 width/height) |
| `rrdp.dynamicResolution` | `true` | 标签页大小变化时调整远程分辨率 |
| `rrdp.hiDpi` | `true` | 按物理像素渲染并同步本机缩放比例(清晰);关掉则按逻辑像素,带宽更省但在高分屏上偏糊 |
| `rrdp.ctrlAltReleasesKeyboard` | `true` | 单独按 Ctrl+Alt 释放键盘(和鼠标) |
| `rrdp.captureSystemKeys` | `true` | Windows:锁定时 Win、Alt+Tab 等系统快捷键也发给远程 |
| `rrdp.confineMouse` | `true` | Windows:锁定时鼠标不移出远程画面(需要开启 `rrdp.ctrlAltReleasesKeyboard`) |
| `rrdp.syncClipboard` | `true` | 同步剪贴板:文本双向;本机是 Windows 时文件也双向(远程复制 ↔ 本机粘贴) |
| `rrdp.mcp.enabled` | `true` | 是否启动 MCP Server |
| `rrdp.mcp.port` | `47389` | MCP 端口;被占用(例如开了多个 VS Code 窗口)时改用随机端口 |
| `rrdp.mcp.screenshotMaxWidth` | `1280` | 给 AI 的截图最大宽度,也决定坐标空间 |
| `rrdp.logLevel` | `WARN` | IronRDP 日志级别(在 webview 开发者工具中查看) |

## 已知限制

- **IronRDP 0.7.0(npm 最新版)在 xrdp 等非 Windows 服务器上部分位图会错位(斜切)**。上游已在 2026-07-30 修复(IronRDP #1486 “preserve bitmap source stride”),但还没有发布到 npm;新版本发布后升级 `@devolutions/iron-remote-desktop-rdp` 即可。Windows 主机通常走 RemoteFX,不经过这条代码路径,但**尚未在真实 Windows 主机上验证**。
- 没有声音、打印机、驱动器(共享文件夹)重定向:IronRDP 的网页版还不支持驱动器重定向,所以传文件走剪贴板。
- 剪贴板同步目前只支持文本(VS Code 扩展 API 读不到图片剪贴板)。
- 上传的文件在远程粘贴完成前保存在内存里,超过 512 MB 会先询问。
- 远程 → 本机粘贴:单个文件最大 2 GB(IronRDP 的限制);每个文件先完整下载到临时目录再交给资源管理器,所以大文件在资源管理器里的进度会先停在 0%,期间工具栏显示 “Receiving … %”。临时文件在下次远程复制或关闭 VS Code 时删除。实现方式是第二个小助手 `native/rrdp-clipboard.cs`(同样首次使用时用 `csc.exe` 编译),它以 “虚拟文件”(FileGroupDescriptorW / FileContents,与 mstsc、Outlook 附件相同)的形式占用本机剪贴板。
- 认证走 NTLM;Kerberos(需要 KDC 代理)尚未接入。
- 不支持 RD Gateway;目标主机需要从本机直接可达。
- 系统快捷键和鼠标锁定只在**本机是 Windows** 时可用(扩展需运行在本机 UI 端,默认如此)。实现方式是一个带低级键盘/鼠标钩子的小助手进程:源码 `native/rrdp-input.cs` 随扩展发布,第一次打开 RDP 标签页时用 Windows 自带的 .NET Framework `csc.exe` 编译到扩展的 globalStorage(约 0.2 秒),只在有 RDP 标签页时运行。它只在远程画面拥有键盘且 VS Code 窗口在前台时拦截按键,其余时间全部放行。个别杀毒软件可能会拦截这类钩子程序,这时扩展会提示并退回到普通模式(Win 键可用顶部栏的 “Win” 按钮)。
- macOS / Linux 本机上 Win 等系统快捷键仍会被本机系统拦截,请用顶部栏的 “Win” 按钮。

## 开发

```
src/
  extension.ts      激活入口、命令、会话管理、证书固定、MCP 注册
  session.ts        一个 RDP 标签页(webview + RPC + 剪贴板同步)
  proxy.ts          RDCleanPath WebSocket → TCP/TLS 代理
  rdcleanpath.ts    RDCleanPath PDU 的 DER 编解码
  mcpServer.ts      MCP 工具定义 + Streamable HTTP 服务
  actions.ts        点击/输入/按键/截图/执行命令等高层操作(与 VS Code 无关)
  keys.ts           键名 → 扫描码
  systemInput.ts    Windows:系统快捷键 / 鼠标锁定助手进程的编译、启动和协议
native/rrdp-input.cs  上述助手(C# 5,低级键盘/鼠标钩子 + ClipCursor)
  fileClipboard.ts  Windows:远程复制的文件放到本机剪贴板(粘贴时按需下载);读取本机复制的文件
webview/localFiles.ts 远程粘贴本机文件时,按请求返回大小/数据块
native/rrdp-clipboard.cs  上述助手(C# 5,OLE 剪贴板上的虚拟文件 IDataObject)
  connections.ts    连接配置、密码、侧边栏
webview/main.ts     IronRDP 会话、画布、键鼠输入、截图
```

```bash
npm run build        # esbuild 打包到 dist/
npm run typecheck
npm test             # 单元测试
# 端到端测试(真实代理 + MCP + 无头 Chromium 中的 webview),用 xrdp 容器当 RDP 服务器:
docker run -d --name rrdp-xrdp --shm-size=1g -p 127.0.0.1:33890:3389 scottyhardy/docker-remote-desktop
npm run test:e2e
# 在真实 VS Code 里跑(Linux 需 xvfb;在 VS Code 终端里需去掉 ELECTRON_RUN_AS_NODE):
env -u ELECTRON_RUN_AS_NODE xvfb-run -a npm run test:vscode
```