bladerunner-mcp
MCP server that lets an AI session operate your remote machines over SSH.
⚠️ These tools run real commands on real servers with your credentials. Running your MCP client in auto-approve ("YOLO") mode means the model can execute anything on your machines without you seeing it first. Keep manual approval on for this server, use least-privilege SSH accounts, and have backups/snapshots for anything you point it at. See Security.
You pip install bladerunner-mcp, describe your hosts (e.g. a Hetzner box) in a
YAML config with credentials, and the server exposes MCP tools to run commands,
track long-running processes and transfer files on those hosts. The model only
ever sees host aliases — secrets never enter the conversation.
Scope: POSIX remote hosts only (sh, nohup, tail, head, wc must be
present on the target). Windows hosts are not supported.
Tools
| Tool | Description |
|---|---|
list_hosts |
List configured host aliases (no secrets) |
run_command |
Run a shell command on a host, return stdout/stderr/exit code (capped at 64 KiB per stream) |
read_output |
Page through the full output of a truncated run_command or a background process |
start_process |
Start a long-running command in the background (nohup), return pid + work_dir |
check_process |
Report process status (running/succeeded/failed/unknown), exit code and log tails |
kill_process |
Kill a background process |
put_file |
Upload a local path to a host via rsync |
get_file |
Download a remote path from a host via rsync |
Large outputs: run_command keeps the full output on the host (in a work_dir
under /tmp/bladerunner_mcp/) whenever it exceeds the 64 KiB cap and returns
truncated: true — page through it with read_output(host, work_dir, offset=...).
Installation
pip install bladerunner-mcp
Configuration
Copy bladerunner_mcp.example.yaml to
~/.bladerunner_mcp.yaml (or set BLADERUNNER_MCP_CONFIG to its path):
hosts:
hetzner-prod:
host: 203.0.113.10
user: root
port: 22
key_path: ~/.ssh/id_ed25519
strict_host_key: true # verify against system known_hosts (default: false)
allow_dangerous: false # keep the dangerous-command filter on (default)
Register with a client
Claude Code:
claude mcp add bladerunner -- bladerunner-mcp
Claude Desktop (claude_desktop_config.json):
{
"mcpServers": {
"bladerunner": {
"command": "bladerunner-mcp",
"env": { "BLADERUNNER_MCP_CONFIG": "/Users/me/.bladerunner_mcp.yaml" }
}
}
}
Security
- Do not run your MCP client in auto-approve ("YOLO") mode with this server.
Every
run_command/start_processcall is a real shell on a real machine; keep per-call approval on so you see each command before it runs. - Protected-path filter (commands) — mutating operations (
rm,mv,chmod,chown,shred,truncate,chattr,sed -i,tee, output redirects) on system paths (/,/etc,/boot,/bin,/sbin,/usr,/lib*,/dev,/sys,/proc,/root,/var/lib) are rejected; reads of the same paths and mutations elsewhere pass. Catastrophic non-path patterns (mkfs,dd of=/dev/...,shutdown/reboot, fork bombs,DROP TABLE/DATABASE) are also rejected. Override with per-hostprotected_paths. This is a seatbelt against accidents, not a security boundary: shell is not statically analyzable. Disable per host withallow_dangerous: true. - Path validation (transfers) —
put_file/get_filepaths are expanded and normalized (..resolved) and then enforced for real: protected system prefixes are denied on the remote side, and~/.ssh,~/.gnupg,~/Library/Keychainsplus system dirs on the local side (both directions — exfiltrating local keys is as bad as overwriting them). Optional per-hostallowed_remote_paths/allowed_local_pathsrestrict transfers to listed prefixes. Remote symlinks are not resolved (known limit). - Least privilege — point the server at dedicated SSH accounts with the
minimum rights the task needs, not at
root, and keep backups/snapshots of anything it can touch. - Host keys — by default unknown host keys are auto-accepted (convenient,
MITM-unsafe). Set
strict_host_key: trueper host to verify against your systemknown_hosts(applies to both paramiko and rsync). - Auth — prefer SSH keys. Password auth works (rsync falls back to
sshpass, which exposes the password in the local process list) and the password sits in plaintext YAML; treat it as legacy-host escape hatch only. - Secrets stay local — the model sees host aliases only; keys and passwords never enter the conversation. Executed commands are logged to stderr for audit.
Design
- One short file (bladerunner_mcp/server.py) built on
MCPServerfrom the officialmcpPython SDK. - Command execution via paramiko; background processes follow the
nohup + PID + exit-code-file pattern proven in
blade_runner's
SSHBackend(the AI session itself plays the role of the polling monitor, so the full FSM is not embedded). - File transfer via rsync_ssh_client
(
put/getover rsync+SSH, sshpass fallback for password auth).
Development
pip install -e ".[dev]"
pytest # unit tests (mocked paramiko/rsync)
RUN_E2E=1 pytest tests/e2e # e2e against a docker compose sshd container
ruff check . && mypy bladerunner_mcp
PoC tasks
- 1. Scaffold Python package (
pyproject.toml,bladerunner_mcp/server.py) withmcpandparamikodependencies and abladerunner-mcpconsole entry point - 2. Write unit tests for YAML host config loading (aliases, key/password auth, defaults, missing-file and unknown-alias errors)
- 3. Implement config loading: read hosts from
BLADERUNNER_MCP_CONFIGor~/.bladerunner_mcp.yamlinto typed dataclasses - 4. Write unit tests for
run_command(mocked paramiko: stdout/stderr capture, exit code, connection error surfaced as tool error) - 5. Implement MCP server with
list_hostsandrun_commandtools using paramiko with per-call connect/close and timeout - 6. Write unit tests for background process tools (start/check/kill, mocked paramiko)
- 7. Implement
start_process/check_process/kill_processreusing blade_runner's nohup + PID + exit-code-file pattern - 8. Write unit tests for
put_file/get_file(mockedRsyncSSHClient: config mapping, direction, error propagation) - 9. Implement
put_file/get_filetools on top ofrsync_ssh_client - 10. Add example config
bladerunner_mcp.example.yamlto the repo - 11. Add e2e tests against a docker compose sshd container (real exec, process lifecycle, rsync round-trip)
- 12. Document installation and Claude Desktop / Claude Code MCP registration in README
- 13. Set up CI (ruff, mypy, unit tests, e2e) via GitHub Actions
- 14. Cap
run_commandoutput per stream and addread_outputpagination over host-side work_dir files - 15. Add execution timeout (deadline poll on exit status) so hung commands fail instead of blocking forever
- 16. Decode remote output with
errors="replace"and validatework_dirtool arguments - 17. Add
strict_host_keyper-host option (system known_hosts for both paramiko and rsync) - 18. Add dangerous-command safety filter with per-host
allow_dangerousopt-out - 19. Add audit logging of executed commands to stderr and YOLO-mode warning (README + server instructions)
- 20. Retry transient SSH connection errors (3 attempts with backoff; auth/host-key errors fail fast)
- 21. Fix PID-reuse race: liveness matched on the process command line (
ps -p -o args=with/procfallback), not barekill -0 - 22. Cap
check_processlog tails by bytes and polish PyPI metadata (classifiers, keywords, project URLs) - 23. Publish
bladerunner-mcpto PyPI (trusted-publishing workflow is in place; needs a PyPI project +pypienvironment configured on GitHub)
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file bladerunner_mcp-0.4.0.tar.gz.
File metadata
- Download URL: bladerunner_mcp-0.4.0.tar.gz
- Upload date:
- Size: 21.8 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: uv/0.12.5 {"installer":{"name":"uv","version":"0.12.5","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"macOS","version":null,"id":null,"libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
0c3e1f83fc5660e994bf323b2c49b3084487b978993c0b5a676ce556d7e63fcd
|
|
| MD5 |
3c60ff5cb5c5a5aa7d5bae672cab7623
|
|
| BLAKE2b-256 |
fc71193debb832387a2a87f810b0bccf5f85777cb9136c6a7984647d060aecf8
|
File details
Details for the file bladerunner_mcp-0.4.0-py3-none-any.whl.
File metadata
- Download URL: bladerunner_mcp-0.4.0-py3-none-any.whl
- Upload date:
- Size: 11.9 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: uv/0.12.5 {"installer":{"name":"uv","version":"0.12.5","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"macOS","version":null,"id":null,"libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
d62475206645518a7113a0f8602e03c42f34ad148b8aaf913d3f30af6c764457
|
|
| MD5 |
5127a9a0ef9a8655a8955e7db8a8bf73
|
|
| BLAKE2b-256 |
a7b64be4e7525b614165c42dff10b15183ae247cdb49644818872f39cc55e83c
|