远程执行MCP服务器

remote-exec-mcp 是一个远程优先的 MCP 服务,通过 broker-daemon 架构在配置的 Linux、macOS 和 Windows 机器上执行命令、应用补丁、传输文件并转发 TCP/UDP 端口,适用于智能体远程控制与开发运维场景。

  • by chitao1234
  • Rust
  • 混合部署
  • 1 星标
  • 更新于
在 GitHub 查看 chitao1234/remote-exec-mcp

使用说明

remote-exec-mcp

remote-exec-mcp is a remote-first MCP server for running Codex-style local system tools on configured Linux, macOS, and Windows machines. Agents connect to one broker, choose an explicit target, and use familiar tools for command execution, stdin, patching, image reads, file transfer, and TCP/UDP forwarding.

The tool interface is influenced by Codex, but this repository is a separate broker plus daemon implementation.

Read This First

  • Start with configs/broker.example.toml and configs/daemon.example.toml.
  • Use crates/remote-exec-daemon-cpp/config/daemon-cpp.example.ini for the standalone C++ daemon.
  • Use skills/using-remote-exec-mcp/SKILL.md for agent-facing tool guidance.
  • Treat docs/ as historical planning and audit context unless a task explicitly asks for maintenance there.
  • The stable external contracts are the MCP tool schemas, operator config files, this README, AGENTS.md, and the user-facing skill.

Quick Start

Generate development certificates for Rust TLS targets:

cargo run -p remote-exec-admin -- certs dev-init \
  --out-dir ./remote-exec-certs \
  --target builder-a

Start a Rust daemon:

cargo run -p remote-exec-daemon -- configs/daemon.example.toml

Start the broker:

cargo run -p remote-exec-broker -- configs/broker.example.toml

List configured targets through the CLI:

cargo run -p remote-exec-broker --bin remote-exec -- \
  --broker-config configs/broker.example.toml \
  list-targets

Run the C++ daemon when you need a smaller daemon or legacy Windows coverage. It supports optional OpenSSL or LibreSSL mutual TLS, POSIX BoringSSL mutual TLS, and explicit plain HTTP:

make -C crates/remote-exec-daemon-cpp check-posix
crates/remote-exec-daemon-cpp/build/remote-exec-daemon-cpp \
  crates/remote-exec-daemon-cpp/config/daemon-cpp.example.ini

Public Surface

Implemented public MCP tools (prefixed with remote_ by default):

Tool Purpose
remote_list_targets Report configured targets and cached daemon metadata.
remote_exec_command Start a command on one target.
remote_write_stdin Write to or poll a live command session.
remote_apply_patch Apply a Codex-style patch on one target.
remote_view_image Read an image from one target.
remote_transfer_files Transfer files or directory trees between local and remote endpoints.
remote_forward_ports Open, list, or close TCP/UDP forwards between two sides.

Default-hidden file tools are available only when explicitly enabled under [tools.file] in broker config:

  • remote_read
  • remote_write
  • remote_edit

Components

Component Role
remote-exec-broker Public MCP server over stdio or streamable HTTP. It validates targets, routes calls, owns public session_id and forward_id namespaces, and can expose local as the broker host or as a relay to one configured remote.
remote-exec CLI client built from the broker crate. It can run against a config in-process or a streamable-HTTP broker.
apply_patch Standalone local CLI built by both the Rust host crate and C++ daemon build. It reads a Codex-style patch from standard input. --help prints built-in usage; --help --help-file PATH prints the supplied help file instead. Without --help, --help-file PATH is ignored.
remote-exec-daemon Rust per-machine daemon. It supports mutual TLS by default, optional plain HTTP, exec, patch, image, transfer, sandbox checks, and v4 port-forward tunnels.
remote-exec-host Shared Rust host runtime reused by the Rust daemon and broker-host local behavior.
remote-exec-daemon-cpp Standalone C++11 daemon with optional OpenSSL or LibreSSL mTLS, POSIX BoringSSL mTLS, explicit plain HTTP, and POSIX and legacy Windows build paths.
remote-exec-proto Public MCP schemas, broker-daemon RPC schemas, path and sandbox helpers, and port-forward protocol types.
remote-exec-admin Certificate/bootstrap CLI.
remote-exec-pki Reusable certificate generation, manifest, and private-key write helpers.

