@jadchene/mcp-ssh-service 1.1.1 â 1.2.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/README.md +187 -94
- package/dist/tools/definitions.js +26 -0
- package/dist/tools/handlers.js +11 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,95 +1,188 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
###
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
{
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
1
|
+
English | [įŽäŊ䏿](./README_zh.md)
|
|
2
|
+
|
|
3
|
+
# đ mcp-ssh
|
|
4
|
+
|
|
5
|
+
[](https://opensource.org/licenses/MIT)
|
|
6
|
+
[](https://nodejs.org/)
|
|
7
|
+
[](https://modelcontextprotocol.io/)
|
|
8
|
+
|
|
9
|
+
A **production-grade** Model Context Protocol (MCP) server designed for secure, stateless SSH automation. This service empowers AI agents to manage remote infrastructure with **human-in-the-loop** safety and **semantic environment awareness**.
|
|
10
|
+
|
|
11
|
+
---
|
|
12
|
+
|
|
13
|
+
## đ Key Pillars
|
|
14
|
+
|
|
15
|
+
### đ Uncompromising Security
|
|
16
|
+
* **Two-Step Confirmation**: High-risk operations (writes, deletes, restarts) return a `confirmationId`. Nothing happens until a human approves the specific transaction.
|
|
17
|
+
* **Command Blacklist**: Real-time regex interception for catastrophic commands like `rm -rf /` or `mkfs`.
|
|
18
|
+
* **Server-Level Read-Only**: Lock specific servers to a non-destructive mode at the configuration level.
|
|
19
|
+
* **Restricted File Deletion**: Hardcoded prevention of accidental deletion of system-critical paths like `/etc` or `/usr`.
|
|
20
|
+
|
|
21
|
+
### đ§ AI-Native Design
|
|
22
|
+
* **Semantic Infrastructure Discovery**: AI can list servers and understand their purposes via natural language descriptions.
|
|
23
|
+
* **Working Directory Aliases**: Map complex paths to simple aliases like `app-root` with descriptive metadata.
|
|
24
|
+
* **Contextual Pre-checks**: Built-in tools to verify dependencies (Docker, Git) before execution.
|
|
25
|
+
|
|
26
|
+
---
|
|
27
|
+
|
|
28
|
+
## đ Quick Start
|
|
29
|
+
|
|
30
|
+
### Installation
|
|
31
|
+
|
|
32
|
+
```bash
|
|
33
|
+
# Install globally via npm
|
|
34
|
+
npm install -g @jadchene/mcp-ssh-service
|
|
35
|
+
|
|
36
|
+
# Start the server with a config file
|
|
37
|
+
mcp-ssh-service --config ./config.json
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
### Source Setup
|
|
41
|
+
|
|
42
|
+
```bash
|
|
43
|
+
git clone https://github.com/jadchene/mcp-ssh.git
|
|
44
|
+
cd mcp-ssh
|
|
45
|
+
npm install
|
|
46
|
+
npm run build
|
|
47
|
+
node dist/index.js --config ./config.json
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
---
|
|
51
|
+
|
|
52
|
+
## âī¸ Configuration Schema
|
|
53
|
+
|
|
54
|
+
### Global Settings
|
|
55
|
+
| Parameter | Type | Description |
|
|
56
|
+
| --- | --- | --- |
|
|
57
|
+
| `logDir` | string | Directory for logs. Supports env vars like `${HOME}`. |
|
|
58
|
+
| `commandBlacklist` | string[] | Prohibited command regex patterns (e.g., `["^rm -rf"]`). |
|
|
59
|
+
| `defaultTimeout` | number | Command timeout in milliseconds (default: 60000). |
|
|
60
|
+
| `servers` | object | Dictionary of server configs where key is the `serverAlias`. |
|
|
61
|
+
|
|
62
|
+
### Server Object
|
|
63
|
+
| Parameter | Type | Description |
|
|
64
|
+
| --- | --- | --- |
|
|
65
|
+
| `host` | string | Remote IP or hostname. Supports env vars. |
|
|
66
|
+
| `port` | number | SSH port (default: 22). |
|
|
67
|
+
| `username` | string | SSH login user. |
|
|
68
|
+
| `password` | string | SSH password. Use `${VAR}` for security. |
|
|
69
|
+
| `privateKeyPath` | string | Path to private key file. |
|
|
70
|
+
| `passphrase` | string | Passphrase for the private key. |
|
|
71
|
+
| `readOnly` | boolean | Disables all write/modify tools for this server. |
|
|
72
|
+
| `desc` | string | Server description shown in `list_servers`. |
|
|
73
|
+
| `strictHostKeyChecking` | boolean | Set to `false` to bypass host key verification. |
|
|
74
|
+
| `workingDirectories` | object | Semantic path mappings (Key: { path, desc }). |
|
|
75
|
+
| `proxyJump` | object | Optional jump host (recursive server config). |
|
|
76
|
+
|
|
77
|
+
---
|
|
78
|
+
|
|
79
|
+
## âī¸ Configuration Example
|
|
80
|
+
|
|
81
|
+
```json
|
|
82
|
+
{
|
|
83
|
+
"logDir": "./logs",
|
|
84
|
+
"defaultTimeout": 60000,
|
|
85
|
+
"commandBlacklist": ["^apt-get upgrade", "curl.*\\|.*sh"],
|
|
86
|
+
"servers": {
|
|
87
|
+
"prod-web": {
|
|
88
|
+
"desc": "Primary API Cluster",
|
|
89
|
+
"host": "10.0.0.5",
|
|
90
|
+
"username": "deploy",
|
|
91
|
+
"privateKeyPath": "~/.ssh/id_rsa",
|
|
92
|
+
"passphrase": "${SSH_KEY_PWD}",
|
|
93
|
+
"workingDirectories": {
|
|
94
|
+
"logs": { "path": "/var/log/nginx", "desc": "Nginx access logs" }
|
|
95
|
+
},
|
|
96
|
+
"proxyJump": {
|
|
97
|
+
"host": "bastion.example.com",
|
|
98
|
+
"username": "jumpuser"
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
}
|
|
95
103
|
```
|
|
104
|
+
|
|
105
|
+
---
|
|
106
|
+
|
|
107
|
+
## đ ī¸ Integrated Toolset (50 Tools)
|
|
108
|
+
|
|
109
|
+
### Discovery & Core (8)
|
|
110
|
+
* `list_servers`
|
|
111
|
+
* `ping_server`
|
|
112
|
+
* `list_working_directories`
|
|
113
|
+
* `check_dependencies`
|
|
114
|
+
* `get_system_info`
|
|
115
|
+
* `pwd`
|
|
116
|
+
* `cd`
|
|
117
|
+
* `execute_batch` [Auth Required if any sub-command is high-risk]
|
|
118
|
+
|
|
119
|
+
### Shell & Basic (2)
|
|
120
|
+
* `execute_command` [Auth Required]
|
|
121
|
+
* `echo`
|
|
122
|
+
|
|
123
|
+
### File Management (10)
|
|
124
|
+
* `upload_file` [Auth Required]
|
|
125
|
+
* `download_file`
|
|
126
|
+
* `ll`
|
|
127
|
+
* `cat`
|
|
128
|
+
* `tail`
|
|
129
|
+
* `grep`
|
|
130
|
+
* `edit_text_file` [Auth Required]
|
|
131
|
+
* `touch`
|
|
132
|
+
* `rm_safe` [Auth Required]
|
|
133
|
+
* `find`
|
|
134
|
+
|
|
135
|
+
### Git (2)
|
|
136
|
+
* `git_status`
|
|
137
|
+
* `git_pull` [Auth Required]
|
|
138
|
+
|
|
139
|
+
### Docker & Compose (17)
|
|
140
|
+
* `docker_compose_up` [Auth Required]
|
|
141
|
+
* `docker_compose_down` [Auth Required]
|
|
142
|
+
* `docker_compose_stop` [Auth Required]
|
|
143
|
+
* `docker_compose_logs`
|
|
144
|
+
* `docker_compose_restart` [Auth Required]
|
|
145
|
+
* `docker_ps`
|
|
146
|
+
* `docker_images`
|
|
147
|
+
* `docker_pull` [Auth Required]
|
|
148
|
+
* `docker_cp` [Auth Required]
|
|
149
|
+
* `docker_stop` [Auth Required]
|
|
150
|
+
* `docker_rm` [Auth Required]
|
|
151
|
+
* `docker_start` [Auth Required]
|
|
152
|
+
* `docker_rmi` [Auth Required]
|
|
153
|
+
* `docker_commit` [Auth Required]
|
|
154
|
+
* `docker_logs`
|
|
155
|
+
* `docker_load` [Auth Required]
|
|
156
|
+
* `docker_save` [Auth Required]
|
|
157
|
+
|
|
158
|
+
### Service & Network (7)
|
|
159
|
+
* `systemctl_status`
|
|
160
|
+
* `systemctl_restart` [Auth Required]
|
|
161
|
+
* `systemctl_start` [Auth Required]
|
|
162
|
+
* `systemctl_stop` [Auth Required]
|
|
163
|
+
* `ip_addr`
|
|
164
|
+
* `firewall_cmd` [Auth Required]
|
|
165
|
+
* `netstat`
|
|
166
|
+
|
|
167
|
+
### Stats & Process (4)
|
|
168
|
+
* `nvidia_smi`
|
|
169
|
+
* `ps`
|
|
170
|
+
* `df_h`
|
|
171
|
+
* `du_sh`
|
|
172
|
+
|
|
173
|
+
Total: 50 tools.
|
|
174
|
+
|
|
175
|
+
---
|
|
176
|
+
|
|
177
|
+
## đ The Confirmation Workflow
|
|
178
|
+
|
|
179
|
+
1. **Request**: AI calls `execute_command({ command: 'systemctl restart nginx' })`.
|
|
180
|
+
2. **Intercept**: Server returns `status: "pending"` with a `confirmationId`.
|
|
181
|
+
3. **Human Input**: You review the action in your chat client and approve.
|
|
182
|
+
4. **Execution**: AI calls `execute_command` again with the `confirmationId` and `confirmExecution: true`.
|
|
183
|
+
5. **Verify**: Server ensures parameters match exactly and executes the SSH command.
|
|
184
|
+
|
|
185
|
+
---
|
|
186
|
+
|
|
187
|
+
## đ License
|
|
188
|
+
Released under the [MIT License](./LICENSE).
|
|
@@ -115,6 +115,16 @@ export const toolDefinitions = [
|
|
|
115
115
|
description: 'File reading: Reads text file content.',
|
|
116
116
|
inputSchema: baseParams({ filePath: { type: 'string' }, ...grepParam }, ['filePath'])
|
|
117
117
|
},
|
|
118
|
+
{
|
|
119
|
+
name: 'tail',
|
|
120
|
+
description: 'Log inspection: Reads last N lines of a file.',
|
|
121
|
+
inputSchema: baseParams({ filePath: { type: 'string' }, lines: { type: 'number' }, ...grepParam }, ['filePath'])
|
|
122
|
+
},
|
|
123
|
+
{
|
|
124
|
+
name: 'grep',
|
|
125
|
+
description: 'Pattern search: Search for a regex pattern in a file.',
|
|
126
|
+
inputSchema: baseParams({ filePath: { type: 'string' }, pattern: { type: 'string' }, ignoreCase: { type: 'boolean' } }, ['filePath', 'pattern'])
|
|
127
|
+
},
|
|
118
128
|
{
|
|
119
129
|
name: 'edit_text_file',
|
|
120
130
|
description: 'File creation/overwrite: Completely replaces file content. REQUIRES CONFIRMATION.',
|
|
@@ -129,11 +139,27 @@ export const toolDefinitions = [
|
|
|
129
139
|
description: 'Timestamp/File creation: Updates access time or creates empty file.',
|
|
130
140
|
inputSchema: baseParams({ filePath: { type: 'string' } }, ['filePath'])
|
|
131
141
|
},
|
|
142
|
+
{
|
|
143
|
+
name: 'rm_safe',
|
|
144
|
+
description: 'File deletion: Removes file or directory. REQUIRES CONFIRMATION.',
|
|
145
|
+
inputSchema: baseParams({ path: { type: 'string' }, recursive: { type: 'boolean' }, ...confirmationParams }, ['path'])
|
|
146
|
+
},
|
|
132
147
|
{
|
|
133
148
|
name: 'find',
|
|
134
149
|
description: 'Search for files in a directory hierarchy.',
|
|
135
150
|
inputSchema: baseParams({ path: { type: 'string' }, name: { type: 'string' }, ...grepParam }, ['path'])
|
|
136
151
|
},
|
|
152
|
+
// --- Git ---
|
|
153
|
+
{
|
|
154
|
+
name: 'git_status',
|
|
155
|
+
description: 'Git status: Displays repository status.',
|
|
156
|
+
inputSchema: baseParams(cwdParam)
|
|
157
|
+
},
|
|
158
|
+
{
|
|
159
|
+
name: 'git_pull',
|
|
160
|
+
description: 'Git update: Pulls latest changes. REQUIRES CONFIRMATION.',
|
|
161
|
+
inputSchema: baseParams({ ...cwdParam, ...confirmationParams })
|
|
162
|
+
},
|
|
137
163
|
// --- Docker & Compose (Requirements) ---
|
|
138
164
|
{
|
|
139
165
|
name: 'docker_compose_up',
|
package/dist/tools/handlers.js
CHANGED
|
@@ -4,6 +4,8 @@ const WRITE_TOOLS = [
|
|
|
4
4
|
'execute_command',
|
|
5
5
|
'upload_file',
|
|
6
6
|
'edit_text_file',
|
|
7
|
+
'rm_safe',
|
|
8
|
+
'git_pull',
|
|
7
9
|
'docker_compose_up',
|
|
8
10
|
'docker_compose_down',
|
|
9
11
|
'docker_compose_stop',
|
|
@@ -68,12 +70,21 @@ export class ToolHandlers {
|
|
|
68
70
|
case 'cd': return `cd ${params.path}`;
|
|
69
71
|
case 'll': return 'ls -l';
|
|
70
72
|
case 'cat': return `cat ${params.filePath}`;
|
|
73
|
+
case 'tail': return `tail -n ${params.lines || 50} ${params.filePath}`;
|
|
74
|
+
case 'grep': return `grep ${params.ignoreCase ? '-inE' : '-nE'} "${params.pattern.replace(/"/g, '\\"')}" ${params.filePath}`;
|
|
71
75
|
case 'edit_text_file':
|
|
72
76
|
const edB64 = Buffer.from(params.content).toString('base64');
|
|
73
77
|
return `echo "${edB64}" | base64 -d > ${params.filePath}`;
|
|
74
78
|
case 'touch': return `touch ${params.filePath}`;
|
|
79
|
+
case 'rm_safe':
|
|
80
|
+
const restricted = ['/', '/etc', '/usr', '/bin', '/var', '/root', '/home'];
|
|
81
|
+
if (restricted.includes(params.path.trim()))
|
|
82
|
+
throw new Error(`RM_SAFE: Denied for restricted directory.`);
|
|
83
|
+
return `rm ${params.recursive ? '-rf' : '-f'} ${params.path}`;
|
|
75
84
|
case 'echo': return `echo "${params.text}"`;
|
|
76
85
|
case 'find': return `find ${params.path} -name "${params.name}"`;
|
|
86
|
+
case 'git_status': return 'git status';
|
|
87
|
+
case 'git_pull': return 'git pull --no-edit';
|
|
77
88
|
case 'execute_command': return params.command;
|
|
78
89
|
case 'docker_compose_up': return 'docker-compose up -d';
|
|
79
90
|
case 'docker_compose_down': return 'docker-compose down --remove-orphans';
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@jadchene/mcp-ssh-service",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.2.0",
|
|
4
4
|
"description": "A production-ready, highly secure SSH MCP server featuring stateless connections, two-step operation confirmation, and comprehensive DevOps tool integration.",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"type": "module",
|