SSH Liaison
FreeNot checkedProvides secure remote server access and command execution through SSH connections with persistent shell sessions.
About
Provides secure remote server access and command execution through SSH connections with persistent shell sessions.
README
Stateful SSH connection and command execution via Model Context Protocol (MCP)
✨ Features
| Feature | Description |
|---|---|
| 🔄 Stateful Sessions | Core feature: Shell state (current directory, environment variables, working directory) is preserved between MCP tool calls. Each command runs in the same persistent shell session, allowing multi-step workflows. |
| 🔌 MCP Server Mode | Integrate with Cursor/Claude Desktop for AI-assisted SSH operations with stateful command execution |
| ⚙️ SSH Config Support | Uses ~/.ssh/config for host aliases and connection parameters |
| 🔐 Direct Connection | Connect directly using user/hostname/password/port without SSH config requirement |
| 💻 Standalone CLI Mode | Interactive terminal for debugging and testing (see below) |
📦 Installation
Pre-built Binaries
Download the latest release for your platform from the Releases page.
Available platforms:
- macOS (Intel x86_64, Apple Silicon aarch64)
- Linux (x86_64, aarch64)
- Windows (x86_64)
# macOS (Apple Silicon)
curl -LO https://github.com/Citizen4our/ssh-liaison-mcp/releases/latest/download/ssh-liaison-mcp-aarch64-apple-darwin.tar.gz
tar -xzf ssh-liaison-mcp-aarch64-apple-darwin.tar.gz
# macOS (Intel)
curl -LO https://github.com/Citizen4our/ssh-liaison-mcp/releases/latest/download/ssh-liaison-mcp-x86_64-apple-darwin.tar.gz
tar -xzf ssh-liaison-mcp-x86_64-apple-darwin.tar.gz
# Linux (x86_64)
curl -LO https://github.com/Citizen4our/ssh-liaison-mcp/releases/latest/download/ssh-liaison-mcp-x86_64-unknown-linux-gnu.tar.gz
tar -xzf ssh-liaison-mcp-x86_64-unknown-linux-gnu.tar.gz
Build from Source
git clone https://github.com/Citizen4our/ssh-liaison-mcp.git
cd ssh-liaison-mcp
cargo build --release
The binary will be located at target/release/ssh-liaison-mcp.
🚀 Usage
MCP Server Mode (Primary Use Case)
The main feature of this server is stateful SSH sessions - each SSH connection maintains a persistent shell session where state (current directory, environment variables, etc.) is preserved between MCP tool calls. This enables natural multi-step workflows where commands build upon each other.
How Stateful Sessions Work
When you connect to a host via ssh_connect, a persistent shell session is established. All subsequent ssh_run_command calls for that host execute in the same shell session, meaning:
- Current directory is preserved: If you
cd /var/login one command, the next command starts from/var/log - Environment variables persist: Variables set with
exportremain available in subsequent commands - Shell state is maintained: History, aliases, and other shell state persist between calls
- Efficient: No need to reconnect or re-establish context for each command
Example workflow:
1. ssh_connect("production") → Establishes persistent shell
2. ssh_run_command("production", "cd /var/log") → Changes directory
3. ssh_run_command("production", "pwd") → Returns "/var/log" (state preserved!)
4. ssh_run_command("production", "ls -la") → Lists files in /var/log
For Cursor IDE
Build the binary:
cargo build --releaseAdd to Cursor settings (
~/.cursor/mcp.jsonor Cursor settings UI):{ "mcpServers": { "ssh-liaison": { "command": "/absolute/path/to/ssh-liaison-mcp", "args": ["serve"] } } }Restart Cursor
For Claude Desktop
Build the binary:
cargo build --releaseAdd to Claude Desktop config (
~/Library/Application Support/Claude/claude_desktop_config.jsonon macOS):{ "mcpServers": { "ssh-liaison": { "command": "/absolute/path/to/ssh-liaison-mcp", "args": ["serve"] } } }Restart Claude Desktop
Legacy Direct Connect Mode
For backward compatibility:
cargo run -- connect <user> <host> [--port <port>]
🔧 SSH Configuration
The server reads from ~/.ssh/config for host aliases. This is the recommended way to connect as it centralizes connection settings.
Example SSH Config
# Simple host alias
Host rpi
HostName 192.168.1.100
User pi
Port 22
IdentityFile ~/.ssh/id_ed25519
# Production server with custom port
Host production
HostName prod.example.com
User deploy
Port 2222
IdentityFile ~/.ssh/deploy_key
# Development server
Host dev
HostName dev.example.com
User developer
IdentityFile ~/.ssh/id_rsa
🛠️ MCP Tools
When running as MCP server, the following tools are available:
| Tool | Description | Parameters |
|---|---|---|
| ssh_connect | Connect to remote SSH server and establish a persistent shell session. The session maintains state between subsequent command calls. | host_alias (string) - Host alias defined in SSH config |
| ssh_connect_direct | Connect to remote SSH server directly using user, hostname/IP, optional password, and optional port. Establishes a persistent shell session that maintains state between subsequent command calls. Authentication tries SSH keys first, then password if provided. | host_alias (string) - Host alias to identify this connection, user (string) - SSH username, hostname (string) - Hostname or IP address, password (string, optional) - SSH password (if SSH keys fail or not available), port (integer, optional) - SSH port (default: 22) |
| ssh_run_command | Execute command in a persistent shell. Auto-connects via SSH config or a registered direct profile. Idle/max timeouts configurable. | host, command, optional sudo_password, timeout_secs, max_timeout_secs |
| ssh_run_detached | Run a long command in the background (tmux or native). Returns job_id. |
host, command |
| ssh_job_status | Non-blocking status and new output of a detached job. | host, job_id |
| ssh_job_wait | Block until job finishes or timeout. | host, job_id, optional timeout_secs |
| ssh_job_signal | Send int, term, or kill to a detached job worker. |
host, job_id, signal |
| ssh_read_log | Read last N lines from a log file. Auto-connects when needed. | host, file_path, lines |
Important Notes:
- Stateful (same MCP process): Commands for the same host share one shell session;
cdand env persist until the MCP server process restarts. - Auto-connect:
ssh_run_command, detached tools, andssh_read_logconnect automatically via~/.ssh/configor a profile saved byssh_connect_directin the same process. - Multi-process MCP clients: Each tool call may run in a fresh process — shell state does not carry over, but auto-connect still works for SSH-config and direct profiles registered in that process.
- Long commands: Prefer
ssh_run_detachedwhen runtime may exceed command timeouts. - Connection Methods:
- Use
ssh_connectfor SSH config-based connections (requires~/.ssh/config) - Use
ssh_connect_directfor direct connections without SSH config requirement
- Use
- Authentication Order (for
ssh_connect_direct):- SSH keys (SSH agent, then common key files)
- Password (if provided and SSH keys fail)
- Session Persistence: Once connected, the session remains active until the MCP server is restarted or explicitly disconnected
- Security: Passwords in
ssh_connect_directare transmitted securely over SSH protocol (encrypted). SSH key authentication is preferred when available.
Example Prompts for MCP Mode
Once the MCP server is configured, you can ask the AI assistant to perform SSH operations:
Example 1:
User: Connect to rpi and show me the current directory and disk usage
AI Response:
I'll connect to rpi and check the current directory and disk usage for you.
[Connecting to rpi...]
Current directory: /home/pi
Disk usage:
Filesystem Size Used Avail Use% Mounted on
/dev/root 15G 5.2G 8.8G 38% /
/dev/mmcblk0p1 253M 49M 204M 20% /boot
Example 2:
User: Connect to production and read the last 50 lines from /var/log/nginx/error.log
AI Response:
I'll connect to production and read the last 50 lines from the nginx error log.
[Connecting to production...]
Last 50 lines from /var/log/nginx/error.log:
2024-01-15 10:23:45 [error] 12345#0: *123 connect() failed (111: Connection refused)
2024-01-15 10:24:12 [error] 12345#0: *124 upstream timed out
...
Example 3:
User: Connect directly to 192.168.88.63 as user pi and show disk usage
AI Response:
I'll connect directly to 192.168.88.63 as user pi and check the disk usage.
[Connecting to 192.168.88.63 using SSH keys...]
Disk usage:
Filesystem Size Used Avail Use% Mounted on
/dev/root 15G 5.2G 8.8G 38% /
/dev/mmcblk0p1 253M 49M 204M 20% /boot
Example 3b (with password):
User: Connect to 192.168.88.63 as user admin with password mypass123 and check uptime
AI Response:
I'll connect to 192.168.88.63 as user admin and check the system uptime.
[Connecting to 192.168.88.63...]
[SSH key authentication failed, trying password...]
System uptime: 5 days, 12 hours, 30 minutes
Example 4:
User: Check system uptime and memory usage on the dev server
AI Response:
I'll check the system uptime and memory usage on the dev server.
[Connecting to dev...]
System uptime: 15 days, 3 hours, 22 minutes
Memory usage:
total used free shared buff/cache available
Mem: 7.8G 2.1G 3.2G 128M 2.5G 5.4G
Swap: 2.0G 0B 2.0G
Example 5:
User: Show me running processes and check if nginx service is running on production
AI Response:
I'll check the running processes and verify if nginx is running on production.
[Connecting to production...]
Nginx service status: active (running)
Top processes by CPU:
PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND
1234 nginx 20 0 123456 4567 1234 S 2.3 0.1 0:05:23 nginx
5678 app 20 0 234567 8901 2345 S 1.8 0.2 0:12:45 app
...
Stateful Session Example
This example demonstrates how state is preserved between commands:
User: Connect to rpi, navigate to /var/log, and show me the last 10 lines of syslog
AI Response:
I'll connect to rpi, navigate to /var/log, and show you the last 10 lines of syslog.
[Connecting to rpi...]
[Changing directory to /var/log...]
Last 10 lines of syslog:
Jan 15 10:23:45 rpi systemd[1]: Started Network Manager.
Jan 15 10:24:12 rpi sshd[1234]: Accepted publickey for pi from 192.168.1.50
...
What happened behind the scenes:
ssh_connect("rpi")- Established persistent shell sessionssh_run_command("rpi", "cd /var/log")- Changed directory (state saved)ssh_run_command("rpi", "tail -n 10 syslog")- Executed from/var/log(state preserved!)
The second command automatically started from /var/log because the shell state was preserved from the previous command.
Standalone CLI Mode (For Debugging)
Interactive terminal mode for testing and debugging. Useful for troubleshooting connection issues or testing commands manually.
Connection Methods
1. Using SSH Config Alias
# Connect immediately using alias from ~/.ssh/config
cargo run -- cli --host rpi
# or
./target/release/ssh-liaison-mcp cli --host rpi
2. Direct Connection via Command Line
# Direct connection with SSH keys (default port 22)
cargo run -- cli --user pi --hostname 192.168.1.100
# Direct connection with custom port
cargo run -- cli --user pi --hostname 192.168.1.100 --port 2222
# Direct connection with password authentication
cargo run -- cli --user pi --hostname 192.168.1.100 --password mypassword
# Direct connection with password and custom port
cargo run -- cli --user pi --hostname 192.168.1.100 --password mypassword --port 2222
Connection Examples
# Example 1: Connect via SSH config
cargo run -- cli --host production
# Example 2: Direct connection to Raspberry Pi
cargo run -- cli --user pi --hostname 192.168.1.100
# Example 3: Direct connection with password to custom port
cargo run -- cli --user admin --hostname server.example.com --password secret --port 2222
# Example 4: Interactive mode - connect later
cargo run -- cli
ssh> connect dev-server
[dev-server]> uname -a
[dev-server]> exit
🔐 Authentication
For ssh_connect (SSH Config)
The server attempts authentication in the following order:
- SSH agent (if available)
- Identity file from SSH config
- Common SSH keys (in order):
~/.ssh/id_ed25519~/.ssh/id_rsa~/.ssh/id_ecdsa~/.ssh/id_dsa
For ssh_connect_direct (Direct Connection)
- SSH keys (same order as above)
- Password (if provided and SSH keys fail or are not available)
⚠️ Security Notes
- Read-only operations recommended: The tools include warnings about destructive operations
- Password handling: Sudo password elicitation support is planned but not yet fully implemented
- No password logging: Passwords are never logged or exposed
🧪 Development
# Run in development mode
cargo run -- cli --host <your-host>
# Build release
cargo build --release
# Run tests
cargo test
# Run lints
cargo clippy --all-targets -- -D warnings
# Format code
cargo fmt
📊 Logging
The server uses structured logging via the tracing crate. Control log verbosity with:
SSH agent (Cursor / GUI)
Cursor often spawns MCP without SSH_AUTH_SOCK. Passphrase-protected keys then fail file auth (Wrong passphrase…), while terminal ssh still works via the agent.
ssh-liaison-mcp ≥ 0.2.2 discovers the agent socket via launchctl getenv SSH_AUTH_SOCK (macOS) and /tmp/ssh-*/agent.* before userauth_agent. After upgrading, restart the MCP server in Cursor so it loads the new binary.
Command-line flags
# Default (warnings only)
ssh-liaison-mcp serve
# Info level (-v)
ssh-liaison-mcp -v serve
# Debug level (-vv)
ssh-liaison-mcp -vv serve
# Trace level (-vvv)
ssh-liaison-mcp -vvv serve
Environment variable
# Set log level via RUST_LOG
RUST_LOG=debug ssh-liaison-mcp serve
# Target specific modules
RUST_LOG=ssh_liaison_mcp=debug ssh-liaison-mcp serve
# Multiple targets
RUST_LOG=ssh_liaison_mcp::ssh=trace,ssh_liaison_mcp::mcp=debug ssh-liaison-mcp serve
📋 TODO / Future Improvements
Infrastructure & Distribution
CI/CD Pipeline (GitHub Actions)
- Automated tests on push/PR
- Linting and formatting checks (clippy, rustfmt)
- Build for multiple platforms (Linux, macOS, Windows)
- Automated release workflow
Release Automation
- GitHub Actions workflow for creating releases
- Automatic binary builds for major platforms
- GitHub Releases with pre-built binaries
- Version bumping automation
Crates.io Publication
- Prepare crate metadata (description, keywords, categories)
- Add crate documentation
- Publish to crates.io
Features
Sudo Password Elicitation
- MCP prompt when password not provided (interactive elicitation)
- Password caching for session duration
Session Management
-
ssh_disconnecttool to explicitly close sessions -
ssh_list_sessionstool to show active connections - Automatic session cleanup on timeout
- Session health checks and reconnection
-
Enhanced Error Handling
- Better error messages with context
- Connection retry logic
- Graceful handling of network interruptions
- Session recovery mechanisms
File Operations
-
ssh_read_filetool for reading remote files -
ssh_write_filetool (with safety checks) -
ssh_list_directorytool for directory listings - Support for binary file transfers
-
Monitoring & Observability
- Connection status monitoring
- Optional verbose logging mode
Code Quality
Testing
- Unit tests for SSH config parsing
- Integration tests for MCP tools
- Mock SSH server for testing
- CLI mode tests
Documentation
- API documentation (rustdoc)
- Architecture documentation
- Contributing guidelines
- Security best practices guide
Code Improvements
- Refactor error handling patterns
- Add comprehensive logging (tracing)
- Performance optimizations
- Code coverage improvements
Platform Support
Cross-platform Binary Releases
- Linux (x86_64, ARM64)
- macOS (Intel, Apple Silicon)
- Windows (x86_64)
Package Managers
- Homebrew formula for macOS
- AUR package for Arch Linux
Security Enhancements
Security Audit
- Dependency security scanning
- Code security review
- Penetration testing considerations
Access Control
- Optional host allowlist/denylist
- Command whitelisting/blacklisting
- Rate limiting for connections
📄 License
This project is licensed under the MIT License - see the LICENSE file for details.
Installing SSH Liaison
This server has no published package — it is built from source. Open the repository and follow its README.
▸ github.com/citizen4our/ssh-liaison-mcpFAQ
Is SSH Liaison MCP free?
Yes, SSH Liaison MCP is free — one-click install via Unyly at no cost.
Does SSH Liaison need an API key?
No, SSH Liaison runs without API keys or environment variables.
Is SSH Liaison hosted or self-hosted?
Self-hosted: the server runs locally on your machine via the install command above.
How do I install SSH Liaison in Claude Desktop, Claude Code or Cursor?
Open SSH Liaison 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
GitHub
PRs, issues, code search, CI status
by GitHubFilesystem
Secure file operations with configurable access controls.
Memory
Knowledge graph-based persistent memory system.
Template MCP Server
A CLI tool to create a new Model Context Protocol server project with TypeScript support, dual transport options, and an extensible structure
by mcpdotdirectCompare SSH Liaison with
Not sure what to pick?
Find your stack in 60 seconds
Author?
Embed badge for your README
Browse similar
All development MCPs