Runtime Model

Agents talk only to the broker. Each configured remote target points at one daemon. The broker performs target validation, identity checks, routing, result formatting, and runtime ID mapping.

Important invariants:

  • Target selection is part of the security boundary.
  • Configured remote targets may not be named local.
  • A session, file operation, or forward opened for one target is not valid for another target.
  • Public session_id and forward_id values are broker-owned opaque tokens. They are not daemon process IDs, daemon-local session IDs, tunnel IDs, or stream IDs.
  • remote_list_targets reports configured targets and cached daemon metadata. It may perform a short bounded status refresh for targets without currently available metadata, but it does not expose stale daemon metadata while a target is unavailable. Each entry includes health_status: unknown, healthy, maybe_unhealthy, or unhealthy. The existing healthy boolean remains the backward-compatible availability indicator.
  • Public supports_port_forward means the target reported forwarding support and the broker verified a supported tunnel protocol version.
  • remote_list_targets also reports supports_exec and supports_apply_patch. These are daemon policy capabilities; missing fields from older daemons mean both operations are supported for compatibility.
  • Temporarily unreachable targets can remain configured. Broker startup may succeed and verify a target before the first forwarded call.
  • Broker-daemon RPC uses HTTP/1.1 JSON. Port forwarding uses daemon-private HTTP/1.1 Upgrade tunnels.
  • Direct targets and daemon-initiated reverse targets preserve the same public MCP behavior and broker-owned ID namespaces.
  • remote_forward_ports v4 uses X-Remote-Exec-Port-Tunnel-Version: 4. Frame numbers 20 and 21 are reserved as ForwardRecovering and ForwardRecovered; public recovery state is reported through broker-owned forward_ports list fields.

Live exec sessions and live port forwards are in-memory runtime state. Broker restart drops public session_id and forward_id mappings. Daemon restart drops daemon-local command sessions and forwarded sockets.

Configuration

Use the example configs as the canonical shape:

File Covers
configs/broker.example.toml MCP transport, targets, TLS client credentials, reverse listener, embedded or relay local, host sandbox, hidden file tools, transfer limits, and forwarding limits.
configs/daemon.example.toml Rust daemon target name, listen/reverse mode, default workdir, TLS or HTTP transport, bearer auth, shell/PTY policy, sandbox, transfer, yield-time, and forwarding limits.
crates/remote-exec-daemon-cpp/config/daemon-cpp.example.ini C++ daemon listen address, direct/reverse TLS or HTTP transport, default workdir, bearer auth, request/session/forwarding limits, yield-time limits, and sandbox.

Common rules:

  • default_workdir must already exist when a broker [local] target or daemon starts.
  • Rust broker and Rust daemon targets use TLS by default.
  • Plain HTTP targets must be configured with allow_insecure_http = true in the broker.
  • The C++ daemon defaults to TLS=auto. POSIX builds compile and link TLS when OpenSSL 1.0.2 or newer, LibreSSL 2.7.1 or newer, or BoringSSL is available; the detected compatible library is used automatically. Windows XP and newer targets enable TLS automatically, while older Windows targets default to TLS off.
  • Plain C++ daemon targets require allow_insecure_http = true and should usually use bearer auth when they are not isolated by another trusted transport.
  • Reverse mode keeps a bounded adaptive lane pool because transfers and port-forward upgrades can occupy HTTP/1.1 connections for long periods.
  • Both daemons support reverse TLS and explicit insecure HTTP. The C++ daemon requires an OpenSSL- or LibreSSL-backed build, or a BoringSSL-backed POSIX build, for TLS.

On macOS, use an existing directory such as /Users/you/work in place of the examples' /srv/work paths. The Rust broker, Rust daemon, and C++ daemon support native command execution, PTYs, file operations, transfers, and TCP/UDP forwards. The system sh, Bash, and zsh load their login profiles when login is omitted or true; set login: false to skip profile initialization. C++ builds use the Xcode Command Line Tools' c++ and make through the existing all-posix and check-posix targets.

