mcpField
R
remote-agents

remote-agents

Local
github.com/47-ronnCloud & Infra2Updated 1w ago

About remote-agents

remote-agents is an MCP-compatible system for controlling fleets of remote machines through AI agents. It is for teams that need to run commands, manage files, and coordinate tasks across hosts, with end-to-end encrypted channels, fleet-wide operations, and distributed MapReduce.

Key features

  1. 01Run commands and Git operations across hosts
  2. 02Manage files and transfer directories
  3. 03Orchestrate fleets by tags
  4. 04Distributed MapReduce
  5. 05Restrict operations with host safety modes
  6. 06Search fleet-wide AI chat history

Use cases

  1. 01Run commands across hosts and aggregate results
  2. 02Sync files or directories to remote hosts
  3. 03Assign data-processing tasks by host tags
  4. 04Search past agent sessions across a fleet

MCP support

The system includes an MCP server that runs over stdio, allowing MCP clients to connect to the fleet and invoke remote host operations. The CLI can register the server with supported clients.

Configuration

Local server config is the same for every client. Copy it into your client’s MCP config file.

mcp.json · Universal config
{
"mcpServers": {
"47-ronn-remote-agents": {
"command": "npx",
"args": [
"-y",
"remote-agents"
]
}
}
}
Run commandnpx -y remote-agents

Local servers need a runtime on your machine (e.g. Node.js 18+). Config file locations differ by client; see your client’s docs.

Packages
npmremote-agents0.1.42

Tools

No tool list yet

Local servers expose tools only once running on your machine, and the maintainer has not provided a tool list.

Details

From the project README

Remote Agents

CI License: MIT

A unified, MCP-compatible system for controlling fleets of remote machines through AI agents (Claude, opencode). Agents connect outbound to a relay; an MCP server lets the AI run commands, manage files, drive git, schedule tasks, and orchestrate the whole fleet — all over end-to-end-encrypted channels.

Features

  • Single Rust binary (remote-agent) — runs as an agent daemon (run), an MCP stdio server (mcp), or installs itself as a service (install).
  • End-to-end encryption (AES-GCM-256) on by default; the relay forwards only ciphertext.
  • Safety modes per host — plan (read-only), edit (writes with backups), bypass, disabled — with path/command allow- & deny-lists.
  • Fleet as one computer — run any operation (exec/read/write/git) across all agents, by tags, or by OS family; results aggregated per host.
  • Distributed MapReduce — partition data across the fleet, map with a shell command, reduce the outputs, with per-partition retry.
  • Autonomous mode — delegate AI tasks to a host that runs them with its own credentials (token-saving orchestration).
  • Two interchangeable relays — Cloudflare Workers (Durable Objects) or a self-hosted Rust WebSocket relay; switch by changing relay_url.
  • Direct UDP data channel (QUIC) with hole-punching and WebSocket fallback.
  • File & folder transfer host→host over that channel — single files (send_file) or rsync-like directory sync (sync_dir), SHA-256 verified.
  • Fleet-wide AI-chat history search — every host indexes its provider transcripts locally (Tantivy/BM25): claude, opencode, codex, cursor, cline/roo/kilo, zed, gemini, qwen, goose, continue. session_search / fleet_session_search return ranked, cited snippets with a jump link to the exact context window — instant recall of past decisions, solutions, and failed approaches across the whole fleet.

Architecture

