SSH terminals for AI agents: stateful shells, exit codes the moment a command ends, router CLIs.
Use your MCP client to work with several SSH hosts through one local connection. Run diagnostics on a VPS, inspect a NAS or query a Keenetic router by name. Sessions preserve terminal state between calls; long output can be read a page at a time.
For people who already use SSH and want an assistant to help with routine diagnostics and administration. It is not an SSH daemon, a hosted proxy or a replacement for access controls on your servers.
You need Python 3.11+, an SSH account on a host you control and an MCP client that can launch a local stdio server.
Install it:
python -m pip install mcp-ssh-gateway
Or run it without installing anything: uvx --from mcp-ssh-gateway mcp-ssh-gateway --servers-config servers.json
To work on the project itself, clone it instead: git clone https://github.com/d00mus/MCP-SSH.git && cd MCP-SSH && python -m pip install -r requirements.txt
Create a servers.json somewhere you will remember (replace the address, user and key path with your own):
{
"servers": {
"lab": {
"host": "192.168.1.10",
"user": "your-ssh-user",
"key_path": "~/.ssh/id_ed25519"
}
}
}
Host-key verification is enabled by default and uses the machine’s system host-key store. Ensure the host key is already trusted there, and verify its fingerprint independently before adding it. For password authentication, use "password": "${LAB_SSH_PASSWORD}" and provide LAB_SSH_PASSWORD to the MCP server process. Do not commit real credentials or your servers.json. See the security policy.
Add this to a client that uses the mcpServers config format. Replace the absolute path: clients do not necessarily start in your working directory.
{
"mcpServers": {
"ssh-gateway": {
"command": "mcp-ssh-gateway",
"args": ["--servers-config", "/absolute/path/to/servers.json"]
}
}
}
On Windows, point command at mcp-ssh-gateway.exe in your Python Scripts directory if the client does not resolve it from PATH, and use escaped backslashes in JSON paths (for example C:\\Users\\you\\servers.json).
Running the command directly is not an interactive SSH terminal: it communicates with the client over stdio. Restart the MCP client after updating its config.
In the client, ask: “List my SSH hosts, then run uname -a on lab.” If the host is missing, check the config path and the client's MCP server logs. If SSH fails, check credentials and host-key verification.
Add more hosts under servers in the same file. servers.json.example shows a multi-host configuration; check its host-key and credential choices before copying it.
A Linux host and a router can share one MCP connection. Your client makes calls like these (they are not terminal commands):
server_list() # find configured hosts
run(server="lab", command="df -h") # inspect disk space
run(server="keenetic", command="show interface", shell=false) # router CLI
run returns a session_id; pass it to later calls if you need the same terminal state. Without it an idle session may be reused with unknown state; new_session: true forces a clean session. A command still running after the initial wait (5 seconds by default) reports still_running: true. Use read(session_id="...") for later output, or whenever has_more indicates unread lines. signal(action="ctrl_c") interrupts a stuck command. Non-zero exits report completed_nonzero and exit_status, not silent success.
The file tool can inspect and edit remote files through SFTP (with shell fallback). Review edits and give an assistant only the SSH permissions it needs.
shell: false sends device CLI commands without a POSIX shell; common pagers such as --More-- are handled. Keenetic NDM is a supported use case, but other vendor CLIs are not guaranteed. Keep NDM CLI and Linux shell operations in separate sessions.Security boundary: read_only and command blacklists are best-effort guardrails against mistakes, not a sandbox. Shell expansion and interpreters can bypass checks on command text. Use restricted SSH users and server-side permissions for sensitive hosts. Host-key verification is on by default; avoid turning it off casually.
The SSH connection originates from the machine running the gateway. This project works with MCP clients that can start a stdio server; it does not add SSH access to a chat app without MCP integration.
Docker (build from this clone):
docker build -t mcp-ssh-server .
docker run -i --rm \
-v /absolute/path/to/servers.json:/app/servers.json:ro \
-v /absolute/path/to/your/.ssh:/root/.ssh:ro \
mcp-ssh-server --servers-config /app/servers.json
Use absolute mount paths and pass required environment variables with -e NAME. This example exposes SSH keys to the container; mount only what it needs. For an MCP client using Docker, set command to docker and put the same run arguments in args.
PyPI / MCP Registry: The package is published as mcp-ssh-gateway and listed in the MCP Registry as io.github.d00mus/mcp-ssh-gateway, so pip install mcp-ssh-gateway and uvx --from mcp-ssh-gateway ... work. See the release process.
host, user, optional port (default 22) and a key_path or password. verify_host defaults to true. password and key_passphrase support environment references (${NAME}); missing references fail at startup.server_list, server_add, run, read, signal, file, session_list, session_update, session_close and last_command_details. server_add accepts an alias and only appends new targets. --tool-profile lean exposes six everyday tools for a smaller catalog.servers.json are checked periodically (every 30 seconds); server_list(reload=true) checks immediately. Unchanged hosts keep their sessions; removing a host or changing its address, login or host-key settings closes that host’s active sessions.--log-output meta (the default) records lifecycle information and command text. full also records raw output; off disables logging. Consider what secrets might appear in commands and output.For contributions or vulnerabilities, see CONTRIBUTING.md and SECURITY.md.
python -m unittest discover -s tests -t .
Source-derived launch command. Check the maintainer’s required arguments and credentials before running:
uvx mcp-ssh-gatewayMerge this template into ~/Library/Application Support/Claude/claude_desktop_config.json. Keep existing servers. Add any arguments, credentials, and permissions required by the maintainer; this template has not been install-tested.
{
"mcpServers": {
"io-github-d00mus-mcp-ssh-gateway": {
"command": "uvx",
"args": [
"mcp-ssh-gateway"
]
}
}
}Restart Claude Desktop completely for changes to take effect. Confirm the server appears connected in the client’s tool list, then try a read-only example from its documentation.
Claude Desktop setup referencemcp-ssh-gatewaypypiMCP SSH Gateway works with any MCP-compatible client. Copy the config snippet from the Configuration section above and add it to the file shown for your client, then restart the application.
~/Library/Application Support/Claude/claude_desktop_config.jsonRestart Claude Desktop completely for changes to take effect.~/.cursor/mcp.jsonRestart Cursor for changes to take effect..vscode/mcp.jsonReload VS Code window for changes to take effect.~/.codeium/windsurf/mcp_config.jsonRestart Windsurf for changes to take effect..mcp.jsonSave at the project root, then start Claude Code in that project and review the MCP server approval prompt. Keep real credentials out of shared files.