TLS And Bootstrap

Rust broker and Rust daemon targets use mutual TLS by default:

  • broker feature broker-tls is enabled by default
  • daemon feature tls is enabled by default
  • the daemon presents a server certificate signed by the configured CA
  • the broker presents a client certificate signed by the configured CA
  • both sides trust the configured CA

If the broker is built without broker-tls, it rejects https:// daemon targets and https:// broker URLs. If the Rust daemon is built without tls, it only supports transport = "http".

Generated broker and daemon leaf certificates are usable for both direct and reverse TLS. The broker leaf has client-auth usage for direct daemon requests and server-auth usage for the reverse listener, with a DNS SAN matching the broker common name. Each daemon leaf has server-auth usage for direct mode and client-auth usage for reverse lane registration, with its common name matching the configured target. Plain reverse mode requires the configured bearer token for lane registration and normal RPC authentication.

Development bootstrap:

cargo run -p remote-exec-admin -- certs dev-init \
  --out-dir ./remote-exec-certs \
  --target builder-a \
  --target builder-b

Reuse an existing CA:

cargo run -p remote-exec-admin -- certs dev-init \
  --out-dir ./remote-exec-certs-next \
  --target builder-c \
  --reuse-ca-from-dir ./remote-exec-certs

Add daemon SANs when the broker connects by DNS name or non-localhost IP:

cargo run -p remote-exec-admin -- certs dev-init \
  --out-dir ./remote-exec-certs \
  --target builder-a \
  --san builder-a=dns:builder-a.example.com \
  --san builder-a=ip:10.0.0.12

dev-init writes ca.pem, ca.key, broker.pem, broker.key, daemons/<target>.pem, daemons/<target>.key, reverse-mode certificates under reverse/, and certs-manifest.json.

Certificate notes:

  • If no SAN is provided, generated daemon certs default to DNS:localhost and IP:127.0.0.1.
  • Generated private keys are written with restricted permissions: Unix 0600; Windows DACL for current user, local Administrators, and LocalSystem.
  • expected_daemon_name should match the daemon's configured target.
  • skip_server_name_verification = true still validates CA, key usage, and expiry, but skips URL host to certificate SAN matching.
  • pinned_server_cert_pem and tls.pinned_client_cert_pem add exact leaf certificate pins on top of normal CA validation.
  • Bearer auth authenticates requests but does not add confidentiality or integrity on plain HTTP.

Running

Start a Rust daemon:

cargo run -p remote-exec-daemon -- configs/daemon.example.toml

Start the broker over stdio:

cargo run -p remote-exec-broker -- configs/broker.example.toml

Expose the broker over streamable HTTP:

[mcp]
transport = "streamable_http"
listen = "127.0.0.1:8787"
path = "/mcp"

MCP tool names are prefixed with remote_ by default. Set prepend_tool_names = false in the broker config to retain legacy unprefixed MCP names; this does not change the remote-exec CLI subcommands.

Run the C++ daemon:

make -C crates/remote-exec-daemon-cpp
crates/remote-exec-daemon-cpp/build/remote-exec-daemon-cpp \
  crates/remote-exec-daemon-cpp/config/daemon-cpp.example.ini

CLI Client

The remote-exec CLI keeps its existing subcommand names and automatically uses either prefixed or legacy MCP tool names advertised by an HTTP broker.

Use a broker config in-process:

cargo run -p remote-exec-broker --bin remote-exec -- \
  --broker-config configs/broker.example.toml \
  list-targets

Use a running streamable-HTTP broker:

cargo run -p remote-exec-broker --bin remote-exec -- \
  --broker-url http://127.0.0.1:8787/mcp \
  list-targets

Common examples:

cargo run -p remote-exec-broker --bin remote-exec -- \
  --broker-config configs/broker.example.toml \
  exec --target builder-a --workdir /srv/project 'cargo test'

cargo run -p remote-exec-broker --bin remote-exec -- \
  --broker-config configs/broker.example.toml \
  transfer-files \
  --source local:/tmp/source.txt \
  --destination builder-a:/tmp/dest.txt \
  --overwrite replace \
  --create-parent

