smooth-ssh-mcp 0.1.1

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.
Files changed (56) hide show
  1. package/LICENSE +21 -0
  2. package/README.en.md +319 -0
  3. package/README.md +32 -0
  4. package/README.zh-CN.md +319 -0
  5. package/bin/smooth-ssh-mcp-codex +43 -0
  6. package/dist/audit.d.ts +23 -0
  7. package/dist/audit.js +140 -0
  8. package/dist/audit.js.map +1 -0
  9. package/dist/auth.d.ts +8 -0
  10. package/dist/auth.js +19 -0
  11. package/dist/auth.js.map +1 -0
  12. package/dist/doctor.d.ts +27 -0
  13. package/dist/doctor.js +169 -0
  14. package/dist/doctor.js.map +1 -0
  15. package/dist/forwardManager.d.ts +49 -0
  16. package/dist/forwardManager.js +141 -0
  17. package/dist/forwardManager.js.map +1 -0
  18. package/dist/init.d.ts +21 -0
  19. package/dist/init.js +80 -0
  20. package/dist/init.js.map +1 -0
  21. package/dist/inventory.d.ts +4 -0
  22. package/dist/inventory.js +262 -0
  23. package/dist/inventory.js.map +1 -0
  24. package/dist/mcpServer.d.ts +8 -0
  25. package/dist/mcpServer.js +403 -0
  26. package/dist/mcpServer.js.map +1 -0
  27. package/dist/operations.d.ts +167 -0
  28. package/dist/operations.js +1240 -0
  29. package/dist/operations.js.map +1 -0
  30. package/dist/policy.d.ts +21 -0
  31. package/dist/policy.js +470 -0
  32. package/dist/policy.js.map +1 -0
  33. package/dist/redaction.d.ts +2 -0
  34. package/dist/redaction.js +64 -0
  35. package/dist/redaction.js.map +1 -0
  36. package/dist/runner.d.ts +24 -0
  37. package/dist/runner.js +90 -0
  38. package/dist/runner.js.map +1 -0
  39. package/dist/server.d.ts +9 -0
  40. package/dist/server.js +130 -0
  41. package/dist/server.js.map +1 -0
  42. package/dist/sessionManager.d.ts +77 -0
  43. package/dist/sessionManager.js +195 -0
  44. package/dist/sessionManager.js.map +1 -0
  45. package/dist/sshArgs.d.ts +24 -0
  46. package/dist/sshArgs.js +135 -0
  47. package/dist/sshArgs.js.map +1 -0
  48. package/dist/stateStore.d.ts +27 -0
  49. package/dist/stateStore.js +99 -0
  50. package/dist/stateStore.js.map +1 -0
  51. package/dist/types.d.ts +95 -0
  52. package/dist/types.js +2 -0
  53. package/dist/types.js.map +1 -0
  54. package/docs/mcp-client.example.json +15 -0
  55. package/examples/hosts.example.yaml +79 -0
  56. package/package.json +58 -0
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Da-bai-da
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.en.md ADDED
@@ -0,0 +1,319 @@
1
+ # smooth-ssh-mcp
2
+
3
+ [中文](README.zh-CN.md) | [Back to README](README.md)
4
+
5
+ ## What It Is
6
+
7
+ `smooth-ssh-mcp` is an SSH connection management MCP server for AI assistants. It wraps system OpenSSH as a tool layer designed for model use: assistants can probe connections, inspect servers, run remote commands, transfer files, and manage port forwards without seeing real passwords or private key contents.
8
+
9
+ It is an accidental-damage prevention and audit layer for SSH operations. It is not a remote shell sandbox or a full terminal emulator.
10
+
11
+ ## Use Cases
12
+
13
+ - Let Codex, Claude, OpenCode, or another MCP client operate saved SSH hosts.
14
+ - List hosts, choose a default host, and inspect recent host usage.
15
+ - Diagnose SSH failures such as TCP errors, timeouts, authentication failure, host key problems, or missing config.
16
+ - Run argv-style remote commands instead of ad-hoc shell strings.
17
+ - Run read-only host health checks for system info, load, memory, disk, services, ports, Docker, and key logs.
18
+ - Open bounded interactive sessions for short commands, simple menus, REPLs, or temporary remote context.
19
+ - Upload and download files with SCP.
20
+ - Manage local `ssh -N -L` port forwards.
21
+ - Require confirmation for sudo, writes, deletes, restarts, firewall changes, sensitive paths, and other risky operations.
22
+
23
+ It is not meant to replace tmux, full-screen TUI programs, long-running unattended sessions, a strong isolation sandbox, or a CI/CD orchestration platform.
24
+
25
+ ## Design Goals
26
+
27
+ - **Reuse OpenSSH**: use system `ssh` and `scp`, including `~/.ssh/config`, ssh-agent, ProxyJump, known_hosts, hardware keys, and certificates.
28
+ - **Prefer structured operations**: expose clear MCP tools and argv-style command execution; keep `ssh_exec` as a shell fallback.
29
+ - **Do not store secrets**: inventory files store connection metadata and key paths, not passwords, private key contents, or sudo passwords.
30
+ - **Auditable confirmation**: risky operations return `confirmationRequired`; confirmation tokens are bound to host, operation, command/path/ports, expire quickly, and are single-use.
31
+ - **Protect returned output**: stdout/stderr are redacted and truncated before being returned.
32
+ - **Reuse connections**: use OpenSSH `ControlMaster`, `ControlPath`, and `ControlPersist` automatically.
33
+
34
+ ## Feature Overview
35
+
36
+ - YAML/JSON host inventory using either `hostname` or `sshConfigHost`.
37
+ - Password authentication through `passwordEnv` and `sshpass -e`.
38
+ - SSH probes with classified failure reasons.
39
+ - Fixed read-only host health checks.
40
+ - `ssh_command` for argv-style remote program execution.
41
+ - `task_batch` for per-task review, confirmation, and resumable execution.
42
+ - `cleanup_paths` for confirming an exact remote path set once.
43
+ - Bounded interactive sessions through `session_start`, `session_send`, `session_read`, and `session_stop`.
44
+ - SCP transfer through `file_upload` and `file_download`.
45
+ - Managed local forwards through `forward_start`, `forward_stop`, and `forward_list`.
46
+ - Persistent local state through `host_select`, `host_recent`, and `host_permission_set`.
47
+
48
+ ## Installation
49
+
50
+ Requirements:
51
+
52
+ - Node.js `>=20`
53
+ - System OpenSSH clients: `ssh`, `scp`
54
+ - `sshpass` if password authentication is used
55
+
56
+ Install from the GitHub source tree:
57
+
58
+ ```bash
59
+ git clone https://github.com/Da-bai-da/smooth-ssh-mcp.git
60
+ cd smooth-ssh-mcp
61
+ npm install
62
+ npm run build
63
+ node dist/server.js init
64
+ node dist/server.js doctor --config ~/.config/smooth-ssh-mcp/hosts.yaml --secrets ~/.config/smooth-ssh-mcp/secrets.env
65
+ ```
66
+
67
+ After npm publication, it can also be installed globally:
68
+
69
+ ```bash
70
+ npm install -g smooth-ssh-mcp
71
+ smooth-ssh-mcp init
72
+ smooth-ssh-mcp doctor --config ~/.config/smooth-ssh-mcp/hosts.yaml --secrets ~/.config/smooth-ssh-mcp/secrets.env
73
+ smooth-ssh-mcp --help
74
+ smooth-ssh-mcp --version
75
+ ```
76
+
77
+ ## Initialize Configuration
78
+
79
+ Run `init` first to create a local configuration skeleton:
80
+
81
+ ```bash
82
+ node dist/server.js init
83
+ ```
84
+
85
+ By default it writes `~/.config/smooth-ssh-mcp/hosts.yaml` and `~/.config/smooth-ssh-mcp/secrets.env`, with directory mode `700` and file mode `600`. If files already exist, `init` preserves them; add `--force` only when you want to regenerate them.
86
+
87
+ ```bash
88
+ node dist/server.js init --force
89
+ node dist/server.js init --config /path/to/hosts.yaml --secrets /path/to/secrets.env
90
+ ```
91
+
92
+ ## Host Configuration
93
+
94
+ Minimal host:
95
+
96
+ ```yaml
97
+ hosts:
98
+ - id: lab-linux
99
+ hostname: 192.0.2.10
100
+ user: ubuntu
101
+ port: 22
102
+ identityFile: ~/.ssh/lab.pem
103
+ environment: dev
104
+ policy:
105
+ permissionLevel: 2
106
+ allowExec: true
107
+ allowPty: true
108
+ allowUpload: true
109
+ allowDownload: true
110
+ allowForward: true
111
+ ```
112
+
113
+ Reference a Host from `~/.ssh/config`:
114
+
115
+ ```yaml
116
+ hosts:
117
+ - id: gateway
118
+ sshConfigHost: openwrt-gw
119
+ environment: staging
120
+ tags: [openwrt, gateway]
121
+ ```
122
+
123
+ Password authentication stores only the environment variable name:
124
+
125
+ ```yaml
126
+ hosts:
127
+ - id: password-vps
128
+ hostname: 203.0.113.20
129
+ user: root
130
+ port: 22
131
+ passwordEnv: SMOOTH_SSH_PASSWORD_PASSWORD_VPS
132
+ ```
133
+
134
+ Do not put real passwords in `hosts.yaml`. Put them in your MCP client's secret env, or load them through the local wrapper.
135
+
136
+ ## Doctor Checks
137
+
138
+ The `doctor` subcommand checks the Node.js version, `ssh`, `scp`, optional `sshpass`, inventory file, secrets file, and file permissions.
139
+
140
+ ```bash
141
+ node dist/server.js doctor --config ~/.config/smooth-ssh-mcp/hosts.yaml --secrets ~/.config/smooth-ssh-mcp/secrets.env
142
+ node dist/server.js doctor --config ~/.config/smooth-ssh-mcp/hosts.yaml --secrets ~/.config/smooth-ssh-mcp/secrets.env --json
143
+ ```
144
+
145
+ ## MCP Client Configuration
146
+
147
+ stdio example:
148
+
149
+ ```json
150
+ {
151
+ "mcpServers": {
152
+ "smooth-ssh": {
153
+ "command": "node",
154
+ "args": [
155
+ "/path/to/smooth-ssh-mcp/dist/server.js",
156
+ "--config",
157
+ "/home/you/.config/smooth-ssh-mcp/hosts.yaml"
158
+ ],
159
+ "env": {
160
+ "SMOOTH_SSH_PASSWORD_PASSWORD_VPS": "set-this-in-your-client-secret-env"
161
+ }
162
+ }
163
+ }
164
+ }
165
+ ```
166
+
167
+ Codex wrapper example:
168
+
169
+ ```toml
170
+ [mcp_servers.smooth-ssh]
171
+ command = "/path/to/smooth-ssh-mcp/bin/smooth-ssh-mcp-codex"
172
+ ```
173
+
174
+ The wrapper reads:
175
+
176
+ - `SMOOTH_SSH_MCP_CONFIG`, defaulting to `~/.config/smooth-ssh-mcp/hosts.yaml`
177
+ - `SMOOTH_SSH_MCP_SECRETS`, defaulting to `~/.config/smooth-ssh-mcp/secrets.env`
178
+
179
+ `secrets.env` must be owned by the current user and have mode `600` or `400`. The wrapper treats it as `KEY=value` data and does not execute shell code from it.
180
+
181
+ ## Tools
182
+
183
+ | Tool | Description |
184
+ | --- | --- |
185
+ | `capability_list` | Lists structured capabilities and the shell fallback policy. |
186
+ | `host_list` | Lists host aliases and non-secret metadata. |
187
+ | `host_get` | Shows one host without returning passwords or private key contents. |
188
+ | `host_select` | Stores the current default host. |
189
+ | `host_recent` | Shows the selected host and recently used hosts. |
190
+ | `host_permission_set` | Stores a per-host permission level. Setting `1` requires confirmation. |
191
+ | `host_connect` | Probes a host and can optionally start an interactive session. |
192
+ | `ssh_probe` | Runs a read-only SSH probe and classifies failure reasons. |
193
+ | `host_health` | Runs fixed read-only host, load, memory, disk, service, port, Docker, and log checks. |
194
+ | `ssh_command` | Executes one remote program using argv-style input. |
195
+ | `task_batch` | Runs argv tasks one by one; risky items stop with a resume index. |
196
+ | `ssh_exec` | Bounded shell fallback; prefer structured tools. |
197
+ | `cleanup_paths` | Deletes exact remote paths or empties exact directories after one confirmation. |
198
+ | `session_start` | Starts a bounded interactive SSH session. |
199
+ | `session_send` | Sends input to an open session; risky input triggers confirmation. |
200
+ | `session_read` | Reads and clears session output. |
201
+ | `session_stop` | Stops a managed session. |
202
+ | `session_list` | Lists managed sessions. |
203
+ | `file_upload` | Uploads a local file through SCP. |
204
+ | `file_download` | Downloads a remote file through SCP. |
205
+ | `forward_start` | Starts a managed local port forward. |
206
+ | `forward_stop` | Stops a managed port forward. |
207
+ | `forward_list` | Lists managed port forwards. |
208
+
209
+ ## Permission Levels
210
+
211
+ Each host can use a numeric permission level:
212
+
213
+ - `1`: highest privilege. Risky operations are treated as authorized and skip the usual smooth-ssh confirmation layer; hard host switches and disaster command blocks still apply.
214
+ - `2`: recommended default. Read-only operations run directly; writes, sudo, restarts, uploads, downloads, and port forwards require confirmation.
215
+ - `3`: restricted read-only. Only clearly recognized read-only `exec` operations are allowed; writes, sudo, restarts, uploads, downloads, port forwards, and interactive PTY are rejected.
216
+
217
+ Set this in `hosts.yaml` as `policy.permissionLevel`, or persist an override with `host_permission_set`.
218
+
219
+ ## Confirmation Flow
220
+
221
+ Risky operations first return:
222
+
223
+ ```json
224
+ {
225
+ "confirmationRequired": true,
226
+ "token": "4f2d...",
227
+ "risk": "high",
228
+ "operation": "exec",
229
+ "preview": {
230
+ "command": "hostname"
231
+ }
232
+ }
233
+ ```
234
+
235
+ After confirmation, retry the same tool call with `confirmationToken`:
236
+
237
+ ```json
238
+ {
239
+ "hostId": "prod-api",
240
+ "command": "hostname",
241
+ "confirmationToken": "4f2d..."
242
+ }
243
+ ```
244
+
245
+ When `task_batch` is blocked by a risky task, it returns `blocked.resumeFrom`. Retry with the same task list plus `startAt: blocked.resumeFrom` to resume without rerunning completed tasks.
246
+
247
+ ## Bounded Interactive Sessions
248
+
249
+ `session_start` uses the system `ssh -tt` command over stdin/stdout pipes. It does not use `node-pty`.
250
+
251
+ Good fits:
252
+
253
+ - Simple REPLs
254
+ - One-off menus
255
+ - Commands that need a short-lived working directory or remote context
256
+
257
+ Poor fits:
258
+
259
+ - Full-screen TUI programs such as `vim`, `top`, `htop`, or `tmux`
260
+ - Programs that require real terminal dimensions, cursor control, or complex ANSI refreshes
261
+ - Long-running unattended sessions
262
+
263
+ Sessions have TTL, idle timeout, maximum session count, and a ring buffer. `session_read` clears the buffer after reading.
264
+
265
+ ## Security Model
266
+
267
+ `smooth-ssh-mcp` is an accidental-damage prevention and audit layer. It is not a remote shell sandbox.
268
+
269
+ Default policies include:
270
+
271
+ - Commands on prod/high-risk hosts require confirmation when they cannot be confidently classified as read-only.
272
+ - sudo, writes, complex shell, uploads, downloads, and port forwards require confirmation.
273
+ - Commands touching sensitive remote paths such as `.env`, `.ssh`, private keys, `.pem`, `.key`, or `/etc/shadow` are never confirmation-free.
274
+ - Downloads from sensitive paths are denied by default.
275
+ - SCP remote paths disallow whitespace and shell metacharacters so file transfer cannot become command execution.
276
+ - Disaster commands such as `rm -rf /`, `mkfs`, `dd of=...`, reboots, and shutdowns are denied by default.
277
+ - sudo passwords are never entered automatically.
278
+ - `known_hosts` is not modified automatically.
279
+ - `acceptNewHostKey: true` only lets OpenSSH save first-seen host keys; changed host keys are still rejected by OpenSSH.
280
+ - Password authentication reads from environment variables only and passes the value to `sshpass -e` through `SSHPASS`.
281
+
282
+ ## Audit Log
283
+
284
+ When the MCP server runs, it writes JSONL audit logs by default:
285
+
286
+ ```text
287
+ ~/.config/smooth-ssh-mcp/audit.jsonl
288
+ ```
289
+
290
+ Audit entries include tool name, hostId, operation, result kind, exit code, duration, truncation state, and redaction counts. `confirmationToken`, `env`, `stdin`, password fields, and secret fields are written as `[REDACTED]`.
291
+
292
+ Environment overrides:
293
+
294
+ ```bash
295
+ SMOOTH_SSH_MCP_AUDIT=0 smooth-ssh-mcp
296
+ SMOOTH_SSH_MCP_AUDIT_LOG=/path/to/audit.jsonl smooth-ssh-mcp
297
+ ```
298
+
299
+ ## State File
300
+
301
+ `host_select`, `host_recent`, and `host_permission_set` write to:
302
+
303
+ ```text
304
+ ~/.config/smooth-ssh-mcp/state.json
305
+ ```
306
+
307
+ The state file stores only host IDs, timestamps, use counts, use reasons, and permission levels. It does not store passwords, private key paths, session IDs, or shell state.
308
+
309
+ ## Development
310
+
311
+ ```bash
312
+ npm test
313
+ npm run typecheck
314
+ npm run build
315
+ npm run test:cli
316
+ npm pack --dry-run
317
+ ```
318
+
319
+ GitHub Actions runs `npm ci`, `npm run typecheck`, `npm test`, `npm run build`, CLI smoke tests, and `npm pack --dry-run` on pushes and pull requests.
package/README.md ADDED
@@ -0,0 +1,32 @@
1
+ # smooth-ssh-mcp
2
+
3
+ [中文说明](README.zh-CN.md) | [English README](README.en.md)
4
+
5
+ An MCP server that gives AI assistants a safer, structured way to use OpenSSH.
6
+
7
+ `smooth-ssh-mcp` wraps the system `ssh` and `scp` clients as a managed connection layer with host aliases, connection probes, argv-style remote commands, bounded interactive sessions, SCP transfer, local port forwards, risk confirmation, and output redaction.
8
+
9
+ `smooth-ssh-mcp` 是一个面向 AI 助手的 SSH 连接管理 MCP server。它复用系统 OpenSSH,并提供主机别名、连接探测、结构化远端命令、有限交互会话、SCP 文件传输、本地端口转发、风险确认和输出脱敏。
10
+
11
+ ## Documentation
12
+
13
+ - [中文说明](README.zh-CN.md)
14
+ - [English README](README.en.md)
15
+
16
+ ## Quick Start
17
+
18
+ ```bash
19
+ git clone https://github.com/Da-bai-da/smooth-ssh-mcp.git
20
+ cd smooth-ssh-mcp
21
+ npm install
22
+ npm run build
23
+ node dist/server.js init
24
+ node dist/server.js doctor --config ~/.config/smooth-ssh-mcp/hosts.yaml --secrets ~/.config/smooth-ssh-mcp/secrets.env
25
+ node dist/server.js --help
26
+ ```
27
+
28
+ After npm publication, use `npm install -g smooth-ssh-mcp` and replace `node dist/server.js` with `smooth-ssh-mcp`.
29
+
30
+ See the language-specific README for host inventory, MCP client configuration, permission levels, confirmation flow, and security model.
31
+
32
+ 请查看对应语言的 README,了解主机 inventory、MCP 客户端配置、权限等级、确认流程和安全模型。
@@ -0,0 +1,319 @@
1
+ # smooth-ssh-mcp
2
+
3
+ [English](README.en.md) | [返回主页](README.md)
4
+
5
+ ## 这是什么
6
+
7
+ `smooth-ssh-mcp` 是一个面向 AI 助手的 SSH 连接管理 MCP server。它把系统 OpenSSH 封装成更适合模型调用的工具层:模型不需要临时拼接复杂 shell,也不需要知道真实密码或私钥内容,就可以通过受控工具完成连接探测、服务器巡检、远端命令、文件传输和端口转发。
8
+
9
+ 它的定位是“防误操作、可审计、适合 AI 调用的 SSH 操作层”,不是远端 shell 沙箱,也不是完整终端模拟器。
10
+
11
+ ## 适合的场景
12
+
13
+ - 让 Codex、Claude、OpenCode 或其他 MCP client 管理保存好的 SSH 主机。
14
+ - 快速查看主机列表、当前默认主机和最近使用记录。
15
+ - 探测 SSH 连接失败原因,例如 TCP 不通、超时、认证失败、host key 问题或配置缺失。
16
+ - 执行明确的 argv 风格远端命令,减少模型拼接 shell 带来的风险。
17
+ - 做只读服务器巡检:系统信息、负载、内存、磁盘、服务、端口、Docker 和关键日志。
18
+ - 打开有限交互 SSH 会话,用于短命令、简单菜单、REPL 或需要保留 cwd 的临时上下文。
19
+ - 通过 SCP 上传和下载文件。
20
+ - 管理本地 `ssh -N -L` 端口转发。
21
+ - 对 sudo、写入、删除、重启、防火墙、敏感路径等风险操作做确认。
22
+
23
+ 不适合的场景:替代 tmux、运行全屏 TUI、长期无人值守会话、强隔离安全沙箱,或复杂 CI/CD 编排。
24
+
25
+ ## 核心设计
26
+
27
+ - **复用 OpenSSH**:使用系统 `ssh` / `scp`,兼容 `~/.ssh/config`、ssh-agent、ProxyJump、known_hosts、硬件密钥和证书。
28
+ - **结构化优先**:优先使用 MCP tools 和 argv 风格命令;`ssh_exec` 只是 shell 兜底。
29
+ - **不保存秘密**:inventory 保存连接元数据和私钥路径,不保存密码、私钥内容或 sudo 密码。
30
+ - **确认可审计**:高风险操作先返回 `confirmationRequired`;确认 token 绑定 host、operation、command/path/ports,短期有效且一次性消费。
31
+ - **输出保护**:stdout/stderr 返回前默认脱敏和截断。
32
+ - **连接复用**:自动使用 OpenSSH `ControlMaster` / `ControlPath` / `ControlPersist`。
33
+
34
+ ## 功能概览
35
+
36
+ - YAML/JSON host inventory,支持 `hostname` 或 `sshConfigHost`。
37
+ - 密码登录通过 `passwordEnv` 引用环境变量,并使用 `sshpass -e`。
38
+ - SSH 探测和失败分类。
39
+ - 固定只读健康检查。
40
+ - `ssh_command` 执行 argv 风格远端程序。
41
+ - `task_batch` 按任务逐个审计,风险项可确认后续跑。
42
+ - `cleanup_paths` 对一组精确远端路径做一次确认。
43
+ - `session_start` / `session_send` / `session_read` / `session_stop` 管理有限交互会话。
44
+ - `file_upload` / `file_download` 通过 SCP 传输文件。
45
+ - `forward_start` / `forward_stop` / `forward_list` 管理本地端口转发。
46
+ - `host_select` / `host_recent` / `host_permission_set` 持久化本地状态。
47
+
48
+ ## 安装
49
+
50
+ 要求:
51
+
52
+ - Node.js `>=20`
53
+ - 系统 OpenSSH 客户端:`ssh`、`scp`
54
+ - 如果使用密码登录,需要安装 `sshpass`
55
+
56
+ 从 GitHub 源码安装:
57
+
58
+ ```bash
59
+ git clone https://github.com/Da-bai-da/smooth-ssh-mcp.git
60
+ cd smooth-ssh-mcp
61
+ npm install
62
+ npm run build
63
+ node dist/server.js init
64
+ node dist/server.js doctor --config ~/.config/smooth-ssh-mcp/hosts.yaml --secrets ~/.config/smooth-ssh-mcp/secrets.env
65
+ ```
66
+
67
+ 发布到 npm 后也可以全局安装:
68
+
69
+ ```bash
70
+ npm install -g smooth-ssh-mcp
71
+ smooth-ssh-mcp init
72
+ smooth-ssh-mcp doctor --config ~/.config/smooth-ssh-mcp/hosts.yaml --secrets ~/.config/smooth-ssh-mcp/secrets.env
73
+ smooth-ssh-mcp --help
74
+ smooth-ssh-mcp --version
75
+ ```
76
+
77
+ ## 初始化配置
78
+
79
+ 推荐先运行 `init` 生成本地配置骨架:
80
+
81
+ ```bash
82
+ node dist/server.js init
83
+ ```
84
+
85
+ 默认写入 `~/.config/smooth-ssh-mcp/hosts.yaml` 和 `~/.config/smooth-ssh-mcp/secrets.env`,目录权限为 `700`,文件权限为 `600`。如果文件已存在,`init` 会保留原文件;需要重新生成时再加 `--force`。
86
+
87
+ ```bash
88
+ node dist/server.js init --force
89
+ node dist/server.js init --config /path/to/hosts.yaml --secrets /path/to/secrets.env
90
+ ```
91
+
92
+ ## 配置主机
93
+
94
+ 最小配置示例:
95
+
96
+ ```yaml
97
+ hosts:
98
+ - id: lab-linux
99
+ hostname: 192.0.2.10
100
+ user: ubuntu
101
+ port: 22
102
+ identityFile: ~/.ssh/lab.pem
103
+ environment: dev
104
+ policy:
105
+ permissionLevel: 2
106
+ allowExec: true
107
+ allowPty: true
108
+ allowUpload: true
109
+ allowDownload: true
110
+ allowForward: true
111
+ ```
112
+
113
+ 引用 `~/.ssh/config` 中的 Host:
114
+
115
+ ```yaml
116
+ hosts:
117
+ - id: gateway
118
+ sshConfigHost: openwrt-gw
119
+ environment: staging
120
+ tags: [openwrt, gateway]
121
+ ```
122
+
123
+ 密码登录只写环境变量名,不写真实密码:
124
+
125
+ ```yaml
126
+ hosts:
127
+ - id: password-vps
128
+ hostname: 203.0.113.20
129
+ user: root
130
+ port: 22
131
+ passwordEnv: SMOOTH_SSH_PASSWORD_PASSWORD_VPS
132
+ ```
133
+
134
+ 真实密码可以放在 MCP client 的 secret env,或用 wrapper 加载本机 secrets 文件。
135
+
136
+ ## Doctor 检查
137
+
138
+ `doctor` 子命令会检查 Node.js 版本、`ssh`、`scp`、可选的 `sshpass`、inventory 文件、secrets 文件和权限。
139
+
140
+ ```bash
141
+ node dist/server.js doctor --config ~/.config/smooth-ssh-mcp/hosts.yaml --secrets ~/.config/smooth-ssh-mcp/secrets.env
142
+ node dist/server.js doctor --config ~/.config/smooth-ssh-mcp/hosts.yaml --secrets ~/.config/smooth-ssh-mcp/secrets.env --json
143
+ ```
144
+
145
+ ## MCP 客户端配置
146
+
147
+ stdio 示例:
148
+
149
+ ```json
150
+ {
151
+ "mcpServers": {
152
+ "smooth-ssh": {
153
+ "command": "node",
154
+ "args": [
155
+ "/path/to/smooth-ssh-mcp/dist/server.js",
156
+ "--config",
157
+ "/home/you/.config/smooth-ssh-mcp/hosts.yaml"
158
+ ],
159
+ "env": {
160
+ "SMOOTH_SSH_PASSWORD_PASSWORD_VPS": "set-this-in-your-client-secret-env"
161
+ }
162
+ }
163
+ }
164
+ }
165
+ ```
166
+
167
+ Codex wrapper 示例:
168
+
169
+ ```toml
170
+ [mcp_servers.smooth-ssh]
171
+ command = "/path/to/smooth-ssh-mcp/bin/smooth-ssh-mcp-codex"
172
+ ```
173
+
174
+ wrapper 默认读取:
175
+
176
+ - `SMOOTH_SSH_MCP_CONFIG`,默认 `~/.config/smooth-ssh-mcp/hosts.yaml`
177
+ - `SMOOTH_SSH_MCP_SECRETS`,默认 `~/.config/smooth-ssh-mcp/secrets.env`
178
+
179
+ `secrets.env` 必须属于当前用户,权限必须是 `600` 或 `400`。wrapper 只按 `KEY=value` 数据格式导出变量,不执行 secrets 文件中的 shell 代码。
180
+
181
+ ## Tools
182
+
183
+ | Tool | 说明 |
184
+ | --- | --- |
185
+ | `capability_list` | 列出结构化能力和 shell fallback 策略。 |
186
+ | `host_list` | 列出主机别名和非敏感元数据。 |
187
+ | `host_get` | 查看单个主机配置,不返回密码或私钥内容。 |
188
+ | `host_select` | 保存当前默认主机。 |
189
+ | `host_recent` | 查看当前选择和最近使用过的主机。 |
190
+ | `host_permission_set` | 保存某台主机的权限等级。设置 `1` 需要确认。 |
191
+ | `host_connect` | 探测指定或当前主机,可选开启交互会话。 |
192
+ | `ssh_probe` | 只读 SSH 连接探测并分类失败原因。 |
193
+ | `host_health` | 固定只读巡检:主机、负载、内存、磁盘、服务、端口、Docker、关键日志。 |
194
+ | `ssh_command` | 以 argv 风格执行单个远端程序。 |
195
+ | `task_batch` | 逐个执行 argv 任务;遇到风险项停止并返回 resume index。 |
196
+ | `ssh_exec` | bounded shell fallback,优先使用结构化工具。 |
197
+ | `cleanup_paths` | 对一组精确远端路径执行删除或清空目录,使用一次确认。 |
198
+ | `session_start` | 开启有限交互 SSH 会话。 |
199
+ | `session_send` | 向已打开会话发送输入,风险输入会触发确认。 |
200
+ | `session_read` | 读取并清空会话输出缓冲区。 |
201
+ | `session_stop` | 停止托管会话。 |
202
+ | `session_list` | 列出托管会话。 |
203
+ | `file_upload` | 通过 SCP 上传本地文件。 |
204
+ | `file_download` | 通过 SCP 下载远端文件。 |
205
+ | `forward_start` | 启动托管本地端口转发。 |
206
+ | `forward_stop` | 停止托管端口转发。 |
207
+ | `forward_list` | 列出托管端口转发。 |
208
+
209
+ ## 权限等级
210
+
211
+ 每台主机可以使用数字权限等级:
212
+
213
+ - `1`:最高权限。风险操作视为已授权,跳过 smooth-ssh 的常规确认层;主机硬开关和灾难命令拦截仍然生效。
214
+ - `2`:默认推荐。只读操作直接执行;写入、sudo、重启、上传、下载、端口转发等风险操作需要确认。
215
+ - `3`:只读受限。只允许明确识别的只读 `exec`;写入、sudo、重启、上传、下载、端口转发和交互 PTY 直接拒绝。
216
+
217
+ 可以在 `hosts.yaml` 的 `policy.permissionLevel` 中设置,也可以通过 `host_permission_set` 持久覆盖。
218
+
219
+ ## 确认流程
220
+
221
+ 高风险操作会先返回:
222
+
223
+ ```json
224
+ {
225
+ "confirmationRequired": true,
226
+ "token": "4f2d...",
227
+ "risk": "high",
228
+ "operation": "exec",
229
+ "preview": {
230
+ "command": "hostname"
231
+ }
232
+ }
233
+ ```
234
+
235
+ 确认后,用同一个 tool 携带 `confirmationToken` 重试同一操作:
236
+
237
+ ```json
238
+ {
239
+ "hostId": "prod-api",
240
+ "command": "hostname",
241
+ "confirmationToken": "4f2d..."
242
+ }
243
+ ```
244
+
245
+ `task_batch` 被风险任务阻塞时会返回 `blocked.resumeFrom`。确认后使用同一任务列表加 `startAt: blocked.resumeFrom`,可以从阻塞项继续,避免重跑已完成任务。
246
+
247
+ ## 有限交互会话边界
248
+
249
+ `session_start` 使用系统 `ssh -tt` 和 stdin/stdout 管道,不使用 `node-pty`。
250
+
251
+ 适合:
252
+
253
+ - 简单 REPL
254
+ - 一次性菜单
255
+ - 需要保持 cwd 或短期上下文的命令
256
+
257
+ 不适合:
258
+
259
+ - `vim`、`top`、`htop`、`tmux` 等全屏 TUI
260
+ - 依赖真实终端尺寸、光标控制或复杂 ANSI 刷新的程序
261
+ - 长时间无人值守会话
262
+
263
+ 会话有 TTL、idle timeout、最大会话数和 ring buffer。`session_read` 读取后会清空缓冲区。
264
+
265
+ ## 安全模型
266
+
267
+ `smooth-ssh-mcp` 是防误操作和可审计连接层,不是远端 shell 沙箱。
268
+
269
+ 默认策略包括:
270
+
271
+ - prod/high-risk 主机执行无法确认只读性的命令会要求确认。
272
+ - sudo、写操作、复杂 shell、上传、下载、端口转发会要求确认。
273
+ - 涉及 `.env`、`.ssh`、私钥、`.pem`、`.key`、`/etc/shadow` 等敏感远端路径的命令不会免确认。
274
+ - 下载敏感路径默认拒绝。
275
+ - SCP 远端路径禁止空白和 shell 元字符,避免文件传输变成命令执行通道。
276
+ - `rm -rf /`、`mkfs`、`dd of=...`、重启关机等灾难命令默认拒绝。
277
+ - 不自动输入 sudo 密码。
278
+ - 不自动修改 `known_hosts`。
279
+ - `acceptNewHostKey: true` 只允许 OpenSSH 对首次连接保存新 host key;已变更的 host key 仍由 OpenSSH 拒绝。
280
+ - 密码认证只从环境变量读取,并通过 `SSHPASS` 传给 `sshpass -e`。
281
+
282
+ ## 审计日志
283
+
284
+ 运行 MCP server 时默认写入 JSONL 审计日志:
285
+
286
+ ```text
287
+ ~/.config/smooth-ssh-mcp/audit.jsonl
288
+ ```
289
+
290
+ 审计记录包含 tool 名称、hostId、operation、结果类型、退出码、耗时、截断状态和脱敏统计。`confirmationToken`、`env`、`stdin`、密码和 secret 字段会被写成 `[REDACTED]`。
291
+
292
+ 可通过环境变量调整:
293
+
294
+ ```bash
295
+ SMOOTH_SSH_MCP_AUDIT=0 smooth-ssh-mcp
296
+ SMOOTH_SSH_MCP_AUDIT_LOG=/path/to/audit.jsonl smooth-ssh-mcp
297
+ ```
298
+
299
+ ## 状态文件
300
+
301
+ `host_select`、`host_recent`、`host_permission_set` 会写入:
302
+
303
+ ```text
304
+ ~/.config/smooth-ssh-mcp/state.json
305
+ ```
306
+
307
+ 状态文件只保存 hostId、时间戳、使用次数、使用原因和 permission level,不保存密码、私钥路径、sessionId 或 shell 状态。
308
+
309
+ ## 开发
310
+
311
+ ```bash
312
+ npm test
313
+ npm run typecheck
314
+ npm run build
315
+ npm run test:cli
316
+ npm pack --dry-run
317
+ ```
318
+
319
+ GitHub Actions 会在 push 和 pull request 上运行 `npm ci`、`npm run typecheck`、`npm test`、`npm run build`、CLI smoke test 和 `npm pack --dry-run`。