Skip to content

nks-hub/remote-cmd

Repository files navigation

CI Release GitHub Stars .NET

RemoteCmd v1.1.0

Remote command execution relay for AI agents. Execute PowerShell commands and transfer files on remote machines through NAT/firewalls via HTTP polling. Multi-client support — a single relay can serve many target machines and route commands to a specific one by name.

Architecture

+---------------------+     +----------------------+     +---------------------+
|   MCP Client        |     |   Relay Server       |     |   Target Machines   |
|   (Claude Code)     |     |   (.NET 9.0)         |     |   (.NET 9.0)        |
|                     |     |                      |     |                     |
|  +---------------+  |     |  HTTP API :7890      |     |  +---------------+  |
|  | MCP Server    |--+-----+-> /api/exec          |     |  | Client A      |  |
|  | (Node.js)     |  |     |  /api/upload         |<----+--| (polling)     |  |
|  | stdio         |  |     |  /api/download       |     |  +---------------+  |
|  +---------------+  |     |  /api/clients        |<----+--| Client B ...  |  |
|                     |     |                      |     |  +---------------+  |
+---------------------+     +----------------------+     +---------------------+

Components

Component Runtime Description
RemoteCmd.Server .NET 9.0 HTTP relay server, accepts commands and proxies to the targeted client
RemoteCmd.Client .NET 9.0 Runs on each target machine, polls server, executes via PowerShell
RemoteCmd.Shared .NET 9.0 Shared Crypto (AES-256-GCM) library
mcp-server Node.js MCP (Model Context Protocol) bridge for Claude Code integration

How it works

  1. Each Client on a target machine registers with a stable clientId (persisted to disk) and a human-readable name (default: Environment.MachineName, override via --name).
  2. Client polls Server every 800 ms for pending commands and file transfers scoped to its session.
  3. Controller (Claude Code via MCP, or curl) sends a command to the Server, optionally with ?client=<name|id> to target a specific machine.
  4. The Server queues the command on that client's session, waits for the result, returns it.

Clients only need outbound HTTP. No inbound ports on the target machines.

Quick Start

1. Start the Relay Server

dotnet run --project RemoteCmd.Server -- <TOKEN>

# With env vars (useful for systemd, containers, tests):
REMOTECMD_TOKEN=<TOKEN> REMOTECMD_NO_TLS=1 dotnet run --project RemoteCmd.Server

Server listens on http://0.0.0.0:7890 (or https:// with TLS). Token is used for authentication on all API endpoints.

2. Start Clients on Target Machines

# From source
dotnet run --project RemoteCmd.Client -- <SERVER_IP> <TOKEN> [--name <alias>]

# Or the published self-contained exe:
RemoteCmd.Client.exe <SERVER_IP> <TOKEN> --name comos-1

Each client persists its GUID to %LOCALAPPDATA%\RemoteCmd\client.<name>.id (Linux/macOS: $XDG_DATA_HOME/RemoteCmd/ or ~/.local/share/RemoteCmd/). The ID survives restarts. The id file is scoped per --name, so multiple aliased instances on the same machine (e.g. elevated + non-elevated) get distinct ids and don't compete for the same session. A legacy client.id is auto-migrated for the default machine-name instance.

3. Configure MCP for Claude Code

{
  "mcpServers": {
    "remote-cmd": {
      "type": "stdio",
      "command": "node",
      "args": ["<path-to>/mcp-server/index.mjs"],
      "env": {
        "REMOTECMD_URL": "https://localhost:7890",
        "REMOTECMD_TOKEN": "<TOKEN>",
        "REMOTECMD_DEFAULT_CLIENT": "comos-1"
      }
    }
  }
}

REMOTECMD_DEFAULT_CLIENT is optional — when set, tools default to that client unless overridden via the client argument.

4. Use via curl

# List all clients
curl "http://localhost:7890/api/clients?token=<TOKEN>"

# Execute command on the single connected client (auto-select)
curl -X POST "http://localhost:7890/api/exec?token=<TOKEN>" \
  -H "Content-Type: application/json" \
  -d '{"command":"hostname","timeoutSeconds":30}'

# Target a specific client by name
curl -X POST "http://localhost:7890/api/exec?token=<TOKEN>&client=comos-1" \
  -H "Content-Type: application/json" \
  -d '{"command":"hostname"}'

# Upload file to a specific client
curl -X POST "http://localhost:7890/api/upload?token=<TOKEN>&client=comos-1&path=C:\dest\file.zip" \
  --data-binary @local.zip

