@aaarc/handfree-ssh-mcp 1.0.5 → 1.0.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -113,11 +113,12 @@ The AI can now execute commands on your servers. All within your defined securit
113
113
 
114
114
  | Tool | Description |
115
115
  |------|-------------|
116
- | `execute-command` | Run SSH command (with optional `stream` for real-time output) |
117
- | `show-whitelist` | Show active command policy + SFTP policy + output-log path for a server |
118
- | `upload` | Upload local file to a remote server (CRLF-fix for shell scripts, skip-if-identical) |
119
- | `download` | Download remote file to local disk |
120
- | `transfer` | Unified upload / download / server-to-server relay (`mode`: `upload` / `download` / `relay`, optional `recursive`, optional `skipIfIdentical`) |
116
+ | `execute-command` | Run SSH command (with optional `stream` for real-time output) |
117
+ | `show-whitelist` | Show active command policy + SFTP policy + output-log path for a server |
118
+ | `close-connection` | Close a cached SSH connection for a server |
119
+ | `upload` | Upload local file to a remote server (CRLF-fix for shell scripts, skip-if-identical, optional `reuseConnection`, `vvv`, `fast`) |
120
+ | `download` | Download remote file to local disk (optional `reuseConnection`, `vvv`, `fast`) |
121
+ | `transfer` | Unified upload / download / server-to-server relay (`mode`: `upload` / `download` / `relay`, optional `recursive`, `skipIfIdentical`, `reuseConnection`, `vvv`, `fast`) |
121
122
  | `list-servers` | List configured (enabled) servers. Lean by default; `verbose:true` adds cached system status, `refresh:true` re-collects it (implies verbose). |
122
123
  | `help` | Self-describing help text for the MCP client |
123
124
 
@@ -142,24 +143,41 @@ Returns command mode, built-in command guards, configured whitelist/blacklist pa
142
143
  {
143
144
  "tool": "execute-command",
144
145
  "params": {
145
- "cmdString": "docker ps",
146
- "connectionName": "dev",
147
- "timeout": 300000,
148
- "stream": true
149
- }
150
- }
151
- ```
146
+ "cmdString": "docker ps",
147
+ "connectionName": "dev",
148
+ "timeout": 300000,
149
+ "stream": true,
150
+ "reuseConnection": true,
151
+ "vvv": false
152
+ }
153
+ }
154
+ ```
152
155
 
153
156
  | Param | Required | Default | Description |
154
157
  |-------|----------|---------|-------------|
155
158
  | `cmdString` | ✅ | - | Command to execute |
156
- | `connectionName` | ❌ | First in `--enable-servers` | Which server to run on |
157
- | `timeout` | ❌ | 300000ms (stream) / 30000ms (no stream) | Timeout in ms |
158
- | `stream` | ❌ | `true` | Real-time streaming output |
159
-
160
- **When to use `stream: false`:**
161
- - Simple, fast commands (ls, pwd, cat)
162
- - When you don't need real-time feedback
159
+ | `connectionName` | ❌ | First in `--enable-servers` | Which server to run on |
160
+ | `timeout` | ❌ | 300000ms (stream) / 30000ms (no stream) | Per-attempt phase timeout in ms. SSH setup, exec-channel opening, and remote command execution each use this cap. |
161
+ | `stream` | ❌ | `true` | Real-time streaming output |
162
+ | `reuseConnection` | ❌ | `true` | Reuse the cached SSH connection. Set `false` after a timeout or suspected stale cached connection to force a fresh TCP/SSH connection for this command. |
163
+ | `vvv` | ❌ | `false` | Append bounded SSH/channel debug output. Use with `reuseConnection: false` when you need fresh ssh2 handshake logs. |
164
+
165
+ **When to use `stream: false`:**
166
+ - Simple, fast commands (ls, pwd, cat)
167
+ - When you don't need real-time feedback
168
+
169
+ ### close-connection
170
+
171
+ ```json
172
+ {
173
+ "tool": "close-connection",
174
+ "params": {
175
+ "connectionName": "dev"
176
+ }
177
+ }
178
+ ```
179
+
180
+ Closes the cached SSH client for a configured server. This is useful after a timeout or suspected stale reused connection when you want the next default `reuseConnection: true` command to reconnect cleanly. Closing a jump host also closes cached targets whose jump chain uses that host. `reuseConnection: false` commands do not need this because their one-shot SSH clients close after each command.
163
181
 
