ssh-mcp-pro 1.0.0
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/AGENTS.md +127 -0
- package/ARCHITECTURE.md +145 -0
- package/LICENSE +21 -0
- package/LICENSES/MIT.txt +21 -0
- package/MIGRATION.md +14 -0
- package/README.md +175 -0
- package/REGISTRY_SUBMISSION.md +38 -0
- package/SECURITY.md +40 -0
- package/SECURITY_DECISIONS.md +59 -0
- package/dist/agent-bin.d.ts +3 -0
- package/dist/agent-bin.d.ts.map +1 -0
- package/dist/agent-bin.js +8 -0
- package/dist/agent-bin.js.map +1 -0
- package/dist/audit.d.ts +25 -0
- package/dist/audit.d.ts.map +1 -0
- package/dist/audit.js +50 -0
- package/dist/audit.js.map +1 -0
- package/dist/auth.d.ts +4 -0
- package/dist/auth.d.ts.map +1 -0
- package/dist/auth.js +33 -0
- package/dist/auth.js.map +1 -0
- package/dist/cli.d.ts +16 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +99 -0
- package/dist/cli.js.map +1 -0
- package/dist/config.d.ts +103 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +490 -0
- package/dist/config.js.map +1 -0
- package/dist/connector-credentials.d.ts +8 -0
- package/dist/connector-credentials.d.ts.map +1 -0
- package/dist/connector-credentials.js +132 -0
- package/dist/connector-credentials.js.map +1 -0
- package/dist/connector-profile.d.ts +17 -0
- package/dist/connector-profile.d.ts.map +1 -0
- package/dist/connector-profile.js +81 -0
- package/dist/connector-profile.js.map +1 -0
- package/dist/container.d.ts +18 -0
- package/dist/container.d.ts.map +1 -0
- package/dist/container.js +52 -0
- package/dist/container.js.map +1 -0
- package/dist/detect.d.ts +7 -0
- package/dist/detect.d.ts.map +1 -0
- package/dist/detect.js +271 -0
- package/dist/detect.js.map +1 -0
- package/dist/ensure.d.ts +17 -0
- package/dist/ensure.d.ts.map +1 -0
- package/dist/ensure.js +531 -0
- package/dist/ensure.js.map +1 -0
- package/dist/errors.d.ts +54 -0
- package/dist/errors.d.ts.map +1 -0
- package/dist/errors.js +84 -0
- package/dist/errors.js.map +1 -0
- package/dist/fs-tools.d.ts +26 -0
- package/dist/fs-tools.d.ts.map +1 -0
- package/dist/fs-tools.js +599 -0
- package/dist/fs-tools.js.map +1 -0
- package/dist/http-rate-limit.d.ts +9 -0
- package/dist/http-rate-limit.d.ts.map +1 -0
- package/dist/http-rate-limit.js +41 -0
- package/dist/http-rate-limit.js.map +1 -0
- package/dist/http-security.d.ts +22 -0
- package/dist/http-security.d.ts.map +1 -0
- package/dist/http-security.js +88 -0
- package/dist/http-security.js.map +1 -0
- package/dist/index.d.ts +10 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +201 -0
- package/dist/index.js.map +1 -0
- package/dist/logging.d.ts +52 -0
- package/dist/logging.d.ts.map +1 -0
- package/dist/logging.js +180 -0
- package/dist/logging.js.map +1 -0
- package/dist/mcp.d.ts +16 -0
- package/dist/mcp.d.ts.map +1 -0
- package/dist/mcp.js +159 -0
- package/dist/mcp.js.map +1 -0
- package/dist/metrics.d.ts +95 -0
- package/dist/metrics.d.ts.map +1 -0
- package/dist/metrics.js +204 -0
- package/dist/metrics.js.map +1 -0
- package/dist/oauth.d.ts +14 -0
- package/dist/oauth.d.ts.map +1 -0
- package/dist/oauth.js +105 -0
- package/dist/oauth.js.map +1 -0
- package/dist/policy.d.ts +64 -0
- package/dist/policy.d.ts.map +1 -0
- package/dist/policy.js +368 -0
- package/dist/policy.js.map +1 -0
- package/dist/process.d.ts +24 -0
- package/dist/process.d.ts.map +1 -0
- package/dist/process.js +212 -0
- package/dist/process.js.map +1 -0
- package/dist/prompts.d.ts +49 -0
- package/dist/prompts.d.ts.map +1 -0
- package/dist/prompts.js +191 -0
- package/dist/prompts.js.map +1 -0
- package/dist/rate-limiter.d.ts +57 -0
- package/dist/rate-limiter.d.ts.map +1 -0
- package/dist/rate-limiter.js +141 -0
- package/dist/rate-limiter.js.map +1 -0
- package/dist/remote/agent-cli.d.ts +2 -0
- package/dist/remote/agent-cli.d.ts.map +1 -0
- package/dist/remote/agent-cli.js +270 -0
- package/dist/remote/agent-cli.js.map +1 -0
- package/dist/remote/agent-executor.d.ts +26 -0
- package/dist/remote/agent-executor.d.ts.map +1 -0
- package/dist/remote/agent-executor.js +400 -0
- package/dist/remote/agent-executor.js.map +1 -0
- package/dist/remote/config.d.ts +3 -0
- package/dist/remote/config.d.ts.map +1 -0
- package/dist/remote/config.js +52 -0
- package/dist/remote/config.js.map +1 -0
- package/dist/remote/control-plane.d.ts +57 -0
- package/dist/remote/control-plane.d.ts.map +1 -0
- package/dist/remote/control-plane.js +1248 -0
- package/dist/remote/control-plane.js.map +1 -0
- package/dist/remote/crypto.d.ts +38 -0
- package/dist/remote/crypto.d.ts.map +1 -0
- package/dist/remote/crypto.js +143 -0
- package/dist/remote/crypto.js.map +1 -0
- package/dist/remote/mcp-tools.d.ts +10 -0
- package/dist/remote/mcp-tools.d.ts.map +1 -0
- package/dist/remote/mcp-tools.js +201 -0
- package/dist/remote/mcp-tools.js.map +1 -0
- package/dist/remote/policy.d.ts +11 -0
- package/dist/remote/policy.d.ts.map +1 -0
- package/dist/remote/policy.js +94 -0
- package/dist/remote/policy.js.map +1 -0
- package/dist/remote/schemas.d.ts +298 -0
- package/dist/remote/schemas.d.ts.map +1 -0
- package/dist/remote/schemas.js +111 -0
- package/dist/remote/schemas.js.map +1 -0
- package/dist/remote/scopes.d.ts +6 -0
- package/dist/remote/scopes.d.ts.map +1 -0
- package/dist/remote/scopes.js +24 -0
- package/dist/remote/scopes.js.map +1 -0
- package/dist/remote/store.d.ts +45 -0
- package/dist/remote/store.d.ts.map +1 -0
- package/dist/remote/store.js +355 -0
- package/dist/remote/store.js.map +1 -0
- package/dist/remote/types.d.ts +183 -0
- package/dist/remote/types.d.ts.map +1 -0
- package/dist/remote/types.js +103 -0
- package/dist/remote/types.js.map +1 -0
- package/dist/remote/util.d.ts +6 -0
- package/dist/remote/util.d.ts.map +1 -0
- package/dist/remote/util.js +45 -0
- package/dist/remote/util.js.map +1 -0
- package/dist/remote/websocket.d.ts +26 -0
- package/dist/remote/websocket.d.ts.map +1 -0
- package/dist/remote/websocket.js +167 -0
- package/dist/remote/websocket.js.map +1 -0
- package/dist/render-http.d.ts +2 -0
- package/dist/render-http.d.ts.map +1 -0
- package/dist/render-http.js +14 -0
- package/dist/render-http.js.map +1 -0
- package/dist/resources.d.ts +19 -0
- package/dist/resources.d.ts.map +1 -0
- package/dist/resources.js +96 -0
- package/dist/resources.js.map +1 -0
- package/dist/retry.d.ts +45 -0
- package/dist/retry.d.ts.map +1 -0
- package/dist/retry.js +120 -0
- package/dist/retry.js.map +1 -0
- package/dist/safety.d.ts +31 -0
- package/dist/safety.d.ts.map +1 -0
- package/dist/safety.js +174 -0
- package/dist/safety.js.map +1 -0
- package/dist/server-http.d.ts +2 -0
- package/dist/server-http.d.ts.map +1 -0
- package/dist/server-http.js +432 -0
- package/dist/server-http.js.map +1 -0
- package/dist/session.d.ts +116 -0
- package/dist/session.d.ts.map +1 -0
- package/dist/session.js +666 -0
- package/dist/session.js.map +1 -0
- package/dist/shell.d.ts +10 -0
- package/dist/shell.d.ts.map +1 -0
- package/dist/shell.js +83 -0
- package/dist/shell.js.map +1 -0
- package/dist/ssh-config.d.ts +94 -0
- package/dist/ssh-config.d.ts.map +1 -0
- package/dist/ssh-config.js +234 -0
- package/dist/ssh-config.js.map +1 -0
- package/dist/streaming.d.ts +36 -0
- package/dist/streaming.d.ts.map +1 -0
- package/dist/streaming.js +140 -0
- package/dist/streaming.js.map +1 -0
- package/dist/telemetry.d.ts +17 -0
- package/dist/telemetry.d.ts.map +1 -0
- package/dist/telemetry.js +101 -0
- package/dist/telemetry.js.map +1 -0
- package/dist/tools/connector.provider.d.ts +28 -0
- package/dist/tools/connector.provider.d.ts.map +1 -0
- package/dist/tools/connector.provider.js +360 -0
- package/dist/tools/connector.provider.js.map +1 -0
- package/dist/tools/ensure.provider.d.ts +18 -0
- package/dist/tools/ensure.provider.d.ts.map +1 -0
- package/dist/tools/ensure.provider.js +173 -0
- package/dist/tools/ensure.provider.js.map +1 -0
- package/dist/tools/fs.provider.d.ts +21 -0
- package/dist/tools/fs.provider.d.ts.map +1 -0
- package/dist/tools/fs.provider.js +259 -0
- package/dist/tools/fs.provider.js.map +1 -0
- package/dist/tools/index.d.ts +4 -0
- package/dist/tools/index.d.ts.map +1 -0
- package/dist/tools/index.js +68 -0
- package/dist/tools/index.js.map +1 -0
- package/dist/tools/metadata.d.ts +11 -0
- package/dist/tools/metadata.d.ts.map +1 -0
- package/dist/tools/metadata.js +10 -0
- package/dist/tools/metadata.js.map +1 -0
- package/dist/tools/output-schemas.d.ts +217 -0
- package/dist/tools/output-schemas.d.ts.map +1 -0
- package/dist/tools/output-schemas.js +300 -0
- package/dist/tools/output-schemas.js.map +1 -0
- package/dist/tools/process.provider.d.ts +22 -0
- package/dist/tools/process.provider.d.ts.map +1 -0
- package/dist/tools/process.provider.js +146 -0
- package/dist/tools/process.provider.js.map +1 -0
- package/dist/tools/registry.d.ts +12 -0
- package/dist/tools/registry.d.ts.map +1 -0
- package/dist/tools/registry.js +163 -0
- package/dist/tools/registry.js.map +1 -0
- package/dist/tools/results.d.ts +4 -0
- package/dist/tools/results.d.ts.map +1 -0
- package/dist/tools/results.js +5 -0
- package/dist/tools/results.js.map +1 -0
- package/dist/tools/session.provider.d.ts +23 -0
- package/dist/tools/session.provider.d.ts.map +1 -0
- package/dist/tools/session.provider.js +299 -0
- package/dist/tools/session.provider.js.map +1 -0
- package/dist/tools/system.provider.d.ts +18 -0
- package/dist/tools/system.provider.d.ts.map +1 -0
- package/dist/tools/system.provider.js +81 -0
- package/dist/tools/system.provider.js.map +1 -0
- package/dist/tools/transfer.provider.d.ts +16 -0
- package/dist/tools/transfer.provider.d.ts.map +1 -0
- package/dist/tools/transfer.provider.js +85 -0
- package/dist/tools/transfer.provider.js.map +1 -0
- package/dist/tools/tunnel.provider.d.ts +18 -0
- package/dist/tools/tunnel.provider.d.ts.map +1 -0
- package/dist/tools/tunnel.provider.js +142 -0
- package/dist/tools/tunnel.provider.js.map +1 -0
- package/dist/tools/types.d.ts +16 -0
- package/dist/tools/types.d.ts.map +1 -0
- package/dist/tools/types.js +2 -0
- package/dist/tools/types.js.map +1 -0
- package/dist/transfer.d.ts +40 -0
- package/dist/transfer.d.ts.map +1 -0
- package/dist/transfer.js +363 -0
- package/dist/transfer.js.map +1 -0
- package/dist/tunnel.d.ts +37 -0
- package/dist/tunnel.d.ts.map +1 -0
- package/dist/tunnel.js +234 -0
- package/dist/tunnel.js.map +1 -0
- package/dist/types.d.ts +341 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +184 -0
- package/dist/types.js.map +1 -0
- package/docs/docker.md +22 -0
- package/examples/README.md +77 -0
- package/mcp.json +21 -0
- package/package.json +147 -0
- package/registry/ssh-mcp-pro/mcp.json +21 -0
- package/server.json +76 -0
package/AGENTS.md
ADDED
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
# AGENTS.md - ssh-mcp-pro
|
|
2
|
+
|
|
3
|
+
Guidance for AI agents using `ssh-mcp-pro` v2.
|
|
4
|
+
|
|
5
|
+
## Quick Start
|
|
6
|
+
|
|
7
|
+
```json
|
|
8
|
+
{
|
|
9
|
+
"name": "ssh-mcp-pro",
|
|
10
|
+
"command": "ssh-mcp-pro",
|
|
11
|
+
"type": "stdio"
|
|
12
|
+
}
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
## Secure Defaults
|
|
16
|
+
|
|
17
|
+
- Host-key verification is strict by default.
|
|
18
|
+
- Root SSH login is denied unless policy allows it.
|
|
19
|
+
- Raw `proc_sudo` is denied unless policy allows it.
|
|
20
|
+
- Destructive commands and filesystem operations are policy-controlled.
|
|
21
|
+
- Use `policyMode: "explain"` before mutations when you need a plan or user confirmation.
|
|
22
|
+
|
|
23
|
+
## Recommended Workflow
|
|
24
|
+
|
|
25
|
+
1. `ssh_list_configured_hosts` to discover aliases when useful.
|
|
26
|
+
2. `ssh_open_session` with `hostKeyPolicy: "strict"` or `expectedHostKeySha256`.
|
|
27
|
+
3. `os_detect` to learn platform capabilities.
|
|
28
|
+
4. Read `ssh-mcp-pro://policy/effective` before privileged or destructive work.
|
|
29
|
+
5. Use task tools: `fs_*`, `proc_exec`, `ensure_*`, `file_*`, `tunnel_*`.
|
|
30
|
+
6. `ssh_close_session` when work is complete.
|
|
31
|
+
|
|
32
|
+
## Explain Mode
|
|
33
|
+
|
|
34
|
+
Use explain mode when a request may mutate a remote host, change files, start or
|
|
35
|
+
stop services, install packages, open tunnels, or require user approval before
|
|
36
|
+
execution. Explain mode is a planning and policy-preview path; it is not a
|
|
37
|
+
permission bypass and it does not execute the requested mutation.
|
|
38
|
+
|
|
39
|
+
Use `policyMode: "explain"` when you need a non-mutating preview from normal SSH
|
|
40
|
+
tools. For example, `ssh_open_session` with `policyMode: "explain"` returns a
|
|
41
|
+
connection plan with `wouldConnect` instead of opening a live SSH connection.
|
|
42
|
+
Write, transfer, process, ensure, and tunnel tools that honor the session policy
|
|
43
|
+
mode return explain-only plans instead of performing the change.
|
|
44
|
+
|
|
45
|
+
Use `policyMode: "enforce"` only after the plan has been reviewed, the target
|
|
46
|
+
host and path are correct, strict host-key verification is active, and the
|
|
47
|
+
effective policy allows the concrete operation. Enforce mode is the default mode
|
|
48
|
+
for actual work.
|
|
49
|
+
|
|
50
|
+
Connector clients can use `ssh_policy_explain` before opening sessions or
|
|
51
|
+
running task tools. It evaluates an optional `hostAlias`, action class, command,
|
|
52
|
+
and path against policy, returns `executed: false`, includes the policy
|
|
53
|
+
decision, and marks non-inspection requests as requiring explicit user
|
|
54
|
+
confirmation.
|
|
55
|
+
|
|
56
|
+
Connector clients can use `ssh_mutation_plan` for remote changes that should be
|
|
57
|
+
planned without execution. It accepts `hostAlias`, `goal`, and an optional
|
|
58
|
+
category such as `package`, `service`, `file`, `command`, `tunnel`, or `other`.
|
|
59
|
+
The result includes `executed: false`, a policy decision, prerequisites that
|
|
60
|
+
must be true before execution, and operations that remain disallowed in remote
|
|
61
|
+
connector profiles.
|
|
62
|
+
|
|
63
|
+
Use the `plan-mutation` prompt when the user asks for a risky remote change and
|
|
64
|
+
you need the agent to produce a concise, reviewable plan first. The prompt
|
|
65
|
+
directs the agent to use explain mode or policy resources and to call out sudo
|
|
66
|
+
needs, destructive operations, path policy, rollback, commands, and files that
|
|
67
|
+
would change.
|
|
68
|
+
|
|
69
|
+
Concrete explain-to-enforce sequence:
|
|
70
|
+
|
|
71
|
+
1. `ssh_list_configured_hosts` to choose an allowed alias.
|
|
72
|
+
2. `ssh_policy_explain` with `hostAlias`, `action: "mutation"`, and the
|
|
73
|
+
proposed command or path.
|
|
74
|
+
3. `ssh_mutation_plan` with `hostAlias`, `goal`, and the closest category.
|
|
75
|
+
4. `ssh_open_session` with `policyMode: "explain"` and strict host-key
|
|
76
|
+
verification to confirm the connection plan.
|
|
77
|
+
5. Review the policy decision, target host, target path, rollback, and exact
|
|
78
|
+
tool payload with the user or supervising workflow.
|
|
79
|
+
6. `ssh_open_session` again with `policyMode: "enforce"` only after approval.
|
|
80
|
+
7. Run the narrow task tool, such as `ensure_package`, `ensure_service`,
|
|
81
|
+
`fs_write`, `patch_apply`, or `proc_exec`.
|
|
82
|
+
8. Verify the result with read-only inspection and then `ssh_close_session`.
|
|
83
|
+
|
|
84
|
+
## Tool Guidance
|
|
85
|
+
|
|
86
|
+
| Tool | Use |
|
|
87
|
+
|------|-----|
|
|
88
|
+
| `ssh_open_session` | Open a persistent SSH connection. Reuse one session per host per task. |
|
|
89
|
+
| `proc_exec` | Run non-interactive commands. Destructive patterns may be denied. |
|
|
90
|
+
| `proc_sudo` | Raw sudo only when policy explicitly permits it. Prefer `ensure_*`. |
|
|
91
|
+
| `proc_exec_stream` | Long-running commands or output that should stream. |
|
|
92
|
+
| `fs_read` | Text-focused reads with size limits. Use `file_download` for large files. |
|
|
93
|
+
| `fs_write` | Write text data. Policy may deny protected paths. |
|
|
94
|
+
| `fs_rmrf` | Destructive delete. Use explain mode and confirm before invoking. |
|
|
95
|
+
| `file_upload` / `file_download` | SFTP transfers with checksum verification. |
|
|
96
|
+
| `ensure_package` | Idempotent package install/remove. |
|
|
97
|
+
| `ensure_service` | Idempotent service state changes where supported. |
|
|
98
|
+
| `ensure_lines_in_file` | Idempotent line management. |
|
|
99
|
+
| `patch_apply` | Apply unified diffs with dry-run behavior. |
|
|
100
|
+
| `tunnel_*` | Real SSH local/remote forwarding. Close tunnels when finished. |
|
|
101
|
+
|
|
102
|
+
## Resources
|
|
103
|
+
|
|
104
|
+
- `ssh-mcp-pro://sessions/active`
|
|
105
|
+
- `ssh-mcp-pro://metrics/json`
|
|
106
|
+
- `ssh-mcp-pro://metrics/prometheus`
|
|
107
|
+
- `ssh-mcp-pro://ssh-config/hosts`
|
|
108
|
+
- `ssh-mcp-pro://policy/effective`
|
|
109
|
+
- `ssh-mcp-pro://audit/recent`
|
|
110
|
+
- `ssh-mcp-pro://capabilities/support-matrix`
|
|
111
|
+
|
|
112
|
+
## Prompts
|
|
113
|
+
|
|
114
|
+
- `safe-connect`
|
|
115
|
+
- `inspect-host-capabilities`
|
|
116
|
+
- `plan-mutation`
|
|
117
|
+
- `managed-config-change`
|
|
118
|
+
|
|
119
|
+
## Common Mistakes
|
|
120
|
+
|
|
121
|
+
| Mistake | Fix |
|
|
122
|
+
|---------|-----|
|
|
123
|
+
| Opening a new session for every tool call | Reuse the existing `sessionId`. |
|
|
124
|
+
| Disabling host-key checks for production | Populate `known_hosts` or pin `expectedHostKeySha256`. |
|
|
125
|
+
| Using raw `proc_sudo` for package/service work | Prefer `ensure_package` or `ensure_service`. |
|
|
126
|
+
| Reading huge files with `fs_read` | Use `file_download`. |
|
|
127
|
+
| Treating BusyBox/dropbear as full Linux | Check `sftpAvailable` and support matrix first. |
|
package/ARCHITECTURE.md
ADDED
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
# Architecture
|
|
2
|
+
|
|
3
|
+
ssh-mcp-pro is organized around a small dependency-injected application container, an MCP server boundary, policy-aware tool providers, and SSH-backed services.
|
|
4
|
+
|
|
5
|
+
## System Diagram
|
|
6
|
+
|
|
7
|
+
```mermaid
|
|
8
|
+
flowchart LR
|
|
9
|
+
Client[MCP client] --> Transport[stdio or Streamable HTTP transport]
|
|
10
|
+
Transport --> Server[SSHMCPServer]
|
|
11
|
+
Server --> Registry[ToolRegistry]
|
|
12
|
+
Registry --> Providers[Tool providers]
|
|
13
|
+
Providers --> Services[Session, process, filesystem, transfer, ensure, tunnel services]
|
|
14
|
+
Services --> SessionManager[SessionManager]
|
|
15
|
+
SessionManager --> SSH[node-ssh / ssh2]
|
|
16
|
+
Providers --> Policy[PolicyEngine.check]
|
|
17
|
+
Policy --> Audit[AuditLog]
|
|
18
|
+
Policy --> Metrics[MetricsCollector]
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
Core request flow:
|
|
22
|
+
|
|
23
|
+
1. `SSHMCPServer` registers MCP tool, resource, and prompt handlers.
|
|
24
|
+
2. `ToolRegistry` resolves aliases and filters tools by connector profile.
|
|
25
|
+
3. Tool providers validate arguments and call services.
|
|
26
|
+
4. Services ask `PolicyEngine` before privileged or destructive work.
|
|
27
|
+
5. Policy decisions are recorded through `AuditLog` and `MetricsCollector`.
|
|
28
|
+
6. `SessionManager` owns SSH connection lifecycle and delegates to `node-ssh`, which uses `ssh2` underneath.
|
|
29
|
+
|
|
30
|
+
## Dependency Injection
|
|
31
|
+
|
|
32
|
+
`AppContainer` is the runtime dependency graph. It contains `ConfigManager`, `RateLimiter`, `MetricsCollector`, `AuditLog`, `PolicyEngine`, `SessionManager`, and `TunnelService`.
|
|
33
|
+
|
|
34
|
+
`createContainer()` builds the production graph from environment-backed `ConfigManager` settings. It enables the normal rate limiter behavior and wires session-close cleanup to tunnel cleanup.
|
|
35
|
+
|
|
36
|
+
`createTestContainer()` builds the same shape for tests while allowing targeted overrides. It disables blocking rate-limit behavior and uses shorter session defaults so unit tests can replace only the dependency they are exercising.
|
|
37
|
+
|
|
38
|
+
## MCP Server And Tool Registry
|
|
39
|
+
|
|
40
|
+
`SSHMCPServer` wraps the MCP SDK `Server`. It exposes tools, resources, and prompts over stdio by default and can be connected to another transport through `connect()`.
|
|
41
|
+
|
|
42
|
+
Tool calls pass through a global sliding-window rate limit first. Calls whose arguments include a top-level `sessionId` then pass through a second configurable `session:<id>` window so one busy SSH session cannot exhaust the entire server budget.
|
|
43
|
+
|
|
44
|
+
Streamable HTTP responses also include `X-RateLimit-Limit`, `X-RateLimit-Remaining`, and `X-RateLimit-Reset` headers derived from the current global limiter usage so HTTP clients can observe budget state before a hard rate-limit error.
|
|
45
|
+
|
|
46
|
+
`ToolRegistry` owns provider registration and dispatch. It supports compatibility aliases such as `ssh.openSession` -> `ssh_open_session`, filters tool exposure through the configured tool profile, and converts thrown project errors into typed `ToolErrorResponse` MCP error results with `structuredContent.error`, `code`, and `message`. Successful tool handlers must return either a JSON object or an explicit MCP `CallToolResult` with non-null `structuredContent`; every listed tool has a concrete `outputSchema` that describes the successful structured response shape exposed to clients.
|
|
47
|
+
|
|
48
|
+
## Policy Engine
|
|
49
|
+
|
|
50
|
+
The policy path is:
|
|
51
|
+
|
|
52
|
+
```text
|
|
53
|
+
MCP tool call -> provider argument validation -> PolicyEngine.check() -> allow, deny, or explain -> AuditLog record -> service action
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
Default policy denies root login, raw sudo, destructive commands, and destructive filesystem operations. In `explain` mode, providers can surface the policy decision without mutating remote state.
|
|
57
|
+
|
|
58
|
+
## Remote Control Plane
|
|
59
|
+
|
|
60
|
+
The remote control plane provides Streamable HTTP MCP access and a no-custody outbound agent model.
|
|
61
|
+
|
|
62
|
+
- `server-http.ts` hosts the HTTP MCP endpoint and enforces bearer or OAuth authorization.
|
|
63
|
+
- `remote/control-plane.ts` implements control-plane HTTP routes, OAuth 2.0 PKCE, GitHub identity exchange, protected resource metadata, agent enrollment, and WebSocket agent communication.
|
|
64
|
+
- `remote/websocket.ts` handles the persistent outbound agent channel.
|
|
65
|
+
- `RemoteStore` persists users, OAuth clients, authorization codes, remote agents, enrollment tokens, and audit events in SQLite.
|
|
66
|
+
- `remote/mcp-tools.ts` exposes administrative remote-agent tools behind control-plane scopes.
|
|
67
|
+
|
|
68
|
+
OAuth uses PKCE for browser-based authorization and validates access tokens before protected MCP operations. Enrollment tokens are one-time secrets stored only as hashes.
|
|
69
|
+
|
|
70
|
+
## Persistence
|
|
71
|
+
|
|
72
|
+
`RemoteStore` currently uses `node:sqlite` through `DatabaseSync`. The project pins Node.js versions that include this module. In Node 24.15.0, `node:sqlite` is still a Stability 1.2 release-candidate API, so the contingency path is to introduce `better-sqlite3` 11.x as a maintained native fallback before widening the runtime matrix or if `node:sqlite` becomes unavailable.
|
|
73
|
+
|
|
74
|
+
## ADR-001: Use `node:sqlite` For RemoteStore
|
|
75
|
+
|
|
76
|
+
Status: accepted.
|
|
77
|
+
|
|
78
|
+
Context: The remote control plane needs a small local database for OAuth clients, authorization codes, users, agents, enrollment tokens, and audit events.
|
|
79
|
+
|
|
80
|
+
Decision: Use `node:sqlite` instead of adding `better-sqlite3` immediately.
|
|
81
|
+
|
|
82
|
+
Rationale:
|
|
83
|
+
|
|
84
|
+
- Avoids native bindings in the default install path.
|
|
85
|
+
- Keeps the package installable through the pinned Node.js runtime without postinstall compilation.
|
|
86
|
+
- Fits the local control-plane storage model.
|
|
87
|
+
- The release-candidate status is acceptable because `engines.node` pins versions known to expose `node:sqlite`.
|
|
88
|
+
|
|
89
|
+
Consequence: If Node removes or materially changes `node:sqlite`, the fallback is `better-sqlite3` 11.x with an explicit migration issue and CI coverage on the affected Node versions.
|
|
90
|
+
|
|
91
|
+
## ADR-002: Use An In-Process Rate Limiter
|
|
92
|
+
|
|
93
|
+
Status: accepted.
|
|
94
|
+
|
|
95
|
+
Context: stdio deployments should not require Redis or another external service.
|
|
96
|
+
|
|
97
|
+
Decision: Use the in-process `RateLimiter` sliding-window log.
|
|
98
|
+
|
|
99
|
+
Rationale:
|
|
100
|
+
|
|
101
|
+
- Keeps local stdio usage dependency-free.
|
|
102
|
+
- Gives deterministic unit-test behavior.
|
|
103
|
+
- Limits accidental global and per-session tool-call bursts without requiring network infrastructure.
|
|
104
|
+
|
|
105
|
+
Consequence: Multi-instance HTTP deployments need an external rate-limiting layer at the reverse proxy or platform edge if they require global limits.
|
|
106
|
+
|
|
107
|
+
## ADR-003: `better-sqlite3` Fallback For RemoteStore
|
|
108
|
+
|
|
109
|
+
Status: contingency.
|
|
110
|
+
|
|
111
|
+
Context: `node:sqlite` is the default storage adapter. Startup now validates that the current Node.js build exposes a constructable `DatabaseSync` before opening the control-plane database. If that check fails on a supported deployment target, the maintained native fallback is `better-sqlite3`.
|
|
112
|
+
|
|
113
|
+
Decision: Keep `node:sqlite` as the default and document a direct fallback patch that can be applied if a pinned Node.js build removes or materially changes the API.
|
|
114
|
+
|
|
115
|
+
Fallback steps:
|
|
116
|
+
|
|
117
|
+
1. Install the fallback dependency:
|
|
118
|
+
|
|
119
|
+
```bash
|
|
120
|
+
pnpm add better-sqlite3@^11.0.0
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
2. Replace the `createRequire`-based `node:sqlite` loader in `src/remote/store.ts` with:
|
|
124
|
+
|
|
125
|
+
```typescript
|
|
126
|
+
import Database from "better-sqlite3";
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
3. Replace the `DatabaseSync` constructor call with:
|
|
130
|
+
|
|
131
|
+
```typescript
|
|
132
|
+
this.db = new Database(filePath);
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
4. Keep the existing `prepare().get()`, `prepare().run()`, and `exec()` call sites. The `better-sqlite3` synchronous API supports the same access pattern used by `RemoteStore`.
|
|
136
|
+
|
|
137
|
+
5. Run the full verification chain on the affected Node.js versions before widening the runtime matrix:
|
|
138
|
+
|
|
139
|
+
```bash
|
|
140
|
+
pnpm run typecheck
|
|
141
|
+
pnpm test
|
|
142
|
+
pnpm run check
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
Consequence: The default install remains native-dependency-free. The fallback path is documented with copy-paste steps, but adopting it must be accompanied by a dependency review, lockfile update, CI coverage, and release notes.
|
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 Osman Aslan (oaslananka)
|
|
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/LICENSES/MIT.txt
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 Osman Aslan (oaslananka)
|
|
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/MIGRATION.md
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
# Migration Guide
|
|
2
|
+
|
|
3
|
+
## 1.x
|
|
4
|
+
|
|
5
|
+
Version `1.0.0` is the initial public baseline for this repository. There are no earlier published package versions with a supported automated migration path.
|
|
6
|
+
|
|
7
|
+
When upgrading within `1.x`, preserve these compatibility expectations:
|
|
8
|
+
|
|
9
|
+
- Node.js must satisfy `^22.22.2 || ^24.15.0`.
|
|
10
|
+
- pnpm must satisfy `^11.0.9`.
|
|
11
|
+
- Existing stdio MCP client configs can continue to launch `ssh-mcp-pro` with no arguments.
|
|
12
|
+
- HTTP deployments should keep explicit authentication, allowed origins, host allowlists, strict host-key verification, and a remote-safe tool profile.
|
|
13
|
+
|
|
14
|
+
Future breaking changes should add a new section with required config, policy, or data migration steps before release.
|
package/README.md
ADDED
|
@@ -0,0 +1,175 @@
|
|
|
1
|
+
[](https://www.npmjs.com/package/ssh-mcp-pro)
|
|
2
|
+
[](LICENSE)
|
|
3
|
+
[](https://github.com/oaslananka/ssh-mcp-pro/actions/workflows/ci.yml)
|
|
4
|
+
|
|
5
|
+
# ssh-mcp-pro
|
|
6
|
+
|
|
7
|
+
ssh-mcp-pro is a secure Model Context Protocol (MCP) server for SSH automation. It lets MCP-capable clients open SSH sessions, inspect hosts, run guarded commands, manage files, transfer artifacts, create tunnels, and perform idempotent package or service work through policy-controlled tools.
|
|
8
|
+
|
|
9
|
+
## Prerequisites
|
|
10
|
+
|
|
11
|
+
- Node.js `>=22.22.2` or `>=24.15.0`
|
|
12
|
+
- pnpm `>=11.0.9`
|
|
13
|
+
- SSH access to the target hosts
|
|
14
|
+
- Docker, only for local integration tests and container image builds
|
|
15
|
+
|
|
16
|
+
## Installation
|
|
17
|
+
|
|
18
|
+
Install globally with pnpm:
|
|
19
|
+
|
|
20
|
+
```bash
|
|
21
|
+
pnpm add --global ssh-mcp-pro
|
|
22
|
+
ssh-mcp-pro --version
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
Run without a global install:
|
|
26
|
+
|
|
27
|
+
```bash
|
|
28
|
+
npx ssh-mcp-pro
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
For pnpm-only environments, use:
|
|
32
|
+
|
|
33
|
+
```bash
|
|
34
|
+
pnpm dlx ssh-mcp-pro
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
## Quickstart
|
|
38
|
+
|
|
39
|
+
Generic stdio MCP config:
|
|
40
|
+
|
|
41
|
+
```json
|
|
42
|
+
{
|
|
43
|
+
"name": "ssh-mcp-pro",
|
|
44
|
+
"command": "ssh-mcp-pro",
|
|
45
|
+
"type": "stdio"
|
|
46
|
+
}
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
VS Code settings style:
|
|
50
|
+
|
|
51
|
+
```json
|
|
52
|
+
{
|
|
53
|
+
"mcp.servers": {
|
|
54
|
+
"ssh-mcp-pro": {
|
|
55
|
+
"type": "stdio",
|
|
56
|
+
"command": "ssh-mcp-pro",
|
|
57
|
+
"args": []
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
Claude Desktop style:
|
|
64
|
+
|
|
65
|
+
```json
|
|
66
|
+
{
|
|
67
|
+
"mcpServers": {
|
|
68
|
+
"ssh-mcp-pro": {
|
|
69
|
+
"command": "ssh-mcp-pro",
|
|
70
|
+
"args": []
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
After registration, start with discovery and a strict host-key policy:
|
|
77
|
+
|
|
78
|
+
```text
|
|
79
|
+
List configured SSH hosts, open a session to bastion.example.com as deploy with hostKeyPolicy=strict, then run os_detect.
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
## Configuration
|
|
83
|
+
|
|
84
|
+
All `SSH_MCP_*` environment variables parsed by `src/config.ts` are listed below. Comma-separated settings also accept newline-separated values.
|
|
85
|
+
|
|
86
|
+
| Variable | Default | Purpose |
|
|
87
|
+
| --- | --- | --- |
|
|
88
|
+
| `SSH_MCP_MAX_SESSIONS` | `20` | Maximum concurrent SSH sessions. |
|
|
89
|
+
| `SSH_MCP_SESSION_TTL` | `900000` | Session time-to-live in milliseconds. |
|
|
90
|
+
| `SSH_MCP_COMMAND_TIMEOUT` | `30000` | Default remote command timeout in milliseconds. |
|
|
91
|
+
| `SSH_MCP_MAX_COMMAND_OUTPUT_BYTES` | `1048576` | Maximum buffered stdout/stderr bytes per command result. |
|
|
92
|
+
| `SSH_MCP_MAX_STREAM_CHUNKS` | `4096` | Maximum retained streaming chunks. |
|
|
93
|
+
| `SSH_MCP_MAX_FILE_SIZE` | `10485760` | Maximum bytes returned by text-focused file reads. |
|
|
94
|
+
| `SSH_MCP_MAX_FILE_WRITE_BYTES` | `10485760` | Maximum accepted write payload before buffering. |
|
|
95
|
+
| `SSH_MCP_MAX_TRANSFER_BYTES` | `52428800` | Maximum upload or download transfer size. |
|
|
96
|
+
| `SSH_MCP_DEBUG` | `false` | Enables debug-oriented configuration behavior. |
|
|
97
|
+
| `SSH_MCP_RATE_LIMIT` | `true` | Enables the global MCP request rate limiter. |
|
|
98
|
+
| `SSH_MCP_RATE_LIMIT_MAX` | `100` | Maximum requests per rate-limit window. |
|
|
99
|
+
| `SSH_MCP_RATE_LIMIT_PER_SESSION` | `true` | Enables per-session MCP request rate limiting when tool arguments include `sessionId`. |
|
|
100
|
+
| `SSH_MCP_RATE_LIMIT_PER_SESSION_MAX` | `50` | Maximum requests per SSH session per rate-limit window. |
|
|
101
|
+
| `SSH_MCP_RATE_LIMIT_PER_SESSION_WINDOW_MS` | `60000` | Per-session rate-limit window in milliseconds. |
|
|
102
|
+
| `SSH_MCP_RATE_LIMIT_WINDOW_MS` | `60000` | Rate-limit window in milliseconds. |
|
|
103
|
+
| `SSH_MCP_STRICT_HOST_KEY` | unset | Legacy boolean alias for strict vs insecure host-key checking. |
|
|
104
|
+
| `SSH_MCP_HOST_KEY_POLICY` | `strict` | Host-key mode: `strict`, `accept-new`, or `insecure`. |
|
|
105
|
+
| `SSH_MCP_KNOWN_HOSTS_PATH` | `~/.ssh/known_hosts` | Known hosts file used for strict host-key verification. |
|
|
106
|
+
| `SSH_MCP_ALLOW_ROOT_LOGIN` | `false` | Allows SSH login as root and mirrors into policy. |
|
|
107
|
+
| `SSH_MCP_ALLOWED_CIPHERS` | empty | Optional SSH cipher allowlist. |
|
|
108
|
+
| `SSH_MCP_POLICY_FILE` | unset | JSON file containing partial policy overrides. |
|
|
109
|
+
| `SSH_MCP_POLICY_MODE` | `enforce` | Policy decision mode: `enforce` or `explain`. |
|
|
110
|
+
| `SSH_MCP_ALLOW_RAW_SUDO` | `false` | Allows raw `proc_sudo`; prefer `ensure_*` tools. |
|
|
111
|
+
| `SSH_MCP_ALLOW_DESTRUCTIVE_COMMANDS` | `false` | Allows commands matching destructive command policy. |
|
|
112
|
+
| `SSH_MCP_ALLOW_DESTRUCTIVE_FS` | `false` | Allows destructive filesystem operations such as `fs_rmrf`. |
|
|
113
|
+
| `SSH_MCP_ALLOWED_HOSTS` | empty | Host allowlist for policy and remote connector safety checks. |
|
|
114
|
+
| `SSH_MCP_COMMAND_ALLOW` | empty | Command allow patterns. |
|
|
115
|
+
| `SSH_MCP_COMMAND_DENY` | empty | Command deny patterns. |
|
|
116
|
+
| `SSH_MCP_PATH_ALLOW_PREFIXES` | `/tmp,/var/tmp,/home,/Users` | Remote path prefixes allowed by filesystem policy. |
|
|
117
|
+
| `SSH_MCP_PATH_DENY_PREFIXES` | `/etc/sudoers,/etc/shadow,/etc/passwd,/boot,/dev,/proc` | Remote path prefixes denied by filesystem policy. |
|
|
118
|
+
| `SSH_MCP_LOCAL_PATH_ALLOW_PREFIXES` | OS temp directory | Local paths allowed for transfer operations. |
|
|
119
|
+
| `SSH_MCP_LOCAL_PATH_DENY_PREFIXES` | empty | Local paths denied for transfer operations. |
|
|
120
|
+
| `SSH_MCP_TUNNEL_ALLOW_BIND_HOSTS` | `127.0.0.1,localhost,::1` | Local bind hosts allowed for tunnels. |
|
|
121
|
+
| `SSH_MCP_TUNNEL_DENY_BIND_HOSTS` | `0.0.0.0,::` | Local bind hosts denied for tunnels. |
|
|
122
|
+
| `SSH_MCP_TUNNEL_ALLOW_REMOTE_HOSTS` | empty | Optional remote tunnel target host allowlist. |
|
|
123
|
+
| `SSH_MCP_TUNNEL_DENY_REMOTE_HOSTS` | empty | Optional remote tunnel target host denylist. |
|
|
124
|
+
| `SSH_MCP_TUNNEL_ALLOW_PORTS` | empty | Optional tunnel port allowlist. |
|
|
125
|
+
| `SSH_MCP_TUNNEL_DENY_PORTS` | empty | Optional tunnel port denylist. |
|
|
126
|
+
| `SSH_MCP_HTTP_HOST` | `127.0.0.1` | Streamable HTTP bind host. |
|
|
127
|
+
| `SSH_MCP_HTTP_PORT` | `3000` | Streamable HTTP bind port. |
|
|
128
|
+
| `SSH_MCP_HTTP_ALLOWED_ORIGINS` | `http://127.0.0.1,http://localhost` | Browser origins allowed for HTTP clients. |
|
|
129
|
+
| `SSH_MCP_HTTP_BEARER_TOKEN_FILE` | unset | Bearer token file for HTTP transport. Required for non-loopback bearer deployments. |
|
|
130
|
+
| `SSH_MCP_ENABLE_LEGACY_SSE` | `false` | Enables legacy SSE compatibility. |
|
|
131
|
+
| `SSH_MCP_HTTP_MAX_REQUEST_BODY_BYTES` | `1048576` | Maximum HTTP request body size. |
|
|
132
|
+
| `SSH_MCP_HTTP_MAX_SESSIONS` | `20` | Maximum HTTP MCP sessions. |
|
|
133
|
+
| `SSH_MCP_HTTP_SESSION_IDLE_TTL_MS` | `900000` | HTTP MCP session idle timeout in milliseconds. |
|
|
134
|
+
| `SSH_MCP_HTTP_PUBLIC_URL` | unset | Stable public HTTPS MCP URL for protected resource metadata. |
|
|
135
|
+
| `SSH_MCP_HTTP_TRUST_PROXY` | `false` | Trust reverse proxy forwarded headers. |
|
|
136
|
+
| `SSH_MCP_TOOL_PROFILE` | `full` | Active tool exposure profile. |
|
|
137
|
+
| `SSH_MCP_CONNECTOR_PROFILE` | `full` | Alias for `SSH_MCP_TOOL_PROFILE`. |
|
|
138
|
+
| `SSH_MCP_CONNECTOR_CREDENTIAL_PROVIDER` | `none` | Credential provider: `none`, `agent`, or `command`. |
|
|
139
|
+
| `SSH_MCP_CONNECTOR_CREDENTIAL_COMMAND` | unset | External credential command when provider is `command`. |
|
|
140
|
+
| `SSH_MCP_CONNECTOR_CREDENTIAL_COMMAND_ARGS` | empty | Arguments passed to the external credential command. |
|
|
141
|
+
| `SSH_MCP_CONNECTOR_CREDENTIAL_COMMAND_TIMEOUT_MS` | `5000` | Credential command timeout in milliseconds. |
|
|
142
|
+
| `SSH_MCP_CONNECTOR_DEFAULT_USERNAME` | unset | Default username for connector broker flows. |
|
|
143
|
+
| `SSH_MCP_HTTP_AUTH_MODE` | `bearer` | HTTP auth mode: `bearer` or `oauth`. |
|
|
144
|
+
| `SSH_MCP_OAUTH_ISSUER` | unset | Expected OAuth issuer. |
|
|
145
|
+
| `SSH_MCP_OAUTH_AUDIENCE` | unset | Expected OAuth audience. |
|
|
146
|
+
| `SSH_MCP_OAUTH_JWKS_URL` | unset | OAuth JWKS URL. |
|
|
147
|
+
| `SSH_MCP_OAUTH_RESOURCE` | unset | OAuth protected resource identifier. |
|
|
148
|
+
| `SSH_MCP_OAUTH_REQUIRED_SCOPES` | `ssh-mcp-pro.read` | Required OAuth scopes. |
|
|
149
|
+
|
|
150
|
+
The parser also accepts non-`SSH_MCP_*` compatibility aliases `PORT`, `KNOWN_HOSTS_PATH`, and `STRICT_HOST_KEY_CHECKING`.
|
|
151
|
+
|
|
152
|
+
## Tool Profiles
|
|
153
|
+
|
|
154
|
+
`full` exposes every registered tool, resource, and prompt. Every other profile uses an explicit per-profile allowset. `chatgpt` and `claude` currently expose the same baseline connector tools as `remote-safe`, with empty client-specific extension sets reserved for future additions.
|
|
155
|
+
|
|
156
|
+
| Profile | Exposed tools | Exposed resources | Exposed prompts |
|
|
157
|
+
| --- | --- | --- | --- |
|
|
158
|
+
| `full` | All SSH, process, filesystem, transfer, ensure, tunnel, connector, and system tools. | All runtime resources. | All prompts. |
|
|
159
|
+
| `remote-safe` | `connector_status`, `ssh_hosts_list`, `ssh_policy_explain`, `ssh_host_inspect`, `ssh_mutation_plan`. | `ssh-mcp-pro://capabilities/support-matrix`. | `inspect-host-capabilities`, `plan-mutation`. |
|
|
160
|
+
| `chatgpt` | Baseline remote connector tools plus an empty ChatGPT extension set. | Same remote connector subset as `remote-safe`. | Same remote connector subset as `remote-safe`. |
|
|
161
|
+
| `claude` | Baseline remote connector tools plus an empty Claude extension set. | Same remote connector subset as `remote-safe`. | Same remote connector subset as `remote-safe`. |
|
|
162
|
+
| `remote-readonly` | Same remote connector subset as `remote-safe`. | Same remote connector subset as `remote-safe`. | Same remote connector subset as `remote-safe`. |
|
|
163
|
+
| `remote-broker` | Same remote connector subset as `remote-safe`. | Same remote connector subset as `remote-safe`. | Same remote connector subset as `remote-safe`. |
|
|
164
|
+
|
|
165
|
+
## Security Defaults
|
|
166
|
+
|
|
167
|
+
ssh-mcp-pro starts with strict SSH host-key verification, denies root login, denies raw sudo, blocks destructive commands and filesystem operations unless policy allows them, and refuses non-loopback HTTP startup unless authentication, origins, public HTTPS URL, strict host-key verification, a remote-safe tool profile, and host allowlists are configured. See [SECURITY.md](SECURITY.md) for vulnerability reporting and [SECURITY_DECISIONS.md](SECURITY_DECISIONS.md) for the design rationale behind these defaults.
|
|
168
|
+
|
|
169
|
+
## More Documentation
|
|
170
|
+
|
|
171
|
+
- [INSTALL.md](INSTALL.md) covers full client setup and troubleshooting.
|
|
172
|
+
- [AGENTS.md](AGENTS.md) describes agent-facing operational guidance.
|
|
173
|
+
- [examples/README.md](examples/README.md) contains workflow examples.
|
|
174
|
+
- [ARCHITECTURE.md](ARCHITECTURE.md) explains the major subsystems and ADRs.
|
|
175
|
+
- [REGISTRY_SUBMISSION.md](REGISTRY_SUBMISSION.md) tracks MCP Registry submission readiness.
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
# MCP Registry Submission
|
|
2
|
+
|
|
3
|
+
Published server name:
|
|
4
|
+
|
|
5
|
+
```text
|
|
6
|
+
io.github.oaslananka/ssh-mcp-pro
|
|
7
|
+
```
|
|
8
|
+
|
|
9
|
+
Package identifier:
|
|
10
|
+
|
|
11
|
+
```text
|
|
12
|
+
ssh-mcp-pro
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
## Checklist
|
|
16
|
+
|
|
17
|
+
- [x] `server.json` uses the `io.github.oaslananka/ssh-mcp-pro` namespace.
|
|
18
|
+
- [x] `mcp.json` and `registry/ssh-mcp-pro/mcp.json` match `package.json` name, version, and entrypoint.
|
|
19
|
+
- [x] Package metadata declares stdio transport and Node runtime.
|
|
20
|
+
- [x] Package metadata declares Linux, macOS, and Windows platforms.
|
|
21
|
+
- [x] Tool, resource, and prompt capabilities are declared.
|
|
22
|
+
- [x] `validate:mcp-metadata` checks local metadata consistency without requiring build artifacts.
|
|
23
|
+
- [ ] A versioned package has been published to npm.
|
|
24
|
+
- [ ] The MCP Registry latest record resolves for `io.github.oaslananka/ssh-mcp-pro`.
|
|
25
|
+
- [ ] Registry submission has been verified after the first published release.
|
|
26
|
+
|
|
27
|
+
## Local Validation
|
|
28
|
+
|
|
29
|
+
```bash
|
|
30
|
+
pnpm run validate:mcp-metadata
|
|
31
|
+
pnpm run sync-version -- --check
|
|
32
|
+
pnpm run build
|
|
33
|
+
pnpm pack --dry-run
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
## Notes
|
|
37
|
+
|
|
38
|
+
The registry workflow validates local metadata and accepts a missing public registry record before the first release. After npm publication, the workflow should confirm that the latest registry record resolves to `io.github.oaslananka/ssh-mcp-pro`.
|
package/SECURITY.md
ADDED
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
# Security Policy
|
|
2
|
+
|
|
3
|
+
## Supported Versions
|
|
4
|
+
|
|
5
|
+
| Version | Security support |
|
|
6
|
+
| --- | --- |
|
|
7
|
+
| `1.x` | Supported. The current major version receives security fixes. |
|
|
8
|
+
| `<1.0.0` | Not supported. |
|
|
9
|
+
|
|
10
|
+
## Reporting a Vulnerability
|
|
11
|
+
|
|
12
|
+
Report suspected vulnerabilities through GitHub's private security advisory form:
|
|
13
|
+
|
|
14
|
+
https://github.com/oaslananka/ssh-mcp-pro/security/advisories/new
|
|
15
|
+
|
|
16
|
+
Do not open a public issue for credential handling bugs, authorization bypasses, command execution escapes, policy bypasses, host-key verification problems, token validation issues, or vulnerabilities that expose SSH session data.
|
|
17
|
+
|
|
18
|
+
## Response SLA
|
|
19
|
+
|
|
20
|
+
Maintainers aim to acknowledge a valid report within 7 days. The acknowledgment should confirm receipt, request any missing reproduction details, and identify the next expected update window.
|
|
21
|
+
|
|
22
|
+
## Coordinated Disclosure
|
|
23
|
+
|
|
24
|
+
Security fixes are coordinated privately until a patch is available. Public disclosure should happen after the patched release is published, release notes are available, and affected users have a practical upgrade path.
|
|
25
|
+
|
|
26
|
+
## Scope
|
|
27
|
+
|
|
28
|
+
In scope:
|
|
29
|
+
|
|
30
|
+
- Authentication or authorization bypasses in the stdio, HTTP, OAuth, or remote agent surfaces.
|
|
31
|
+
- Policy bypasses that allow denied commands, root login, raw sudo, filesystem mutation, transfers, or tunnels.
|
|
32
|
+
- Secret leakage through logs, audit records, MCP responses, package artifacts, or container images.
|
|
33
|
+
- Host-key verification failures that silently weaken strict mode.
|
|
34
|
+
- Remote control-plane vulnerabilities in enrollment, token validation, OAuth PKCE, or WebSocket agent handling.
|
|
35
|
+
|
|
36
|
+
Out of scope:
|
|
37
|
+
|
|
38
|
+
- Vulnerabilities in `node-ssh`, `ssh2`, `jose`, Node.js, OpenSSH, Docker, or operating system packages that are not exploitable through this project's exposed behavior.
|
|
39
|
+
- Findings that require disabling documented secure defaults.
|
|
40
|
+
- Denial-of-service reports based only on local resource exhaustion in a trusted development environment.
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
# Security Decisions
|
|
2
|
+
|
|
3
|
+
This document records security-relevant defaults that affect SSH sessions, remote command execution, HTTP transport, and registry readiness.
|
|
4
|
+
|
|
5
|
+
## Strict Host-Key Verification
|
|
6
|
+
|
|
7
|
+
`SSH_MCP_HOST_KEY_POLICY` defaults to `strict`, using `~/.ssh/known_hosts` unless `SSH_MCP_KNOWN_HOSTS_PATH` is set. This prevents silent trust-on-first-use in production paths.
|
|
8
|
+
|
|
9
|
+
Non-loopback HTTP startup is refused unless host-key verification remains strict. The HTTP transport can be exposed to browsers or hosted clients, so allowing `insecure` there would combine remote reachability with unverifiable SSH host identity.
|
|
10
|
+
|
|
11
|
+
## Non-Loopback HTTP Restrictions
|
|
12
|
+
|
|
13
|
+
For non-loopback HTTP bindings, startup requires:
|
|
14
|
+
|
|
15
|
+
- Bearer authentication or configured OAuth.
|
|
16
|
+
- Explicit allowed origins.
|
|
17
|
+
- A stable HTTPS public URL.
|
|
18
|
+
- A remote-safe tool profile.
|
|
19
|
+
- A non-empty host allowlist.
|
|
20
|
+
- Strict SSH host-key verification.
|
|
21
|
+
|
|
22
|
+
These checks prevent accidentally exposing the full local SSH automation surface on a public interface.
|
|
23
|
+
|
|
24
|
+
## Root Login And Raw Sudo
|
|
25
|
+
|
|
26
|
+
Root login is denied by default through both security config and policy config. Raw `proc_sudo` is denied by default because it can bypass higher-level idempotent package and service helpers.
|
|
27
|
+
|
|
28
|
+
Operators who need privileged work should prefer `ensure_package`, `ensure_service`, `ensure_lines_in_file`, or `patch_apply`, with `SSH_MCP_POLICY_MODE=explain` before mutation when reviewing the plan.
|
|
29
|
+
|
|
30
|
+
## Destructive Commands And Filesystem Operations
|
|
31
|
+
|
|
32
|
+
Destructive command execution and destructive filesystem operations are denied by default. Policy allowlists, path prefixes, and explicit destructive toggles are required before tools such as `fs_rmrf` can remove remote paths.
|
|
33
|
+
|
|
34
|
+
## Audit Redaction
|
|
35
|
+
|
|
36
|
+
`AuditLog` stores policy decisions and selected action metadata. Before retention, it calls `redactSensitiveData()` and `redactErrorMessage()` so fields matching password, private key, passphrase, sudo password, secret, token, credential, auth, API key, bearer, and PEM patterns are redacted.
|
|
37
|
+
|
|
38
|
+
## Audit Buffer Size
|
|
39
|
+
|
|
40
|
+
The in-memory audit buffer keeps 500 events by default. This bounded size avoids unbounded memory growth in stdio and local HTTP deployments while retaining recent security-relevant decisions for inspection. Deployments with compliance retention requirements should export or persist audit events; OTLP log persistence is tracked separately from this baseline.
|
|
41
|
+
|
|
42
|
+
## Token Comparison
|
|
43
|
+
|
|
44
|
+
Bearer token comparison uses SHA-256 digests and `timingSafeEqual` through `constantTimeTokenEquals()`. Remote enrollment token validation also compares fixed-length hashes with `timingSafeEqual`. This avoids leaking token equality information through variable-time string comparison.
|
|
45
|
+
|
|
46
|
+
## CodeQL Agent Bootstrap Findings
|
|
47
|
+
|
|
48
|
+
CodeQL alerts #2, #4, #5, and #6 are documented false positives for the remote agent bootstrap flow. They are tracked by CodeQL as `js/http-to-file-access` / `js/file-access-to-http` because the agent writes enrollment data to its local config file and later uses that config to connect to the control plane.
|
|
49
|
+
|
|
50
|
+
The flow is intentional:
|
|
51
|
+
|
|
52
|
+
- `ssh-mcp-pro-agent enroll` requires an explicit `--server` URL and one-time enrollment token from the operator.
|
|
53
|
+
- Enrollment writes only to `SSHAUTOMATOR_AGENT_CONFIG` or `~/.sshautomator/agent.json` with file mode `0600`.
|
|
54
|
+
- Server response fields are validated with `requireString()` and `parseAgentPolicy()` before persistence.
|
|
55
|
+
- The agent private key is generated locally and is not received from the network.
|
|
56
|
+
- `ssh-mcp-pro-agent run` connects only to the enrolled `websocketUrl` from that config and sends signed agent envelopes to the configured control plane.
|
|
57
|
+
- The private key remains local; outbound payloads contain signed metadata, policy-limited capabilities, and policy-controlled action results.
|
|
58
|
+
|
|
59
|
+
These findings are dismissed individually with a false-positive rationale. No broad CodeQL suppression is used.
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"agent-bin.d.ts","sourceRoot":"","sources":["../src/agent-bin.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { runAgentCli } from "./remote/agent-cli.js";
|
|
3
|
+
runAgentCli(process.argv.slice(2)).catch((error) => {
|
|
4
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
5
|
+
process.stderr.write(`${message}\n`);
|
|
6
|
+
process.exit(1);
|
|
7
|
+
});
|
|
8
|
+
//# sourceMappingURL=agent-bin.js.map
|