Ssh For Agents
FreeNot checkedEnables AI agents to execute SSH commands, read files, and list directories on remote hosts with a configurable command-safety policy.
About
Enables AI agents to execute SSH commands, read files, and list directories on remote hosts with a configurable command-safety policy.
README
An MCP (Model Context Protocol) server that lets AI agents talk to SSH servers — run commands, read files, and list directories on remote hosts — behind a configurable command-safety policy.
Because it speaks MCP, any MCP-capable agent (Claude Desktop, Claude Code, or your own client) can use it with no glue code. Connections are async (built on asyncssh) and pooled per host.
AI agent ──MCP(stdio)──▶ ssh-for-agents ──asyncssh──▶ remote host(s)
│
guardrails policy
(readonly / guarded / unrestricted)
Quickstart
git clone https://github.com/WilliamSmithEdward/pySSHForAgents.git
cd pySSHForAgents
python -m venv .venv
.venv\Scripts\Activate.ps1 # macOS/Linux: source .venv/bin/activate
pip install -e .
Store your SSH key's passphrase in an environment variable (it's never written to a
config file) — Windows setx NAME "value" then open a new terminal, macOS/Linux
export NAME=value — then register a host and test it end to end:
ssh-for-agents-config add myserver --hostname 203.0.113.10 --user deploy \
--passphrase-env MYSERVER_SSH_PASSPHRASE
ssh-for-agents-run myserver "uptime"
That's it. Now point your agent at it — see Connect to Claude below, or Codex / a local Ollama model. The full walkthrough (keys, host-key verification, every agent) is in SETUP.md.
Tools exposed to the agent
| Tool | What it does |
|---|---|
list_hosts |
List configured hosts and the active safety policy. |
check_command |
Dry-run the policy for a command (allow / needs_confirmation / block). |
run_command |
Run a shell command on a host. Destructive commands need confirm=true. |
read_file |
Read a remote file over SFTP (size-capped). |
list_dir |
List a remote directory over SFTP. |
Requirements
- Python 3.10+
- An SSH key (password auth is not wired up by design — use keys).
Install
python -m venv .venv
.venv\Scripts\activate # Windows
# source .venv/bin/activate # macOS/Linux
pip install -e .
asyncssh needs bcrypt to decrypt passphrase-protected OpenSSH keys; it's a
declared dependency, so the install above pulls it in.
Configure
Copy the example and edit it:
cp hosts.example.json hosts.json
{
"hosts": {
"myserver": {
"hostname": "203.0.113.10",
"username": "deploy",
"port": 22,
"private_key": "~/.ssh/id_ed25519",
"passphrase_env": "MYSERVER_SSH_PASSPHRASE",
"known_hosts": "~/.ssh/known_hosts",
"verify_host_key": true
}
},
"policy": {
"mode": "guarded",
"extra_denylist": [],
"extra_readonly_allow": [],
"max_output_bytes": 100000,
"default_timeout": 60
}
}
Host fields:
private_key— path to the private key (~is expanded).passphrase_env— name of an environment variable holding the key's passphrase. The passphrase itself is never stored in the config. Omit if the key has no passphrase.known_hosts— path to a known_hosts file used to verify the server's host key. Omit to use the default (~/.ssh/known_hosts+ system files).verify_host_key— setfalseto skip host-key verification (insecure; only for throwaway hosts).
The server looks for hosts.json in the working directory, or wherever
SSH_AGENT_CONFIG points.
hosts.jsonis git-ignored since it describes your infrastructure. Commithosts.example.jsoninstead.
Adding & managing hosts
The config holds any number of hosts — add as many as you like under hosts.
Manage them with the bundled CLI instead of hand-editing JSON:
# add or update a host
ssh-for-agents-config add prod \
--hostname 203.0.113.10 --user deploy \
--key ~/.ssh/id_ed25519 --passphrase-env PROD_SSH_PASSPHRASE
ssh-for-agents-config list # show configured hosts + policy
ssh-for-agents-config remove prod # drop a host
ssh-for-agents-config import-ssh-config # pull hosts from ~/.ssh/config
--config <path> (or $SSH_AGENT_CONFIG) targets a specific hosts.json.
Hot-reload: a running server watches hosts.json and reloads it when it
changes, so a newly added host is usable on the next tool call — no restart needed.
Live connections to unchanged hosts are kept; a half-written or invalid edit is
ignored (the last good config stays active).
Shell CLI — for agents that can't call MCP tools
Some agents run shell commands but can't reliably call MCP tools — notably a local
(Ollama) model in Codex, which emits tool-call names the host rejects. For those,
the same hosts and safety policy are exposed as a plain command, ssh-for-agents-run:
ssh-for-agents-run hosts # list aliases + policy
ssh-for-agents-run linode "df -h" # run a command (default verb)
ssh-for-agents-run linode "rm /tmp/x" --confirm
ssh-for-agents-run check linode "rm -rf /" # dry-run the policy
ssh-for-agents-run read linode /etc/os-release
ssh-for-agents-run ls linode /var/log
It reuses hosts.json and the guardrails, and on Windows resolves the key passphrase
from the persisted user environment even when the launching shell filters env vars.
See SETUP.md for wiring it into Codex (AGENTS.md + giving the sandbox read
access to ~/.ssh).
Run
ssh-for-agents # console script (serves over stdio)
python -m ssh_agent_mcp # equivalent
The server communicates over stdio — it's normally launched by an MCP client rather than run by hand.
Connect to Claude Code
claude mcp add ssh-for-agents -- /abs/path/to/.venv/Scripts/python.exe -m ssh_agent_mcp
Set the working directory (or SSH_AGENT_CONFIG) so it finds hosts.json.
Connect to Claude Desktop
Add to claude_desktop_config.json:
{
"mcpServers": {
"ssh-for-agents": {
"command": "C:\\path\\to\\.venv\\Scripts\\python.exe",
"args": ["-m", "ssh_agent_mcp"],
"env": {
"SSH_AGENT_CONFIG": "C:\\path\\to\\hosts.json",
"MYSERVER_SSH_PASSPHRASE": "..."
}
}
}
}
Use it from a local Ollama model
Ollama runs models and does function calling, but it is not an MCP client — so a small host/bridge sits between them: it launches this server, hands the model the tools, runs the tool-calling loop, and routes tool calls back. A working one is included at examples/ollama_bridge.py:
pip install -e ".[examples]" # adds the `ollama` client library
ollama pull qwen3-coder:30b # any tool-calling model works
python examples/ollama_bridge.py "what is the uptime and disk usage on linode?"
The model discovers hosts via list_hosts and runs commands via run_command. When
the policy returns needs_confirmation, the bridge prompts you on the terminal
rather than letting the model authorize its own destructive command. Pick a model
with OLLAMA_MODEL=...; it must support tool calling (qwen3-coder, llama3.1/3.2,
mistral-nemo, …).
Ollama model ◀──function calling──▶ bridge ◀──MCP(stdio)──▶ ssh-for-agents ──▶ host
Safety model
The policy runs before any command reaches the server. Pick a mode:
| Mode | Behavior |
|---|---|
readonly |
Only commands on a read-only allowlist run; everything else is blocked. |
guarded |
(default) Most commands run freely. Destructive/state-changing commands return needs_confirmation until the agent re-calls with confirm=true. A few catastrophic patterns are hard-blocked. |
unrestricted |
Anything runs. Only sensible for disposable/sandboxed hosts. |
In guarded mode:
- Needs confirmation:
rm,dd,mkfs,shutdown/reboot,kill,mount, firewall edits,crontab, package installs/removals (apt install, …), service changes (systemctl stop, …),git push/reset,curl … | sh, recursivechmod/chown, redirects into system paths, and more. - Hard-blocked (cannot be confirmed away): fork bombs, recursive deletes of root
paths (
rm -rf /,/home, …), and writing/formatting raw disk devices (dd of=/dev/sda,mkfs … /dev/nvme0n1). - Commands are parsed into segments across
;,&&,||, and pipes, andsudo/envwrappers are seen through, socd /x && sudo rm yis still flagged.
Extend it without editing code via extra_denylist (more binaries that need
confirmation) and extra_readonly_allow (more binaries allowed in readonly mode).
Important — this is a safety net, not a sandbox. String-based inspection of shell commands can always be evaded by a determined caller (
base64 … | sh, exotic quoting, writing then running a script, etc.). It exists to prevent accidents and obvious mistakes. For a real trust boundary, connect as a dedicated unprivileged user and constrain it with OS permissions and/or sshdForceCommand. Treat the mode as defense-in-depth, not as containment of an adversarial agent.
Develop & test
pip install -e ".[dev]"
pytest # guardrail policy tests (no SSH needed)
Project layout
ssh_agent_mcp/
guardrails.py # command-safety policy engine (pure, well-tested)
config.py # hosts.json loading + validation
ssh_client.py # asyncssh connection pool (run / read_file / list_dir)
server.py # FastMCP server + tools (with hosts.json hot-reload)
manage.py # `ssh-for-agents-config` CLI: add / list / remove / import hosts
run_cli.py # `ssh-for-agents-run` CLI: run SSH from the shell (Codex/local models)
tests/
test_guardrails.py
test_manage.py
test_run_cli.py
examples/
ollama_bridge.py # drive a local Ollama model with these tools
hosts.example.json
SETUP.md # step-by-step reproduction guide
Install Ssh For Agents in Claude Desktop, Claude Code & Cursor
unyly install ssh-for-agentsInstalls into Claude Desktop, Claude Code, Cursor & VS Code — handles npx, uvx and build-from-source repos for you.
First time? Get the CLI: curl -fsSL https://unyly.org/install | sh
Or configure manually
Run in your terminal:
claude mcp add ssh-for-agents -- uvx --from git+https://github.com/WilliamSmithEdward/pySSHForAgents ssh-for-agentsFAQ
Is Ssh For Agents MCP free?
Yes, Ssh For Agents MCP is free — one-click install via Unyly at no cost.
Does Ssh For Agents need an API key?
No, Ssh For Agents runs without API keys or environment variables.
Is Ssh For Agents hosted or self-hosted?
Self-hosted: the server runs locally on your machine via the install command above.
How do I install Ssh For Agents in Claude Desktop, Claude Code or Cursor?
Open Ssh For Agents on unyly.org, pick your client tab (Claude Desktop, Claude Code, Cursor) and press Install — the config is generated automatically, no JSON editing.
Related MCPs
Fetch
Web content fetching and conversion for efficient LLM usage.
AWS KB Retrieval
Retrieval from AWS Knowledge Base using Bedrock Agent Runtime.
by modelcontextprotocolSpring AI MCP Server
Provides auto-configuration for setting up an MCP server in Spring Boot applications.
llm-analysis-assistant
A very streamlined mcp client that supports calling and monitoring stdio/sse/streamableHttp, and can also view request responses through the /logs page. It also
by xuzexin-hzCompare Ssh For Agents with
Not sure what to pick?
Find your stack in 60 seconds
Author?
Embed badge for your README
Browse similar
All ai MCPs