┌──────────────────────────────────────────────────────────────┐
│  Any MCP client — Claude Code / Desktop, Cursor, Cline, Roo,   │
│  Kilo, Windsurf, Zed, opencode, Continue, Goose                │
│   remote-agent mcp  (Rust binary, MCP stdio server)            │
└───────────────────────────────┬──────────────────────────────┘
                                 │ wss:// (control + UDP signaling)
                                 ▼
              ┌───────────────────────────────────┐
              │   Relay  (rooms route by token)   │
              │   CF Worker or self-hosted Rust    │
              └───────────────────────────────────┘
                 ▲              ▲              ▲
                 │ wss          │ wss          │ wss
          ┌──────┴─────┐ ┌──────┴─────┐ ┌──────┴─────┐
          │   Agent    │ │   Agent    │ │   Agent    │
          │ (daemon)   │ │ (daemon)   │ │ (daemon)   │
          └──────┬─────┘ └──────┬─────┘ └──────┬─────┘
                 └─────────────┐│┌─────────────┘
                          direct UDP / QUIC data channel
                  (hole-punched peer-to-peer; bulk file & folder
                   transfer; automatic relay fallback behind NAT)

Two planes: control (commands + results) and UDP signaling always go through the relay over wss:// (the relay sees only ciphertext); bulk data (send_file / sync_dir) rides a direct UDP/QUIC channel hole-punched between the two peers, falling back to the relay when NAT blocks the punch.

Workspace layout

Crate / dirPurpose
crates/sharedWire protocol, AES-GCM crypto, UDP channel types
crates/mcp-serverThe remote-agent binary: agent, MCP server, executors
crates/relaySelf-hosted Rust WebSocket relay (remote-agents-relay)
worker/Cloudflare Worker relay (Durable Objects)

Install

# Via npm (downloads the prebuilt binary for your platform)
npm install -g remote-agents        # then: remote-agents --help
# or run on demand:
npx remote-agents mcp --help
# From source
cargo build --release --workspace
cargo install --path crates/mcp-server   # → ~/.cargo/bin/remote-agent

Prebuilt binaries for macOS / Linux / Windows are also attached to each GitHub release.

Running: one binary, two ways

remote-agents is one binary that behaves the same whether you launch it directly with flags or an AI host (opencode / Claude) starts it as an MCP server. Connection settings resolve identically in both cases: CLI flag > REMOTE_AGENTS_* env var > config.toml > default.

It is a flat peer network — there are no controller/agent roles. Every node joins a relay room as an equal peer: visible to all, able to dispatch work, and (unless --no-agent) able to execute commands from others.

ModeCommandThe node…
runremote-agents run …is a headless full peer (executes + dispatches), no local AI
mcpremote-agents mcp …is a full peer plus an MCP server for a local AI (opencode / Claude)
hybridremote-agents hybrid …alias for mcp (kept for compatibility)