cargo run -p remote-exec-broker --bin remote-exec -- \
  --broker-url http://127.0.0.1:8787/mcp \
  forward-ports open \
  --listen-side local \
  --connect-side builder-a \
  --forward tcp:127.0.0.1:15432=127.0.0.1:5432

Use --json for normalized JSON output. Use apply-patch --input-file - and write-stdin --chars-file - to read payloads from stdin.

CLI exit codes are:

Code Meaning
0 Success.
2 Usage or input error.
3 Broker config load/build error.
4 Streamable-HTTP connection or transport error.
5 MCP tool error returned by the broker.

--broker-config mode builds broker state for one CLI invocation. Persistent port forwards require a long-running broker, so prefer --broker-url for forward-ports open/list/close workflows.

Tool Behavior

remote_exec_command:

  • runs one command on one target
  • returns session_id when still running
  • merges stdout/stderr order into one public output field for non-TTY exec
  • applies daemon or embedded broker-local yield_time_ms policy
  • truncates output by approximate token budget, where one token is about four UTF-8 bytes

remote_write_stdin:

  • writes to or polls a broker-owned live session
  • can route by session_id alone
  • rejects mismatched target if supplied
  • accepts pty_size for live TTY resize before writing or polling
  • normalizes lost daemon sessions into the usual unknown-process error

remote_apply_patch:

  • applies Codex-style patches on one target
  • preserves existing LF versus CRLF style for updated files
  • supports the documented *** End of File marker
  • accepts Codex parser tolerances for empty envelopes, blank update context, blank separators after *** End of File, and Unicode whitespace around patch control lines
  • accepts *** Environment ID: <id> as metadata returned by the daemon; it never overrides the explicitly selected target
  • parses the complete patch before writing; malformed patches make no changes
  • executes valid patch actions sequentially and is non-transactional across actions, so a later filesystem, sandbox, decode, or hunk-match failure can leave earlier actions applied
  • writes directly to the target for *** Add File and normal in-place *** Update File actions, following symlinks; all update hunks are resolved before writing, but an underlying write failure can leave a partially written file
  • can use experimental target encoding autodetection when enabled in config

Target encoding autodetection covers UTF-8 with or without a byte-order mark, UTF-16LE/BE with or without a byte-order mark, and Shift_JIS, EUC-JP, GB18030 (which also decodes GBK and GB2312), Big5, EUC-KR, and the single-byte code pages Windows-1250 through Windows-1258, ISO-8859-2 through ISO-8859-16, KOI8-R, KOI8-U, Macintosh, and IBM866. Both daemons verify a detected encoding by re-encoding the decoded text and comparing it against the original bytes, so a wrong guess is reported as a decode failure rather than silently rewriting the file. An edit that introduces a character the detected encoding cannot represent fails instead of substituting a replacement character.

The C++ daemon can also be built without this feature (TEXT_ENCODING=off), in which case the file tools and apply_patch require UTF-8.

Standalone apply_patch executables print the action and underlying filesystem or patch-engine error to stderr. If a runtime write/remove error occurs after earlier actions complete, stdout reports Partial success and lists those completed actions; the process still exits unsuccessfully.

Default-hidden file tools:

  • remote_read reads one text file and returns text output only
  • read.file_path, write.file_path, and edit.file_path resolve relative to the target default workdir unless they are target-native absolute paths
  • read.offset is one-based; omitted or 0 means line 1
  • read.limit defaults to tools.file.default_read_limit_lines
  • remote_write overwrites an existing file or creates a new file
  • remote_edit replaces old_string with new_string; if old_string matches more than once, the call fails unless replace_all = true
  • these tools are not listed or callable unless enabled under [tools.file]

remote_view_image:

  • reads an image from one target
  • accepts detail for compatibility but does not use it
  • preserves PNG, JPEG, and WebP bytes without resizing
  • Rust daemon transcodes BMP, GIF, ICO, PNM (including PPM), and TGA to PNG; the C++ daemon supports passthrough PNG, JPEG, and WebP only

