ssh-for-agents

ssh-for-agents

Enables AI agents to execute SSH commands, read files, and list directories on remote hosts with a configurable command-safety policy.

Category
访问服务器

README

ssh-for-agents

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 — set false to 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.json is git-ignored since it describes your infrastructure. Commit hosts.example.json instead.

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, recursive chmod/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, and sudo/env wrappers are seen through, so cd /x && sudo rm y is 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 sshd ForceCommand. 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

推荐服务器

Baidu Map

Baidu Map

百度地图核心API现已全面兼容MCP协议,是国内首家兼容MCP协议的地图服务商。

官方
精选
JavaScript
Playwright MCP Server

Playwright MCP Server

一个模型上下文协议服务器,它使大型语言模型能够通过结构化的可访问性快照与网页进行交互,而无需视觉模型或屏幕截图。

官方
精选
TypeScript
Magic Component Platform (MCP)

Magic Component Platform (MCP)

一个由人工智能驱动的工具,可以从自然语言描述生成现代化的用户界面组件,并与流行的集成开发环境(IDE)集成,从而简化用户界面开发流程。

官方
精选
本地
TypeScript
Audiense Insights MCP Server

Audiense Insights MCP Server

通过模型上下文协议启用与 Audiense Insights 账户的交互,从而促进营销洞察和受众数据的提取和分析,包括人口统计信息、行为和影响者互动。

官方
精选
本地
TypeScript
VeyraX

VeyraX

一个单一的 MCP 工具,连接你所有喜爱的工具:Gmail、日历以及其他 40 多个工具。

官方
精选
本地
graphlit-mcp-server

graphlit-mcp-server

模型上下文协议 (MCP) 服务器实现了 MCP 客户端与 Graphlit 服务之间的集成。 除了网络爬取之外,还可以将任何内容(从 Slack 到 Gmail 再到播客订阅源)导入到 Graphlit 项目中,然后从 MCP 客户端检索相关内容。

官方
精选
TypeScript
Kagi MCP Server

Kagi MCP Server

一个 MCP 服务器,集成了 Kagi 搜索功能和 Claude AI,使 Claude 能够在回答需要最新信息的问题时执行实时网络搜索。

官方
精选
Python
e2b-mcp-server

e2b-mcp-server

使用 MCP 通过 e2b 运行代码。

官方
精选
Neon MCP Server

Neon MCP Server

用于与 Neon 管理 API 和数据库交互的 MCP 服务器

官方
精选
Exa MCP Server

Exa MCP Server

模型上下文协议(MCP)服务器允许像 Claude 这样的 AI 助手使用 Exa AI 搜索 API 进行网络搜索。这种设置允许 AI 模型以安全和受控的方式获取实时的网络信息。

官方
精选