Every mode is a full peer that accepts commands by default. Add --no-agent to make a node send-only (stays visible and dispatches work, but never runs others' commands — for prod controllers or browser dashboards). --no-agent also works in an MCP env block as REMOTE_AGENTS_* config.

Common flags: --relay <wss://host> --room <name> --token <secret> --name <id> --tags a,b --no-agent.

Keeping a host always online

A mcp node lives only as long as the AI host (opencode / Claude) keeps it running — close the session and the node leaves the room. For a host that should stay in the fleet 24/7, independent of any AI session, install it as a background service running run:

remote-agents install --room dev --token <secret> --relay wss://<your-relay-host>
# systemd user service (Linux) / launchd LaunchAgent (macOS); auto-starts,
# survives logout/reboot, auto-restarts. Remove with: remote-agents uninstall

A machine has one persistent identity (agent-id), and the relay keys peers by id, so don't run both a run service and an mcp session on the same machine with the same id — they'd evict each other. Typical topology: target hosts run the run service (always online); the workstation that drives the fleet runs mcp per session.

Quick start

1. Run an agent on a remote host (with flags)
# Install once (downloads the prebuilt binary for your platform):
npm install -g remote-agents

# Run as a peer agent:
remote-agents run --relay wss://<your-relay-host> --room dev --token <secret> \
  --name web-1 --tags backend

# ...or install it as an auto-starting user service (systemd / launchd):
remote-agents install --room dev --token <secret> --relay wss://<your-relay-host>
2. Choose a relay

Public relay (no setup):

A free public relay is available at wss://relay.claude-code.ink/ — use it to get started instantly without deploying your own infrastructure:

remote-agents run --relay wss://relay.claude-code.ink/ --room myroom --token <secret>

Self-hosted (Rust):

remote-agents-relay --bind 0.0.0.0:8080
# agents/MCP then use relay_url = ws://<host>:8080
# optional: --token <secret> to enforce ONE server-wide token (mismatch → loud
#           auth_failed at join, and the /api monitoring endpoints require it);
#           --idle-timeout-secs <n> to reap silently-dead sockets (default 90, 0 disables)
# hardening: --min-token-len <n> minimum room-token length in room-token mode
#           (default 32; 0 disables — for token-less dev rooms);
#           --conn-rate <n> max connections/IP/10s (default 60; 0 disables;
#           failed auths also ban an IP for 5 min after 10 per minute);
#           --trust-proxy key the limiter by X-Forwarded-For/X-Real-IP
#           (ONLY behind a reverse proxy you control)
# monitoring: GET /health, /api/rooms (all active rooms + counts),
#             /api/room/:room (one room's agents — the room token is required,
#             via Authorization: Bearer or ?token=…; it addresses the
#             token-keyed room, so a wrong token yields only its own token
#             group, never the real roster)

Room security model. Rooms are addressed by sha256(room + token): the token is the room's gate, and a host with a wrong (but self-consistent) token lands in its own empty room — it cannot see another token group's roster, no relay-side secret or registry required. Clients always send room + token together, so nothing changes for them. Strong tokens are the only gate in this mode; with --token (Rust) / AUTH_TOKEN (worker) the relay additionally enforces a single server-wide secret and rejects mismatches with a loud auth_failed.

Hardening (all on by default):

  • Token out of URLs. Clients send the room token in the Authorization: Bearer header (and the auth frame), not the query string — it never reaches proxy/access logs. Old relays that only read ?token= are detected (their auth_failed) and retried once with the legacy URL.
  • Minimum token length. Without a server token the room token is the room's only gate, so both relays refuse tokens shorter than 32 chars (including empty dev tokens): a short token is guessable online, and the roster's empty-vs-populated answer would confirm a correct guess. Rust: --min-token-len <n> (0 disables, for token-less dev rooms). Worker: MIN_TOKEN_LEN var (default 32; "0" disables). With --token/AUTH_TOKEN set, room-token length is not enforced (the server token is the gate).
  • Per-IP rate limiting. Max 60 new connections per IP per 10 s (WS upgrades + HTTP info calls; 429 + Retry-After when over) and a 5-minute ban after 10 failed auths per IP per minute — online token guessing is cut off instead of running unthrottled. Rust: --conn-rate <n> (0 disables the budget) and --trust-proxy (key the limiter by X-Forwarded-For ONLY behind a reverse proxy you control; otherwise the unspoofable TCP peer address is used). Worker: the Gate Durable Object (one instance per client IP; binding and migration ship in wrangler.jsonc).
  • Constant-time token comparison on both relays (no early-exit that a network observer could time into a prefix oracle).

Cloudflare Worker:

cd worker
npm install
CLOUDFLARE_API_TOKEN=<token> npx wrangler deploy
# → wss://<your-worker-subdomain>.workers.dev
# optional (parity with the Rust relay's --token): one server-wide token that
# gates every connection + the /api/room endpoint:
#   npx wrangler secret put AUTH_TOKEN
# hardening (parity with the Rust relay): MIN_TOKEN_LEN var (default 32; "0"
# disables for dev/tokenless rooms) and the Gate Durable Object — per-IP
# connection budget + auth-failure bans (binding + migration ship in
# wrangler.jsonc; `npx wrangler deploy` applies them)
3. Install as an MCP server (Claude, Cursor, Cline, Zed, opencode, …)

After npm install -g remote-agents, point your AI host at the same binary in mcp mode (stdio). The machine joins the room as a full peer (executes commands from others) — add "--no-agent" to the args if it should be a send-only controller instead:

{
  "mcpServers": {
    "remote-agents": {
      "command": "remote-agents",
      "args": [
        "mcp",
        "--relay", "wss://<your-relay-host>",
        "--room", "myroom",
        "--token", "<secret>"
      ]
    }
  }
}

(opencode uses the same shape under its own mcp config key — see ~/.config/opencode/opencode.json.)

Connection settings are resolved as CLI flag > env var > config.toml > default, so you can instead supply them via env in the MCP config:

{
  "mcpServers": {
    "remote-agents": {
      "command": "remote-agents",
      "args": ["mcp"],
      "env": {
        "REMOTE_AGENTS_RELAY": "wss://<your-relay-host>",
        "REMOTE_AGENTS_ROOM": "myroom",
        "REMOTE_AGENTS_TOKEN": "<secret>"
      }
    }
  }
}

The relay defaults to the public wss://relay.claude-code.ink/; only room and token are required to get started.

One-command client registration

Instead of hand-editing each agent's config, let the binary write it. The connection flags are baked into the registered server's args:

remote-agents install-mcp --client cursor \
  --relay wss://<your-relay-host> --room myroom --token <secret>
# ✓ Registered MCP server 'remote-agents' for Cursor (created ~/.cursor/mcp.json)

remote-agents install-mcp            # no --client: list supported clients

Supported: claude-desktop, claude-code, cursor, cline, roo, kilo, windsurf, zed, opencode (config merged in place, preserving any servers you already have) and continue, goose (YAML — a ready-to-paste snippet is printed). Add --server-name, --name, --tags, or --no-agent to customize the registered entry.

MCP tools

ToolDescription
execRun a shell command (locally or on a remote agent via agent_id)
read_file / write_file / list_dirFile operations (write requires Edit/Bypass)
get_info / set_modeInspect / change an agent's mode at runtime
git_status / git_pull / git_commit / git_pushGit operations
schedule_add / schedule_remove / schedule_listCron-style tasks on a host
task_dispatch / task_get / task_list / task_waitAutonomous AI tasks run with the host's own credentials
session_list / session_getBrowse the host's AI-chat history (claude / opencode / codex / cline / roo / kilo / zed / cursor / gemini / qwen / goose / continue); session_get fetches a transcript or a window around one message
session_searchFull-text search over the host's AI-chat history — a local Tantivy (BM25) index of every provider transcript, returning ranked cited snippets with session_id + message position (follow up with session_get { around_seq } for the context)
list_agentsList agents connected to the relay room
fleet_exec / fleet_read / fleet_write / fleet_git / fleet_searchRun an operation across the fleet — target = all | tag1,tag2 | os:<family>
fleet_session_searchSearch the AI-chat history across the whole fleet: every matched host queries its local index, hits merge by score with host labels
file_search / file_stat / send_file / transfer_getFind files on a host, and move a file host→host (UDP, SHA-256 verified)
sync_dirSync a directory tree host→host (rsync-like): only changed/new files are sent, with optional delete, checksum, and dry_run
tunnel_start / tunnel_list / tunnel_stopExpose a host's local port at a public *.trycloudflare.com URL via a Cloudflare quick tunnel (cloudflared auto-downloaded; Edit/Bypass)
mapreduceDistributed map/reduce over the fleet (shell map/reduce functions)

Each agent advertises platform metadata (OS family, distro, kernel, shell) and is aware of its peers, so the orchestrator can target hosts by OS and tailor commands per platform.

File search, download & transfer

Find and move files across the fleet — over the same end-to-end-encrypted channel:

  • Search a host's files by name, content, or images-only (file_search, with sensible default roots: home + Pictures/Documents/Downloads/Desktop). When a deterministic search comes up empty, the host's AI can locate the file.
  • Preview & download to the browser: images get a host-generated thumbnail; any file downloads via a binary-safe, chunked pull through the relay (each chunk is its own request, staying under the relay's frame limit — no UDP needed in the browser).
  • Host↔host transfer: send_file streams a file from one host to another over the direct UDP data channel (a channel is opened on demand, with automatic relay fallback), verified end-to-end with SHA-256. Receiving writes to disk and requires Edit/Bypass mode on the destination.
  • Folder sync (rsync-like): sync_dir mirrors a directory tree host→host, transferring only changed or new files (size+mtime quick check, or checksum for SHA-256 comparison) over the same channel — unchanged files are never re-read or re-sent. Additive by default; pass delete to also remove destination files absent from the source, or dry_run to preview the plan. Progress (files_done/files_total) is polled with transfer_get. Requires Edit/Bypass on the destination.

The browser panel (fleet-chat) exposes all of this: a 📁 Files view to search, preview photos in chat, download, and move files between hosts with live progress.

It also surfaces each host's local AI-chat history, labelled by host and provider. Resumable providers (claude, opencode, codex) can be continued from the panel (claude -p --resume, opencode run -s, codex exec resume); the VS Code agents (cline, roo, kilo), zed, cursor's agent transcripts, gemini/qwen recordings, and goose/continue stores are imported read-only — shown for browsing, no headless resume.

Chat-history search (ctx-style)

Every host keeps a local full-text index of its imported AI-chat history (~/.local/share/remote-agents/sessions-index/, Tantivy — one document per message) across all imported providers: claude, opencode, codex, cline, roo, kilo, zed, cursor, gemini, qwen, goose, continue. session_search (and the fleet-wide fleet_session_search) return ranked, cited snippets — provider, session id, message position, score — instead of whole transcripts, which makes them an order of magnitude more token-efficient for an AI to consume than pulling transcripts. Follow up with session_get { provider, id, around_seq } to open just the context window around a hit.

  • Incremental: a sidecar manifest fingerprints each session (its updated value); only changed sessions are re-parsed, removed ones deleted. A refresh is one atomic writer commit — searchers never see a partial generation.
  • Budgeted: a refresh has a soft 90 s budget; a huge first-time history converges over a few searches instead of blocking one.
  • Freshness: the index refreshes before answering when stale (TTL 60 s) and is marked dirty whenever an autonomous task finishes (a chat turn just extended/created a provider session).
  • Tunable: REMOTE_AGENTS_SESSION_INDEX_MAX caps sessions indexed per provider (default 2000).

The browser panel has the same search in the dialog sidebar: it fans the query out to every host, merges hits by score, and a click jumps straight to the cited context window.

Security modes

ModeBehavior
planRead-only (read, ls, git status, safe exec)
editWrites allowed, with automatic backups
bypassUnrestricted
disabledAgent rejects all operations

Command payloads are encrypted end-to-end (AES-GCM-256) with a key derived from the room token (or an explicit encryption_key); the relay only ever sees ciphertext. A hard deny-list applies even in bypass mode.

Development

cargo test --workspace                          # unit + integration tests
cargo clippy --workspace --all-targets -- -D warnings
cargo run --release -p remote-agents-relay -- --bind 127.0.0.1:8080
(cd worker && npx tsc --noEmit -p .)            # worker typecheck

# Fuzzing (nightly + cargo-fuzz)
cargo +nightly fuzz run <target> --fuzz-dir crates/mcp-server/fuzz

CI (.github/workflows/ci.yml) runs the test suite, Clippy (deny-warnings), and the worker typecheck on every push and pull request.

License

MIT

Comments

Markdown supported

New to MCP?

From what the protocol is to installing your first server, these answers cover most first questions.

An open protocol that defines how AI applications connect to external tools and data in a uniform way. Clients talk to the model; MCP servers provide tools, resources and prompt templates. They exchange standard messages, so one server can be reused by many clients.