MCP Tools

Tool Description
remote_list_clients List all known clients with connection status
remote_status Check aggregate or single-client status
remote_exec Execute PowerShell on a target client
remote_upload Upload file from local to remote client (max 200MB)
remote_download Download file from remote client to local (max 200MB)

Every tool except remote_list_clients accepts an optional client argument (name or id). When omitted, the server auto-selects if exactly one client is connected; otherwise an error with the list of connected clients is returned.

API Reference

All endpoints require a token — via ?token=<TOKEN>, X-Token: <TOKEN> header, or Authorization: Bearer <TOKEN>.

Controller endpoints

Method Endpoint Description
GET /api/clients List all clients ({count, connected, clients: [...]})
GET /api/status[?client=X] Aggregate status; with client returns per-client details
POST /api/exec[?client=X] Execute command {"command":"...","timeoutSeconds":30}
POST /api/upload?path=<remote>[&client=X] Upload file (binary body)
GET /api/download?path=<remote>[&client=X] Download file

Client-facing polling endpoints

Clients identify themselves via ?clientId=<guid>&name=<hostname> on every polling request.

Method Endpoint Description
GET /api/poll Poll for pending command (encrypted)
POST /api/result Post encrypted command result
GET /api/file-poll Poll for pending file transfer
GET /api/file-data Download file data for upload-to-remote
POST /api/file-done Confirm file saved
POST /api/file-upload Upload file data for download-from-remote

Target resolution rules

  1. If ?client=<name|id> is specified → that session (404 if unknown, 400 if not connected).
  2. Else if exactly one client is connected → that one.
  3. Else → error listing connected client names.

Build

# Build + test
dotnet build RemoteCmd.sln
dotnet test RemoteCmd.sln

# MCP tests
cd mcp-server && npm install && npm test

# Publish self-contained client
dotnet publish RemoteCmd.Client -c Release -r win-x64 --self-contained \
  -p:PublishSingleFile=true -p:IncludeNativeLibrariesForSelfExtract=true \
  -o publish/client

Environment Variables

Variable Where Default Description
REMOTECMD_TOKEN Server, MCP Shared authentication token
REMOTECMD_NO_TLS Server unset 1/true disables TLS (fallback when --no-tls is not passed)
REMOTECMD_URL MCP https://localhost:7890 Relay URL
REMOTECMD_DEFAULT_CLIENT MCP unset Name or id of client to target when client arg is omitted

Security

Layer Technology Scope
Transport TLS 1.2+ (self-signed cert) Server ↔ Client HTTPS
Payload AES-256-GCM All commands, results, file data, metadata
Authentication Shared token, constant-time comparison All /api/* endpoints

Token may be passed via ?token=, X-Token: header, or Authorization: Bearer. Prefer the header or Bearer form in production — query strings leak into proxy logs.

Use --no-tls (or REMOTECMD_NO_TLS=1) on the server for HTTP-only mode (AES payload encryption stays active).

Technical Details

Parameter Value
Client poll interval 800 ms
Command timeout Configurable per request (default 30 s, max 300 s)
Process kill timeout 60 s (client-side)
File transfer timeout 5 minutes
Max file size / body 200 MB
Auto-reconnect Exponential backoff (1 s → 30 s)
Concurrency Per-client SemaphoreSlim(1) — each machine serial, multiple machines in parallel
Shell powershell.exe -NoProfile -NonInteractive
Client detection Connected if last poll < 10 s ago

Project Structure

RemoteCmd.sln
├── RemoteCmd.Shared/       # Shared Crypto (AES-256-GCM)
├── RemoteCmd.Server/       # HTTPS relay server
├── RemoteCmd.Client/       # Target machine client
├── RemoteCmd.Tests/        # xUnit unit + integration tests
├── mcp-server/             # MCP bridge (Node.js)
│   └── tests/              # node --test validation tests
├── .github/workflows/      # CI + Release
│   ├── ci.yml
│   └── release.yml
└── rcmd.sh                 # Shell helper

Contributing

  1. Fork the repository
  2. Create your feature branch (git checkout -b feature/amazing-feature)
  3. Commit your changes (git commit -m 'feat: description')
  4. Push to the branch (git push origin feature/amazing-feature)
  5. Open a Pull Request

Support

License

Private — NKS Development


Made by NKS Hub

About

Remote command execution relay for AI agents - MCP server + HTTP relay + client for remote PowerShell execution and file transfer through NAT/firewalls

Resources

Stars

Watchers

Forks

Packages

 
 
 

Contributors