Install
openclaw skills install ssh-connection-execution-management-skillManage persistent SSH connections, execute async non-blocking commands, handle concurrent bulk executions across fleets, and manage SSH keys and client assignments.
openclaw skills install ssh-connection-execution-management-skill[!IMPORTANT] Dependency Warning: This skill requires the
ssh_mcpserver to be running and registered with the Model Context Protocol (MCP) host. It enables secure, persistent, non-blocking execution across remote Linux servers.
This skill equips the agent to manage remote SSH connection lifecycles, perform system updates, troubleshoot connectivity, and execute commands in bulk across server fleets.
ssh_conn (action list) to view all saved connection configurations.ssh_conn (action test) to check remote host reachability before saving a new target connection.ssh_conn (action save) to store connections in the SQLite database to avoid hardcoding auth credentials or private keys in scripts.ssh_exec (action open) with the target connection ID. This establishes a persistent SSH connection and returns a sessionId.ssh_exec (action list).ssh_exec (action close) with the sessionId to release resources and prevent orphan connections on remote hosts.ssh_exec (action run) on an open sessionId. This returns a commandId immediately and does not block the agent's execution.ssh_exec (action status) with the commandId.ssh_exec (action logs). Since remote commands can produce long outputs, always use appropriate log filters:
grep: Filter output lines matching a regex or substring.head / tail: Limit the number of lines returned.fromLine / toLine: Fetch specific line ranges.stream: Target stdout, stderr, or both.ssh_bulk_exec or ssh_bulk_audit instead of sequentially looping.ssh_bulk_exec will automatically reuse it.bulkExecId to ssh_exec (action status or logs).connectionId, name, or host.ssh_client (action list) to inspect current client groups.ssh_client with action assign or remove. This keeps server fleets clean and traceable by client context.ssh_key (action link).ssh_key (action add) to import the private key content securely, and link it to the target connections.Add an SSH Private Key:
{
"name": "ssh_key",
"arguments": {
"op": "add",
"keyName": "prod-deploy-key",
"privateKey": "-----BEGIN OPENSSH PRIVATE KEY-----\nb3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAAAMwAAAAtzc2gtZW\n..."
}
}
Save a New Connection with a Linked Key:
{
"name": "ssh_conn",
"arguments": {
"op": "save",
"name": "production-app-server",
"host": "192.168.1.50",
"port": 22,
"username": "ubuntu",
"client": "AcmeCorp"
}
}
Tip: After saving, link the key using:
{
"name": "ssh_key",
"arguments": {
"op": "link",
"keyName": "prod-deploy-key",
"connectionId": "uuid-of-production-app-server"
}
}
Open Session:
{
"name": "ssh_exec",
"arguments": {
"op": "open",
"connectionId": "uuid-of-production-app-server"
}
}
Returns: Session ID: 9f8e7d6c-5b4a-3f2e-1d0c-9b8a7f6e5d4c
Run a Command Asynchronously:
{
"name": "ssh_exec",
"arguments": {
"op": "run",
"sessionId": "9f8e7d6c-5b4a-3f2e-1d0c-9b8a7f6e5d4c",
"command": "apt-get update && apt-get upgrade -y"
}
}
Returns: Command ID: a1b2c3d4-e5f6-7a8b-9c0d-1e2f3a4b5c6d
Check Status & Fetch Filtered Logs:
{
"name": "ssh_exec",
"arguments": {
"op": "status",
"commandId": "a1b2c3d4-e5f6-7a8b-9c0d-1e2f3a4b5c6d"
}
}
{
"name": "ssh_exec",
"arguments": {
"op": "logs",
"commandId": "a1b2c3d4-e5f6-7a8b-9c0d-1e2f3a4b5c6d",
"tail": 50,
"grep": "error"
}
}
Disconnect Session:
{
"name": "ssh_exec",
"arguments": {
"op": "close",
"sessionId": "9f8e7d6c-5b4a-3f2e-1d0c-9b8a7f6e5d4c"
}
}
Execute Command Across Multiple Targets (Async):
{
"name": "ssh_bulk_exec",
"arguments": {
"commands": ["systemctl restart nginx", "systemctl status nginx"],
"connectionIds": ["server-uuid-1", "server-uuid-2", "server-uuid-3"],
"concurrency": 5
}
}
Returns: bulkExecId: e8d7c6b5-a4f3-9e2d-8c1b-7a0f9e8d7c6b
Monitor Bulk Status:
{
"name": "ssh_exec",
"arguments": {
"op": "status",
"bulkExecId": "e8d7c6b5-a4f3-9e2d-8c1b-7a0f9e8d7c6b"
}
}
Fetch Aggregated Logs for the Bulk Run:
{
"name": "ssh_exec",
"arguments": {
"op": "logs",
"bulkExecId": "e8d7c6b5-a4f3-9e2d-8c1b-7a0f9e8d7c6b",
"tail": 20
}
}
Fetch Logs for a Specific Target in a Bulk Run:
{
"name": "ssh_exec",
"arguments": {
"op": "logs",
"bulkExecId": "e8d7c6b5-a4f3-9e2d-8c1b-7a0f9e8d7c6b",
"connectionId": "server-uuid-1",
"tail": 20
}
}
Run Bulk Security Audit (Synchronous):
{
"name": "ssh_bulk_audit",
"arguments": {
"op": "security",
"client": "AcmeCorp",
"concurrency": 3
}
}
sequenceDiagram
participant LLM as OpenClaw Agent
participant MCP as SSH MCP Server
participant Host as Target Linux Server
LLM->>MCP: ssh_exec(op="open", connectionId="web-server-uuid")
MCP->>Host: SSH Handshake & Connect
Host-->>MCP: Handshake Success
MCP-->>LLM: sessionId="9f8e7d6c..."
Note over LLM, Host: Run multiple sequential tasks on the same persistent session
LLM->>MCP: ssh_exec(op="run", sessionId="9f8e7d6c...", command=["apt-get update", "apt-get upgrade -y"])
MCP-->>LLM: commandId="cmd-12345" (returns immediately)
MCP->>Host: Execute "apt-get update"
Host-->>MCP: stdout/stderr logs stream
Note right of MCP: Stream logs to SQLite
MCP->>Host: Execute "apt-get upgrade -y"
Host-->>MCP: stdout/stderr logs stream
LLM->>MCP: ssh_exec(op="status", commandId="cmd-12345")
MCP-->>LLM: Status: running / completed / failed
LLM->>MCP: ssh_exec(op="logs", commandId="cmd-12345", tail=20)
MCP-->>LLM: [stdout] ... Completed successfully!
LLM->>MCP: ssh_exec(op="close", sessionId="9f8e7d6c...")
MCP->>Host: Close Connection
MCP-->>LLM: Success
sequenceDiagram
participant LLM as OpenClaw Agent
participant MCP as SSH MCP Server
participant Srv1 as Server 1 (No active session)
participant Srv2 as Server 2 (Active session exists)
LLM->>MCP: ssh_bulk_exec(commands=["docker ps"], connectionIds=["srv1-id", "srv2-id"])
MCP-->>LLM: bulkExecId="bulk-abc-123", status="running"
par Server 1 Execution
Note over MCP, Srv1: No active session → Create transient session
MCP->>Srv1: Open Dynamic Connection
MCP->>Srv1: Execute "docker ps"
Srv1-->>MCP: Command Completed
Note over MCP, Srv1: Auto-disconnect transient session
MCP->>Srv1: Disconnect Session
and Server 2 Execution
Note over MCP, Srv2: Active session found → Re-use it
MCP->>Srv2: Execute "docker ps" on existing channel
Srv2-->>MCP: Command Completed
Note over MCP, Srv2: Keep session open for future reuse
end
Note over LLM, MCP: Monitor bulk run progress and logs
LLM->>MCP: ssh_exec(op="status", bulkExecId="bulk-abc-123")
MCP-->>LLM: targets: [Srv1: completed, Srv2: completed]
LLM->>MCP: ssh_exec(op="logs", bulkExecId="bulk-abc-123", tail=10)
MCP-->>LLM: Aggregated logs JSON output for all servers
Connection Timeouts & Failures:
ssh_exec (action open) fails, check connection credentials, firewall rules, and key links. Use ssh_conn (action test) to run diagnostics.Channel Concurrency Limitations:
MaxSessions), concurrent executions may fail to open channels. The SSH MCP server implements a self-healing queue that detects channel failures, decreases concurrency, and retries tasks with an exponential backoff.Exit Code Evaluation:
0 indicates success. A non-zero exit code or null indicates failure or premature termination.Transient Dynamic Connection Isolation: