Skip to main content
Glama
Cocos666a

rustdesk-mcp

by Cocos666a

rustdesk-mcp

An MCP (Model Context Protocol) server that lets AI agents control remote desktops through the local RustDesk client on macOS.

Instead of re-implementing a remote-desktop stack, rustdesk-mcp drives the RustDesk app you already have: it launches sessions, captures the session window, and injects keyboard/mouse input — exposing all of it as 21 MCP tools to any MCP-capable agent (Claude Code, Hermes Agent, …).

Agent ──stdio MCP──> rustdesk-mcp ──> local RustDesk.app ──> remote desktop

Features

  • 21 tools: session lifecycle, screenshots, hardware-level input injection, a device book, diagnostics (see Tools).

  • Capture pipeline built for macOS 14+ / 26 — where per-window CGWindowListCreateImage was removed: a bundled ScreenCaptureKit helper (tools/rds_capture.swift, source included), a cua-driver daemon fallback, and full-screen-capture + crop as last resort.

  • One coordinate contract: desktop_* coordinates are pixels on the most recent control_screenshot image, auto-converted to screen points (Retina included).

  • Device book: persist peers with alias/notes, fuzzy search, discovery from local RustDesk state.

  • Passwords are accepted per connection call and never written to disk.

Related MCP server: MacWright

How it works

  1. Connect — starts the RustDesk GUI if needed and delivers rustdesk://connect/<peer> through the --connect CLI. The session window is discovered by title and remembered (survives MCP host restarts).

  2. Capture — grabs the session window image, in order:

    • ScreenCaptureKit helper app (bin/rds-cap2.app; fast, needs its own Screen Recording grant);

    • cua-driver daemon (optional fallback);

    • full-screen capture + crop (last resort; slow under LaunchDaemon contexts).

  3. Input — posts hardware-level CGEvents (needs an Accessibility grant for the Python process) at coordinates mapped from the latest screenshot.

  4. Diagnose — rustdesk_status reports RustDesk install/version, TCC permission state, and capture-route availability with actionable hints.

Requirements

  • macOS 14+ (developed and tested on macOS 26, Apple Silicon).

  • RustDesk desktop client installed and signed in — this server drives your client, so the IDs it can reach are exactly the ones the app can reach.

  • Python 3.11+.

  • Xcode Command Line Tools (swiftc) — only to build the optional capture helper.

Install

git clone https://github.com/Cocos666a/rustdesk-mcp.git rustdesk-mcp
cd rustdesk-mcp
python3 -m venv .venv
.venv/bin/pip install -r requirements.txt
.venv/bin/pip install -e .

# Optional but recommended — build the fast capture helper:
bash tools/build_helper.sh
./bin/rds-cap2.app/Contents/MacOS/rds-capture --request   # grant Screen Recording

Permissions (macOS, one-time)

Full manual: docs/MACOS-PERMISSIONS.md. Summary:

Permission

Who needs it

Why

Screen Recording

the Python process running this server; the capture helper

screenshot the session window

Accessibility

the Python process running this server

post keyboard/mouse events

Accessibility

RustDesk.app

forward your keyboard/mouse to the remote

Input Monitoring

RustDesk.app

capture your local input on modern macOS — without it, video works but remote input is dead

Register with your MCP client

Generic stdio config (Claude Desktop, Cursor and other MCP clients):

{
  "mcpServers": {
    "rustdesk": {
      "command": "/absolute/path/to/rustdesk-mcp/bin/rustdesk-mcp"
    }
  }
}

Hermes Agent:

hermes mcp add rustdesk --command /absolute/path/to/rustdesk-mcp/bin/rustdesk-mcp

Quick start

With the server registered, a typical agent session calls the tools in this order (real names, real arguments):

  1. rustdesk_status() — RustDesk version, own ID, TCC state, active sessions.

  2. session_connect(peer_id="123456789", password="…") — opens a session window and returns its window_id + bounds.

  3. control_screenshot() — returns the window image plus its pixel size and scale. That image is the coordinate space for every desktop_* call.

  4. desktop_click_mouse(x=640, y=320) — click at pixel (640, 320) of that image.

  5. desktop_typing_text(text="hello") / desktop_typing_keys(keys="cmd+shift+t").

  6. session_disconnect(peer_id="123456789") — close the window (the RustDesk process keeps running).

If a screenshot fails, rustdesk_status() tells you which capture route is available and which permission is missing.

Tools

Tool

Purpose

rustdesk_status

Environment + permission diagnostics

session_connect / session_state / session_list / session_disconnect

Session lifecycle (peer id, mode, wait, front, password)

control_portforward

Open a port-forward session window

control_screenshot

Capture the session window (image + geometry meta)