remote_transfer_files:

  • supports local -> remote, remote -> local, remote -> remote, and local -> local
  • accepts either one source or a sources array
  • requires endpoint-native absolute paths
  • defaults to overwrite = "merge", destination_mode = "auto", and symlink_mode = "preserve"
  • supports overwrite = "fail", "merge", and "replace"
  • supports destination_mode = "exact" and "into_directory"
  • supports symlink_mode = "preserve", "follow", and "skip"
  • supports exclude glob patterns relative to each source root
  • skips unsupported special files inside directory trees with warnings
  • streams multi-source archives through the broker with bounded buffering rather than staging them on disk
  • does not expose a public compression option; compression is broker-internal
  • uses Zstandard for eligible remote transfers, but skips it when every source filename has a known already-compressed/container extension such as .zip or .mp4
  • is not transactional; failed transfers can leave partial destination changes

remote_forward_ports:

  • supports action = "open" | "list" | "close"
  • supports tcp and udp
  • opens listeners on listen_side and outbound connections/datagrams on connect_side
  • allows either side to be a configured target or "local"
  • treats bare endpoint "8080" as "127.0.0.1:8080"
  • allows non-loopback listen binds such as "0.0.0.0:8080"
  • allows listen_endpoint port 0; read the result for the actual bound port
  • requires nonzero connect_endpoint ports
  • reports phase, side health, generation, reconnect counters, drop counters, and effective limits
  • treats phase = "ready" as readiness; legacy status = "open" can coexist with phase = "reconnecting"
  • can recover future listen-side traffic after broker-daemon transport loss when the daemon stays alive, but active TCP streams and UDP per-peer connector state are lost

Local Semantics

local is a logical endpoint with three possible configurations:

  • With no local configuration, local is unavailable for exec, stdin, patch, image, and hidden file tools. Transfer endpoints and forwarding sides named local retain their legacy broker-host filesystem/network behavior.
  • An embedded [local] table makes local an actual broker-host target for exec, stdin, patch, image, hidden file tools, and the host-side transfer and forwarding compatibility paths. host_sandbox applies to broker-host filesystem access.
  • A scalar local = "builder-a" enables relay mode. Every operation addressed to local, including exec, stdin, patch, image, hidden file tools, transfers, and both sides of port forwarding, runs through the selected remote daemon. The broker host's filesystem and network are not reachable through this endpoint. The selected configured name is replaced publicly by local, so builder-a is not a second bypass target. Remote daemon sandbox and limits apply; broker-host sandbox and host transfer limits do not.

The scalar relay form and [local] table are mutually exclusive. The relay target must be a configured remote target and cannot itself be named local.

Daemon Operation Policies

Rust and C++ daemons accept two independent, default-enabled policy settings:

  • allow_exec = false disables command execution, including both new exec sessions and stdin/write or poll requests for existing sessions.
  • allow_apply_patch = false disables the patch RPC while leaving other daemon operations available. If command execution remains enabled, commands can still modify files; this setting is not a general write prohibition.

The daemon advertises both settings through target capabilities, and the broker rejects known-disabled operations before forwarding them.

Trust Model

Selecting a target grants broad access on that machine unless static sandbox config restricts the relevant path-based operation. There is no per-call approval flow and no sandbox selection flow.

Sandbox rules are static allow/deny lists:

  • missing allow or allow = [] means allow all
  • deny entries refine the allowed set
  • remote_exec_command checks only the resolved starting cwd
  • command text is not inspected for arbitrary path references
  • remote_view_image and remote_read check read paths
  • remote_write, remote_edit, and remote_apply_patch check write paths
  • remote_transfer_files checks source read access and destination write access on their respective endpoints
  • remote_forward_ports can bind non-loopback addresses and connect to arbitrary endpoints reachable from each side, subject to configured forwarding limits

Security is based on explicit target selection plus broker-to-daemon mutual TLS for normal Rust targets. Plain HTTP requires explicit opt-in.

Reliability Notes

  • Broker startup probes run concurrently and are bounded by timeouts.startup_probe_ms.
  • Broker target health refresh runs periodically in the background. One failed probe marks a target maybe_unhealthy; a second consecutive failure marks it unhealthy. Both states use the shorter unhealthy interval, and any successful probe restores healthy.
  • Timed-out broker-daemon requests are never replayed.
  • After repeated direct-target timeouts without a successful HTTP response, the broker replaces the target's HTTP client pool. Reverse targets additionally discard queued idle lanes so the daemon replenishes them. Connection resets remain warning-level while the target is healthy or maybe_unhealthy, but are debug-level once repeated probes have marked it unhealthy; a successful daemon response restores warning-level reset logging.
  • remote_exec_command and remote_write_stdin use at least yield_time_ms plus a small slow-host margin for their daemon RPC timeout.
  • Rust daemon and broker-host live exec sessions are capped by max_open_sessions and prune older sessions under pressure, preferring completed sessions.
  • forward_ports open is all-or-nothing for a single tool call.
  • Explicit forward_ports close reports an error if daemon-side cleanup cannot be confirmed, leaving listed state available for retry or inspection.
  • If the broker disappears without closing a forward, daemon-side detached listeners and UDP sockets are reclaimed after the reconnect grace window.
  • Rust daemon shutdown cancels pending tunnel work and closes live forwarded sockets before exit.
  • C++ daemon forwarding bounds worker count, tunnel I/O, queued bytes, UDP binds, active TCP streams, retained sessions/listeners, and TCP connect time.
  • C++ daemon Windows non-PTY commands are detached from the daemon console. Its configured default shell, COMSPEC, and per-command shell may name any executable; shell names are not restricted to a built-in list. Recognized shell families keep their argument conventions, and other Windows executables receive /C followed by the command as one argument. Windows 9x/Me commands report completion after process exit and a bounded drain of final output, including when redirected pipes do not report EOF. If output completion arrives later than descendant cleanup, exec calls and stdin polls continue waiting within their remaining yield_time_ms budget. Windows stdin writes are bounded by stdin_write_timeout_ms; a timeout terminates and retires the affected session.

C++ Daemon

The C++ daemon intentionally supports a smaller surface than the Rust daemon:

Area C++ daemon behavior
Transport Optional OpenSSL or LibreSSL direct/reverse mTLS, BoringSSL direct/reverse mTLS on POSIX, or explicit plain HTTP. TLS builds use https://...; plain targets use http://... and allow_insecure_http = true.
Security Mutual TLS with OpenSSL 1.0.2 or newer, LibreSSL 2.7.1 or newer, or BoringSSL on POSIX, optional bearer auth, certificate pinning, and static path sandboxing.
Exec C++11 implementation for POSIX and legacy Windows hosts. POSIX PTY is supported when the host can allocate one. GNU/MSVC Windows PTY support depends on vendored winpty.
Files remote_apply_patch, remote_view_image passthrough for PNG/JPEG/WebP, transfer import/export, and the default-hidden remote_read / remote_write / remote_edit with the same semantics and wire error codes as the Rust daemon.
Text encoding Optional. With experimental_apply_patch_target_encoding_autodetect, the file tools and apply_patch decode and rewrite a target file's detected encoding instead of requiring UTF-8. Can be omitted from the binary with TEXT_ENCODING=off.
Transfers File, directory, and broker-built multi-source transfers. No transfer compression.
Forwarding v4 TCP/UDP port-forward tunnel support with daemon-local worker and queue limits.
Legacy Windows GNU paths cover NT 3.x Winsock 1.1, NT 4.0 Winsock 1.1/2, Windows 2000, XP, XP x64, x64 NT-family, and ANSI Windows 9x/Me variants. MSVC covers native, x86 v141_xp, and x64 XP-compatible paths.

The daemon builds as C++11 on every supported build path. In this repository, "Windows XP-compatible" means using a toolchain that can target XP while still compiling the daemon as C++11.

Common C++ checks:

