mcpx
An MCP server that provides shell-level control over local and SSH-administered hosts.
Run local commands, argv executions, and multi-line bash/sh/Python scripts with cwd, env, stdin, timeout, and output limits.
Launch and manage persistent local PTY sessions: send input, receive output, resize, signal/close, and list sessions.
Run remote commands and open interactive SSH PTYs using inventory aliases or user@host, with jump hosts, ports, keys, and known-hosts policy.
Upload and download files or directories over SFTP.
Create, list, and close SSH port forwards: local, remote, and dynamic SOCKS.
Probe SSH connectivity, list resolved hosts, and report server version, limits, policy mode, and audit status.
Audit calls and emit risk notices; optional guarded/readonly modes, deny lists, Tier-3 confirmation, strict SSH host-key checking, and authenticated HTTP are available.
Operates a local Linux host through the MCP server: run one-shot commands, multi-line bash/sh/python scripts with strict error handling, and persistent local PTY sessions (REPLs, TUIs, prompts, long-running jobs) with send/recv/resize/kill control, all bounded by timeouts, output caps, and an audited append-only trail.
Exposes server metrics over the HTTP transport at GET /metrics in Prometheus text format, including per-tool/per-tier call counters, syscall-notify counters and overflow counts, plus gauges for active sessions, active port forwards, and audit degradation, so a Prometheus instance can scrape tool usage, session load, and security-signal health.
Click on "Deploy Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@mcpx check disk usage on db01 via ssh"
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
Relay Shell
v0.5.2 · Python 3.12 / 3.14 · transports stdio and streamable-http · MCP SDK
mcp>=2,<3 · Apache-2.0
relay-shell is a Model Context Protocol server that gives
an MCP client the full reach of a shell: run commands on the local host, hold interactive PTY
sessions, run scripts, operate a fleet over SSH, move files with SFTP and open port forwards.
It is operator infrastructure for hosts you own and administer. It runs with the privileges of its service account, with no sandbox, and it is open by default: no command is refused for what it does. What it adds is a record and a warning:
Per-call audit: one JSON line with the redacted arguments, the output's hash and length (never its body), the exit code, the client and the assessed tier.
Risk notices: a call classified Tier 2 (changes state) or Tier 3 (irreversible), or an SSH call with host-key checking off, ends with a
[NOTICE ...]line.server_infolists the standing unsafe settings.Refusal is opt-in:
guardedandreadonlymodes, a deny list and a Tier-3 confirmation step are available and all off by default.
Audit records describe completed, denied and cancelled calls; a process crash can
leave an execution without a completion record. Opt-in RELAY_SHELL_AUDIT_INTENT=true
adds a pre-execution record. Runtime sink failures mark audit health degraded while
calls continue; monitor that health and ship evidence off-host. See the
audit lifecycle.
Tools
Local shell | Purpose |
| Run a command with timeout and output caps, cwd, env overlay and stdin. |
| Run a multi-line bash, sh or python script, optionally under |
| Start a persistent PTY session (REPLs, TUIs, prompts, long jobs). |
SSH | Purpose |
| Run a command on a remote host (jump host, key or agent, known-hosts policy). |
| Start an interactive remote PTY session. |
| SFTP transfer, recursive supported. |
| Local ( |
| List or close active forwards. |
| Probe connectivity across the inventory or a host list. |
| Run one command on many hosts in parallel; per-host exit codes in one JSON. |
| Fetch host keys with |
| Show the resolved inventory ( |
Sessions (local and SSH PTY) | Purpose |
| Send input, optionally followed by Enter. |
| Read buffered and new output, with a short wait. |
| Resize the PTY. |
| Signal or terminate a session. |
| List active sessions. |
Diagnostics | Purpose |
| Version, effective limits, policy mode, audit path, unsafe-setting warnings. |
| Read recent audit records, filterable by |
| Arm a Tier-3 confirmation token when that opt-in step is enabled. |
The server also exposes three read-only MCP resources (relay-shell://inventory,
relay-shell://inventory/{host}, relay-shell://ssh-config) and one prompt,
operating_guide, with guidance on which tool to use when; resource reads and prompt fetches
are audited too. Full reference: docs/tools.md. Over HTTP, GET /metrics
serves Prometheus counters and gauges (docs/deployment.md §9a).
Related MCP server: SSH MCP Server
Quickstart
Qualified host profiles are Ubuntu 24.04 LTS with CPython 3.12 and Ubuntu 26.04 LTS with CPython 3.14. Use the matching hashed dependency profile. The package minimum is CPython 3.12; other interpreter/OS combinations are not qualified.
git clone https://github.com/rmednitzer/relay-shell.git && cd relay-shell
python3.12 -m venv .venv
. .venv/bin/activate
python -m pip install --only-binary=:all: --require-hashes -r requirements/development-py3.12.txt
python -m pip install --no-deps --no-build-isolation -e ".[dev]"
python -m pip check
relay-shell # stdio transport
RELAY_SHELL_TRANSPORT=http relay-shell # streamable-http on 127.0.0.1:8080
relay-shell --check-config # validate config, print unsafe-setting notes
relay-shell --verify-deploy # compare installed templates with the shipped onesThe example uses Ubuntu 24.04 / CPython 3.12. On Ubuntu 26.04, use
python3.14 and requirements/development-py3.14.txt instead. For a service
installation using the runtime-only lock and an isolated builder, follow the
deployment guide.
Register with an MCP client (stdio):
{ "mcpServers": { "relay-shell": { "command": "relay-shell" } } }Configuration comes from RELAY_SHELL_* environment variables, optionally via a .env file;
see .env.example and docs/deployment.md.
Default behaviour
With the default configuration every command on every tool is admitted, including rm -rf /,
mkfs, dd to a block device, shutdown, curl | sh and sudo (pinned by
tests/test_notices.py). The call is classified and audited, and a risky one ends with a
notice:
[exit 0]
...command output...
[NOTICE tier 3 (IRREVERSIBLE), matched an irreversible-operation pattern. It may not be
undoable and ran with no confirmation step; verify the target and take a backup or snapshot
first. Classification is a heuristic, not a boundary]The classifier is a regex heuristic over the command text, not a parse of the shell language. A call without a notice is not thereby safe, and a notice does not block anything.
Open mode controls command admission; it does not bypass argument validation, SSH host verification, transport authentication or resource limits. With the default policy, a command is not refused because of its meaning. Important bounds and startup checks include:
Bound | Setting (default) | Behaviour |
Input size (command, stdin, script, env) |
| refused, because the policy scan is synchronous |
One-shot command timeout |
| local process group terminated; remote termination is best effort; use |
Live sessions / forwards |
| a new one is refused at the cap |
Output returned |
| truncated, not refused |
HTTP without OAuth on a non-loopback address |
| refused at startup unless set |
Session output buffers are also bounded, and idle sessions (RELAY_SHELL_SESSION_IDLE_TIMEOUT,
1800 s) and exited, drained sessions are reaped. Persistent sessions have no
maximum lifetime. SFTP transfers default to timeout=0 (no per-call deadline);
set a positive transfer timeout when needed. After a remote timeout or disconnect,
reconcile the remote operation before retrying a mutation.
Making it stricter
Every option below is opt-in and they combine:
Want | Setting |
Refuse state-changing calls unless allow-listed |
|
Observe only |
|
Refuse specific text (checked first, in every mode) |
|
Two-step confirmation for Tier-3 calls |
|
Verified SSH host keys, no per-call downgrade |
|
Tamper-evident audit |
|
Authenticated HTTP transport |
|
Silence the |
|
Security
The server grants real administrative power. Run it as a scoped service account, only on hosts
you are authorized to administer, behind the network controls in
docs/deployment.md. A privileged (root or sudo) deployment is supported
for isolated administrative hosts with strict network controls and full audit shipping.
SECURITY.md has the threat model and how to report a vulnerability.
Besides the audit record, notices and bounds above:
Audit integrity: the log is append-only on disk with a rotation-safe handler. The optional hash chain makes edits, insertions, reorders and interior deletions detectable with
relay-shell --verify-audit(fail-closed); detecting tail truncation needs the off-host copy.Redaction: audited arguments are scrubbed of tokens, keys and
Authorizationmaterial. Heuristic; tested with synthetic secrets.Syscall visibility (optional):
RELAY_SHELL_SECCOMP_NOTIFYrecords a spawned child'sexecve, privilege, namespace, mount, write-openand privilege-relevantprctlsyscalls. It never blocks a syscall and installs only withCAP_SYS_ADMIN.OAuth 2.1 (optional, off by default): dynamic client registration behind an operator approval gate, PKCE, rotating refresh tokens with reuse detection. See
docs/auth.md.Edge: the Caddy config from
deploy/install-edge.shrestricts the MCP endpoint to known CIDRs, adds security headers, caps public OAuth request bodies, keeps authorization codes out of the access log and automates TLS via ACME. The systemd unit applies resource caps.
Compatibility
Surface | Supported | Notes |
Python | 3.12 / 3.14 | Full CI on the corresponding Ubuntu LTS; 3.13 is not a maintained profile. |
Host OS | Ubuntu 24.04 / 26.04 LTS | 24.04 uses Python 3.12; 26.04 uses Python 3.14. Both run full tests and installer/recovery rehearsals. macOS is development-only. |
Windows | SSH target only | Over OpenSSH with PowerShell 7; not supported as a host (no PTY contract, no systemd). |
Transports |
| stdio is the default; HTTP binds loopback and needs a TLS edge. |
MCP SDK |
| Pinned in |
SSH library |
| Native async; no shell-out to the system |
Development
ruff check . && ruff format --check .
mypy
pytestLayout: src/relay_shell/ (server and modules), deploy/ (systemd unit, Caddyfile, logrotate,
installers, off-host audit sync, monitoring), docs/ (start at
docs/index.md), tests/ (unit and integration; an in-process SSH server,
no network).
CONTRIBUTING.md covers scope, branch naming, the local loop and review of
security-sensitive changes; docs/runbook.md holds the audit, review and
validation procedures and the backlog. AI agents follow AGENTS.md (with
CLAUDE.md for Claude). Participation is governed by the
Code of Conduct.
License
Available Tools
18 toolsserver_infoA
Report version, effective limits, policy mode, and audit status.
Inputs: no named parameters. Returns: result
session_killA
Signal a session and (by default) close and reap it.
Inputs: session_id (required), signal_name, close. Returns: result
session_listA
List active sessions with size, age, idle, and byte counters.
Inputs: no named parameters. Returns: result
session_recvA
Read buffered/new output from a session, waiting up to timeout seconds.
Inputs: session_id (required), timeout, max_bytes. Returns: result
session_resizeC
Resize a session's PTY.
Inputs: session_id (required), cols (required), rows (required). Returns: result
session_sendC
Send input to a session (local or SSH). enter appends a newline.
Inputs: session_id (required), data (required), enter. Returns: result
shell_execB
Run a shell command on the local host and return its combined output.
Timeout and output size are clamped to the server limits. Set
use_shell=false to exec an argv without a shell.
Inputs: command (required), timeout, max_output, cwd, +4 more. Returns: result
shell_scriptA
Run a multi-line script (bash/sh/python) fed on stdin.
With strict and a shell interpreter, set -euo pipefail is
prepended so failures abort early.
Inputs: script (required), interpreter, strict, timeout, +2 more. Returns: result
shell_spawnB
Start a persistent local PTY session; returns a session id.
Drive it with session_send / session_recv.
Inputs: command, cols, rows, cwd, +1 more. Returns: result
ssh_checkC
Probe connectivity to the given hosts (or the whole inventory).
Inputs: hosts, timeout. Returns: result
ssh_downloadC
Download a file or tree from a remote host via SFTP.
Inputs: host (required), remote_path (required), local_path (required), recursive, +5 more. Returns: result
ssh_execA
Run a command on a remote host over SSH and return its output.
host may be an inventory/ssh_config alias or user@host.
known_hosts is strict | accept-new | ignore.
Inputs: host (required), command (required), timeout, user, +4 more. Returns: result
ssh_forwardC
Create a port forward.
Spec: L:lport:dhost:dport (local), R:rport:dhost:dport
(remote), or D:lport (dynamic SOCKS).
Inputs: host (required), spec (required), user, port, +3 more. Returns: result
ssh_forward_closeB
Close an SSH port forward by id.
Inputs: forward_id (required). Returns: result
ssh_forward_listB
List active SSH port forwards.
Inputs: no named parameters. Returns: result
ssh_hostsA
Show the resolved host inventory (ssh_config + inventory file).
Inputs: no named parameters. Returns: result
ssh_spawnB
Open a persistent interactive PTY on a remote host; returns a session id.
Inputs: host (required), command, cols, rows, +5 more. Returns: result
ssh_uploadB
Upload a file or tree to a remote host via SFTP.
Inputs: host (required), local_path (required), remote_path (required), recursive, +5 more. Returns: result
Tool Schema Changelog
Recent tool additions, removals, and schema changes observed during successful MCP inspections.
Loading tool schema changelog…
TDQS
Scored across 18 tools
Most tools target distinct operations (e.g., session vs shell vs SSH), but shell_exec and shell_script overlap in running commands, and session_send/recv are closely tied to shells. Overall, an agent can distinguish them with careful reading.
Naming is predominantly <domain>_<action> with underscores, e.g., ssh_upload, session_kill. A few exceptions like server_info (reverse order) and ssh_hosts (noun) break the pattern slightly, but consistency is high overall.
18 tools cover a well-scoped domain of remote execution and session management without being excessive. Each tool serves a clear purpose, and the count feels appropriate for the functionality offered.
The set covers local and remote execution, interactive sessions, file transfer, port forwarding, and server info. Minor gaps exist (e.g., no remote file listing, no SSH key management), but core workflows are supported.
Maintenance
Related MCP Connectors
Scoped, audited SSH exec, sessions, and SFTP on your saved servers without exposing credentials
Termalin's hosted MCP server. Claude, ChatGPT or any MCP client runs commands, moves files over SFTP, queries databases and reads server metrics and monitoring alerts on your servers, keylessly through Termalin Connectors: no SSH key is handed over and no inbound port is opened. Per-host command policy and an audit log. Needs a termal.in Pro or Team account (new accounts start with a 14-day Pro trial).
Manage remote servers, SSH terminals, files, databases and proxies with scoped access and approvals.
MCP server for mandates, delegation, policy-gated execution, credential grants, and audit.
Related MCP Servers
- AlicenseCqualityDmaintenanceA secure server that implements the Model Context Protocol (MCP) to enable controlled execution of authorized shell commands with stdin support.1MIT
- AlicenseBqualityDmaintenanceA server that enables remote command execution over SSH through the Model Context Protocol (MCP), supporting both password and private key authentication.110 npm2MIT
- FlicenseAqualityDmaintenanceA local Model Context Protocol server that allows LLMs to securely execute shell commands on remote Linux and Windows systems via SSH connections.610 npm2-
- AlicenseAqualityAmaintenanceA Model Context Protocol server that allows LLMs to securely execute shell commands on remote Linux and Windows systems via SSH. It supports password and key-based authentication, command timeouts, and sudo elevation for administrative tasks.1516,179 npm869MIT