164
182
  ## 📄 YAML Config Reference
165
183
 
@@ -276,16 +294,22 @@ Rules (enforced at config load — bad configs fail fast):
276
294
 
277
295
  ### Connection lifecycle (connect / reconnect)
278
296
 
279
- - **Lazy by default.** A server's SSH client is created on its first tool call via `ensureConnected()`. Set `preConnect: true` (or pass `--pre-connect`) to open all enabled servers at startup in parallel; failures are logged but don't block startup.
280
- - **Auto-reconnect on `execute-command`.** Every command runs inside a retry loop (default 3 attempts: 1 initial + 2 retries) with exponential backoff. If the underlying error matches a connection-shaped pattern (`econnreset`, `epipe`, `socket`, `closed`, `channel`, `end of stream`, or a `SSH_CONNECTION_FAILED` ToolError), the manager closes the dead client, reconnects, and retries the command. Non-connection errors (permission denied, validation, command-not-found) are returned immediately without retry.
281
- - **SFTP transfers do NOT auto-retry.** `upload` / `download` / `transfer` lazy-connect via the same path, but a mid-transfer disconnect surfaces as a single failure re-issue the call manually. This is a known gap.
282
- - **No background keepalive or health probe.** Dead connections are only discovered on the next tool call. If you idle for hours through a NATed network, expect the first call after the gap to fail-then-reconnect on its own (you'll see one retry in the logs).
297
+ - **Lazy by default.** A server's SSH client is created on its first tool call via `ensureConnected()`. Set `preConnect: true` (or pass `--pre-connect`) to open all enabled servers at startup in parallel; failures are logged but don't block startup.
298
+ - **Cached clients stay open while reused.** With the default `reuseConnection: true`, `execute-command` does not close the SSH client after each command. If a cached client must be opened first, SSH setup, jump, SOCKS, and channel-open waits are bounded by the call's `timeout`. The cached client is closed on explicit disconnect/server shutdown, config hot-reload for changed connection fields, underlying SSH close/error cleanup, or reconnect after a connection-shaped command failure.
299
+ - **Manual cached-client close.** Use `close-connection { connectionName: "name" }` to drop a cached SSH client on demand. If the named server is a jump host, cached targets that jump through it are closed too.
300
+ - **Auto-reconnect on `execute-command`.** Every command runs inside a retry loop (default 3 attempts: 1 initial + 2 retries) with exponential backoff. If the underlying error matches a connection-shaped pattern (`econnreset`, `epipe`, `socket`, `closed`, `channel`, `end of stream`, or a `SSH_CONNECTION_FAILED` ToolError), the manager closes the dead client, reconnects, and retries the command. Non-connection errors (permission denied, validation, command-not-found) are returned immediately without retry.
301
+ - **Optional fresh command/SFTP connections.** `execute-command`, `upload`, `download`, and `transfer` reuse cached SSH clients by default (`reuseConnection: true`). If a call times out or you suspect the cached `ssh2.Client` is stale while native `ssh` works, retry with `reuseConnection: false`; that one operation opens a fresh SSH connection and closes it afterwards. SFTP still opens a new SFTP channel/session per operation.
302
+ - **Optional SSH debug output.** Set `vvv: true` on `execute-command` to append bounded SSH/channel debug output to the result or error. For full ssh2 handshake logs, combine it with `reuseConnection: false`; an already-open cached client cannot retroactively emit handshake debug.
303
+ - **Optional SFTP debug output.** Set `vvv: true` on `upload`, `download`, or `transfer` to append bounded SSH/SFTP debug where the result is textual. Recursive `transfer` success keeps its structured `{ summary, files }` JSON response, so recursive debug is surfaced on errors rather than appended to successful summaries.
304
+ - **SFTP transfers do NOT auto-retry.** `upload` / `download` / `transfer` lazy-connect via the same acquisition path and close stale cached clients on connection-shaped SFTP/channel errors, but a mid-transfer disconnect surfaces as a single failure — re-issue the call manually, preferably with `reuseConnection: false` if you suspect the cached SSH client.
305
+ - **No background keepalive or health probe.** Dead connections are only discovered on the next tool call. If you idle for hours through a NATed network, expect the first call after the gap to fail-then-reconnect on its own (you'll see one retry in the logs).
283
306
 
284
307
  ### Upload behaviors
285
308
 
286
- - **CRLF auto-fix for shell scripts.** When uploading a `.sh`, `.bash`, or `.zsh` file, any `\r\n` line endings are automatically converted to `\n` before the bytes are sent. The response notes when this happens and how many line endings were rewritten. The local file on disk is left untouched.
287
- - **Skip-if-identical (default on).** Before transferring, `upload` checks whether the remote file already matches the local payload. Files ≤ 256 MiB are compared byte-for-byte; larger files are compared via MD5 (using `md5sum` on the remote host). **Shell scripts (`.sh` / `.bash` / `.zsh`) are compared in a line-ending-agnostic way — both sides are LF-normalized before the comparison, so a CRLF-only diff is treated as identical and the upload is still skipped.** If they match, the upload is skipped and the response says so. Pass `skipIfIdentical: false` to force a re-upload. Recursive `transfer` (`mode: upload`, `recursive: true`) applies the same check per file.
288
- - **Relay skip-if-identical (default on).** `transfer mode=relay` does the same check between two remote servers: matching size on both sides plus matching `md5sum` skips the transfer. If `md5sum` is missing on either server, the check falls back to a normal transfer.
309
+ - **CRLF auto-fix for shell scripts.** When uploading a `.sh`, `.bash`, or `.zsh` file, any `\r\n` line endings are automatically converted to `\n` before the bytes are sent. The response notes when this happens and how many line endings were rewritten. The local file on disk is left untouched.
310
+ - **Skip-if-identical (default on).** Before transferring, `upload` checks whether the remote file already matches the local payload. Files ≤ 256 MiB are compared byte-for-byte; larger files are compared via MD5 (using `md5sum` on the remote host). **Shell scripts (`.sh` / `.bash` / `.zsh`) are compared in a line-ending-agnostic way — both sides are LF-normalized before the comparison, so a CRLF-only diff is treated as identical and the upload is still skipped.** If they match, the upload is skipped and the response says so. Pass `skipIfIdentical: false` to force a re-upload. Recursive `transfer` (`mode: upload`, `recursive: true`) applies the same check per file.
311
+ - **Fast single-file SFTP (default off).** Set `fast: true` on `upload`, `download`, or `transfer` upload/download mode to use ssh2 `fastPut` / `fastGet` for host-to-remote or remote-to-host single-file throughput. Optional `sftpConcurrency` and `chunkSize` are passed to ssh2 only when `fast: true`; omitted values use ssh2 defaults. This is not multi-file concurrency: recursive directory transfer remains sequential, and relay mode keeps its remote-A → MCP host → remote-B streaming pipe path. If a shell-script upload needs CRLF-to-LF conversion, it falls back to the normal safe upload path.
312
+ - **Relay skip-if-identical (default on).** `transfer mode=relay` does the same check between two remote servers: matching size on both sides plus matching `md5sum` skips the transfer. If `md5sum` is missing on either server, the check falls back to a normal transfer.
289
313
 
290
314
  ### `execute-command` output capping & full logs
291
315
 
@@ -353,8 +377,10 @@ Example with selective servers:
353
377
  ### Nice to Have
354
378
  - [x] **Command history / output archive**: full stdout/stderr of every `execute-command` is persisted under `<outputLogDir>/<server>/<user>/*.log`
355
379
  - [x] **Multi-command execution**: Execute multiple commands in sequence with `&&` or `;` safely (fixed: `2>/dev/null` now allowed)
356
- - [x] **Connection auto-recovery**: `execute-command` retries with exponential backoff and forced reconnect on connection-shaped errors
357
- - [ ] **SFTP retry parity**: extend the retry-with-reconnect loop to `upload` / `download` / `transfer`
380
+ - [x] **Connection auto-recovery**: `execute-command` retries with exponential backoff and forced reconnect on connection-shaped errors
381
+ - [x] **SFTP connection reuse controls**: `upload` / `download` / `transfer` support `reuseConnection`, `vvv`, and explicit fresh one-shot SSH clients
382
+ - [x] **Fast single-file SFTP**: optional ssh2 `fastPut` / `fastGet` via `fast`, `sftpConcurrency`, and `chunkSize`
383
+ - [ ] **SFTP retry parity**: extend the retry-with-reconnect loop to `upload` / `download` / `transfer`
358
384
  - [ ] **TCP keepalive**: pass `keepaliveInterval` / `keepaliveCountMax` to `ssh2.Client` so half-open connections are detected without waiting for the next command
359
385
  - [ ] **Server health check**: optional periodic ping to detect drops proactively
360
386
 
@@ -1,6 +1,6 @@
1
1
  export const SERVER_CONFIG = {
2
2
  name: "ssh-mcp-server",
3
- version: "1.0.5",
3
+ version: "1.0.7",
4
4
  };
5
5
  export const SERVER_INSTRUCTIONS = `This server provides SSH access to servers loaded from OpenSSH config (~/.ssh/config by default) and optional YAML config/policy overlays.
6
6
 
@@ -10,16 +10,21 @@ Recommended workflow:
10
10
  3. Use execute-command for remote shell commands. Prefer a single command per call. Compound commands may be rejected by command policy even if each subcommand is individually safe.
11
11
  4. Use stream=false for short commands that should finish quickly, such as pwd, ls, cat, head, tail, git status, or docker ps.
12
12
  5. Use stream=true for commands that may take longer or where incremental output is useful.
13
- 6. Use upload and download for single-file SFTP transfers. Use transfer for recursive directory transfers or cross-server relay.
14
- 7. Call help or help { tool: "<name>" } for detailed per-tool parameter docs and examples.
13
+ 6. execute-command reuses SSH connections by default. If an execute-command call times out, retry the next command with reuseConnection=false to bypass a potentially stale cached SSH connection.
14
+ 7. SFTP tools also reuse SSH connections by default. If upload/download/transfer times out or reports a connection-shaped SFTP/channel error, retry with reuseConnection=false; the fresh SSH connection closes after that transfer.
15
+ 8. Use close-connection to close a cached SSH connection for a server before retrying with a clean reused connection. Closing a jump host also closes cached targets that jump through it.
16
+ 9. For SSH/channel diagnostics, add vvv=true. Use it with reuseConnection=false when you need fresh handshake/debug output.
17
+ 10. Use upload and download for single-file SFTP transfers. Use transfer for recursive directory transfers or cross-server relay.
18
+ 11. For large host-to-remote or remote-to-host single-file SFTP transfers, fast=true enables ssh2 fastPut/fastGet with optional sftpConcurrency and chunkSize. It is off by default and does not add multi-file concurrency; relay keeps the streaming pipe path.
19
+ 12. Call help or help { tool: "<name>" } for detailed per-tool parameter docs and examples.
15
20
 
16
21
  Server targeting:
17
22
  - When only one server is enabled, connectionName can be omitted and the server is auto-selected.
18
- - When multiple servers are enabled, connectionName is REQUIRED on execute-command, upload, download, show-whitelist, and transfer (upload/download mode). Omitting it returns an error listing the available names.
23
+ - When multiple servers are enabled, connectionName is REQUIRED on execute-command, close-connection, upload, download, show-whitelist, and transfer (upload/download mode). Omitting it returns an error listing the available names.
19
24
 
20
25
  Behavior notes:
21
26
  - A tool may automatically establish the SSH connection on first use.
22
27
  - Command execution uses blacklist mode by default, with a built-in dangerous-command blacklist. Servers can opt into whitelist mode through YAML. If a command is rejected, inspect the command policy rather than retrying the same command repeatedly.
23
28
  - Some commands return no output on success; this is normal.
24
- - File transfer tools use SFTP and can fail if the remote path is missing or permissions are insufficient.
29
+ - File transfer tools use SFTP and can fail if the remote path is missing or permissions are insufficient. They open a new SFTP channel per operation; they reuse the underlying SSH client unless reuseConnection=false is set.
25
30
  - OpenSSH/YAML config changes are hot-reloaded without restarting the server. Connection-field changes close the old client so the next tool call reconnects with fresh settings.`;