make -C crates/remote-exec-daemon-cpp check-posix
make -C crates/remote-exec-daemon-cpp check-windows-2000
make -C crates/remote-exec-daemon-cpp prepare-openssl-xp OPENSSL_DEPS_DIR=/path/to/deps
make -C crates/remote-exec-daemon-cpp check-windows-xp OPENSSL_ROOT=/path/to/deps/openssl-1.0.2u
make -C crates/remote-exec-daemon-cpp check-windows-x64 OPENSSL_ROOT=/path/to/openssl-mingw64
make -C crates/remote-exec-daemon-cpp check-windows-xp-x64 TLS=off
make -C crates/remote-exec-daemon-cpp check-windows-nt3x-ws1
make -C crates/remote-exec-daemon-cpp check-windows-nt4-ws1
make -C crates/remote-exec-daemon-cpp check-windows-nt4-ws2
bmake -C crates/remote-exec-daemon-cpp check-posix

Build and execution can be split across machines. test-bundle-* targets compile and stage a relocatable directory containing the daemon, apply_patch, test binaries, runtime companions, fixtures, and a native test runner, without launching target binaries:

make -C crates/remote-exec-daemon-cpp test-bundle-posix
make -C crates/remote-exec-daemon-cpp test-bundle-windows-xp TLS=off

Copy the printed directory under build/test-bundles/ to a compatible machine, preserving executable permissions, then run ./run-tests on POSIX or run-tests.exe on Windows. Pass --list to inspect the test names or pass names such as transfer server-streaming to run a subset. Set TEST_BUNDLE_DIR to stage directly into a fresh artifact output directory. The execution machine does not need the repository, Make/NMAKE, or a compiler, but it must provide any dynamically linked libraries selected at build time. The C++ daemon README documents the GNU, BSD make, and MSVC bundle targets in detail.

For OpenSSL-backed C++ builds, run the matching prepare-openssl or prepare-openssl-xp target first and pass the resulting OPENSSL_ROOT. LibreSSL and BoringSSL builds use the existing TLS=openssl compatibility mode with OPENSSL_CPPFLAGS and OPENSSL_LDLIBS pointing to the selected library's headers and libraries. The TLS=auto probe recognizes LibreSSL by its LIBRESSL_VERSION_NUMBER marker, so its OpenSSL-compatible version number is not misclassified. When no supported library is available and TLS is not needed, set TLS=off on the build and check commands.

From an x86 Visual Studio developer prompt:

nmake /f crates\remote-exec-daemon-cpp\NMakefile check-msvc-native OPENSSL_ROOT=C:\path\to\openssl

From an x86 Visual Studio developer prompt with a v141_xp-capable C++11 toolset:

nmake /f crates\remote-exec-daemon-cpp\NMakefile check-msvc-xp OPENSSL_ROOT=C:\path\to\openssl-xp

From an x64 Visual Studio developer prompt with the same XP-capable toolset:

vcvarsall.bat x64 -vcvars_ver=14.16
nmake /f crates\remote-exec-daemon-cpp\NMakefile check-msvc-xp-x64 OPENSSL_ROOT=C:\path\to\openssl-xp-x64

More C++ daemon details live in crates/remote-exec-daemon-cpp/README.md.

Observability

Runtime components log to stderr.

  • The broker keeps stdout reserved for MCP stdio.
  • Broker tool errors include request_id, tool, and target when known.
  • Rust components read REMOTE_EXEC_LOG first, then RUST_LOG.
  • C++ daemon reads REMOTE_EXEC_LOG first, then RUST_LOG.
  • C++ daemon accepts a bare level such as debug, shared filters such as remote_exec_daemon_cpp=debug, and the old remote_exec_daemon_xp=<level> alias.

Examples:

REMOTE_EXEC_LOG=debug cargo run -p remote-exec-daemon -- configs/daemon.example.toml
REMOTE_EXEC_LOG=debug cargo run -p remote-exec-broker -- configs/broker.example.toml
REMOTE_EXEC_LOG='warn,remote_exec_broker=debug,remote_exec_daemon=debug,remote_exec_daemon_cpp=debug'

Development

Rust MSRV is 1.85.0, the first stable release with Rust 2024 edition support.

The Rust broker, Rust daemon, and C++ daemon embed build metadata in their reported version. A build from a Git checkout with git available reports versions in the form 0.2.0+g<short-commit>.<YYYYMMDD>; builds without Git report 0.2.0+<YYYYMMDD>. The SemVer core remains unchanged for compatibility, and the date is the UTC build date.

Full quality gate:

cargo test --workspace
cargo fmt --all --check
cargo clippy --workspace --all-targets --all-features -- -D warnings
crates/remote-exec-daemon-cpp/scripts/clang_format.sh check
make -C crates/remote-exec-daemon-cpp check-posix
make -C crates/remote-exec-daemon-cpp check-windows-2000
make -C crates/remote-exec-daemon-cpp check-windows-xp
make -C crates/remote-exec-daemon-cpp check-windows-x64
make -C crates/remote-exec-daemon-cpp check-windows-xp-x64 TLS=off
make -C crates/remote-exec-daemon-cpp check-windows-nt3x-ws1
make -C crates/remote-exec-daemon-cpp check-windows-nt4-ws1
make -C crates/remote-exec-daemon-cpp check-windows-nt4-ws2

Additional platform checks:

make -C crates/remote-exec-daemon-cpp check-windows WINDOWS_TOOLCHAIN=native WINDOWS_WINVER=0x0501 WINDOWS_WINSOCK_VERSION=2
nmake /f crates\remote-exec-daemon-cpp\NMakefile check-msvc-native
nmake /f crates\remote-exec-daemon-cpp\NMakefile check-msvc-xp
nmake /f crates\remote-exec-daemon-cpp\NMakefile check-msvc-xp-x64

Focused commands:

cargo test -p remote-exec-broker --test multi_target -- --nocapture
cargo test -p remote-exec-broker --test mcp_cli
cargo test -p remote-exec-broker --test mcp_transfer -- --nocapture
cargo test -p remote-exec-broker --test mcp_forward_ports -- --nocapture
cargo test -p remote-exec-daemon --test transfer_rpc -- --nocapture
cargo test -p remote-exec-daemon --test port_forward_rpc -- --nocapture
make -C crates/remote-exec-daemon-cpp test-host-transfer
make -C crates/remote-exec-daemon-cpp test-host-server-runtime
make -C crates/remote-exec-daemon-cpp test-host-server-streaming
make -C crates/remote-exec-daemon-cpp test-windows-xp-server-runtime
make -C crates/remote-exec-daemon-cpp test-windows-xp-server-routes-common

C++ daemon sources are formatted with the root .clang-format:

crates/remote-exec-daemon-cpp/scripts/clang_format.sh format
crates/remote-exec-daemon-cpp/scripts/clang_format.sh check

No-default-features checks:

cargo test -p remote-exec-broker --no-default-features --tests
cargo test -p remote-exec-daemon --no-default-features --tests
cargo test -p remote-exec-host --no-default-features --tests
cargo clippy -p remote-exec-broker --no-default-features --all-targets -- -D warnings
cargo clippy -p remote-exec-daemon --no-default-features --all-targets -- -D warnings
cargo clippy -p remote-exec-host --no-default-features --all-targets -- -D warnings

Windows GNU compile-only checks from Linux:

cargo check --workspace --all-targets --all-features --target x86_64-pc-windows-gnu
cargo clippy --workspace --all-targets --all-features --target x86_64-pc-windows-gnu -- -D warnings
cargo build --workspace --all-targets --all-features --target x86_64-pc-windows-gnu

CI covers Rust broker/daemon paths on Linux, macOS, and Windows, standalone C++ daemon jobs on Linux, macOS, and Windows, C++ formatting, selected Wine runs for legacy GNU Windows binaries when available, and periodic/manual BSD coverage using BSD make. Rust broker integration tests consume a prebuilt C++ daemon binary when one is present and skip C++ daemon scenarios when it is absent.

References

  • AGENTS.md: implementation guidance for coding agents
  • skills/using-remote-exec-mcp/SKILL.md: tool and CLI usage guide for agents
  • configs/broker.example.toml: broker config shape
  • configs/daemon.example.toml: Rust daemon config shape
  • crates/remote-exec-daemon-cpp/README.md: C++ daemon build/runtime guide
  • crates/remote-exec-proto/src/public.rs: public MCP tool schema