desktop_move_mouse / desktop_click_mouse / desktop_drag_mouse / desktop_scroll_mouse

Mouse control (image-pixel coordinates)

desktop_typing_text / desktop_typing_keys / desktop_press_keys / desktop_paste_text

Keyboard control (combos like cmd+shift+t, key repetition)

desktop_waiting

Sleep (UI settling)

device_add / device_remove / device_update / device_search / device_info

Local device book

peer_id accepts both RustDesk IDs (123456789) and direct IPs (100.x.y.z, LAN IPs) — see docs/TROUBLESHOOTING.md for direct-IP setup on the controlled side.

Runtime state is written to data/ next to the project and is gitignored; the directory is created on first use. It holds devices.json (the device book: peer ids, aliases, notes) and sessions.json (window ids and bounds) — no credentials.

Development

.venv/bin/pytest -q                          # unit tests
.venv/bin/python tests/smoke_local.py status # local smoke check

Security

This server is an input-automation bridge: anything that can call its tools can move the mouse, type, and read the screen of the remote machine. Run it only for an MCP client you trust, and consider a dedicated RustDesk peer instead of your daily-driver machine.

  • Passwords: accepted per connection call, forwarded to the RustDesk CLI, and never written to disk. They are redacted (***) in the server's stderr log. Note that the rustdesk:// URL in the tool result echoes the password you passed, so your MCP host's transcript holds it — treat that transcript as sensitive.

  • Clipboard: desktop_paste_text writes to the system clipboard, which overwrites whatever was there, then sends Cmd+V.

  • Device scan: the device book scanner reads only file names under ~/Library/Preferences/com.carriez.RustDesk; it never parses peer configs or password fields.

  • No config writes: the CLI wrapper only passes whitelisted arguments, so it cannot change RustDesk settings (--set-id, --option, --config, …).

Known limitations

  • macOS only — implemented against pyobjc/Quartz/CGEvent; no Linux/Windows backend.

  • No file transfer / command channel yet (Phase 2).

  • Does not modify RustDesk configuration; it only drives the app.

  • Public rendezvous reachability depends on your network — some networks (e.g. mainland China) cannot reach RustDesk's public servers; direct IP works fine in that case.

Alternative: driving a Windows host over RDP (tools/winexec)

When macOS refuses to deliver synthetic input (TCC judges the event sender, so a grant for RustDesk.app does not cover the Python process), the GUI path cannot type or click at all. tools/winexec/ sidesteps the macOS input layer: a headless FreeRDP client runs in a Linux container, and keystrokes are injected into its X11 window with xdotool, so they reach the RDP session without touching macOS permissions.

  • Needs Docker, a Windows host with RDP enabled, and WIN_HOST / WIN_USER / WIN_PASS in the environment (never written to disk).

  • ./winexec.py up → ./winexec.py type 'dir C:\' --enter → ./winexec.py shot → ./winexec.py down.

  • It reconnects your own Windows session, so processes started there survive down.

  • Details: tools/winexec/README.md.

This is a companion channel, not part of the MCP server — the 21 tools above remain the contract.

Contributing

Issues and pull requests are welcome.

  • Tool names and arguments are the public contract (tests/test_tools.py enforces it) — renaming or unregistering a tool is a breaking change.

  • Run the unit tests before opening a PR: .venv/bin/pytest -q. They need no TCC permissions and no running RustDesk.

  • Behaviour that only shows up on a real machine (TCC prompts, capture routes, session windows) should be stated with the macOS/RustDesk version you tested on.

License

MIT — see LICENSE.

Related MCP Connectors

Related MCP Servers

  • A
    license
    A
    quality
    D
    maintenance
    Provides native macOS computer control tools including mouse and keyboard simulation, screenshot capture, and application management for MCP-compatible agents. It enables AI assistants to directly interact with the macOS operating system and installed apps through standard tool calls.
    24
    44 npm
    8
    MIT
  • A
    license
    Not graded
    quality
    D
    maintenance
    An MCP server for reliable native macOS desktop control from AI agents, providing 72 tools for screenshots, mouse, keyboard, scroll, clipboard, window management, and more.
    MIT
  • A
    license
    Not graded
    quality
    D
    maintenance
    Enables MCP clients to control macOS via accessibility and screen recording, providing tools to list apps, observe UI, click, type, press keys, and scroll.
    MIT
  • A
    license
    Not graded
    quality
    B
    maintenance
    Provides a JSON-RPC computer use runtime for macOS, exposing 7 MCP tools (observe/act/inspect/session/cancel/trace) as image content blocks so external agents like Claude Code, Pi, OpenCode, or Codex CLI can capture screenshots and drive the desktop with clicks, keys, and typing while enforcing session locking, stale-frame protection, and trace redaction server-side.
    14 npm
    1
    MIT