ssh-mcp 1.0.5 → 1.0.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (3) hide show
  1. package/README.md +35 -7
  2. package/build/index.js +54 -15
  3. package/package.json +25 -1
package/README.md CHANGED
@@ -1,7 +1,13 @@
1
1
  # SSH MCP Server
2
2
 
3
- [![License](https://img.shields.io/github/license/tufantunc/ssh-mcp)](./LICENSE)
4
3
  [![NPM Version](https://img.shields.io/npm/v/ssh-mcp)](https://www.npmjs.com/package/ssh-mcp)
4
+ [![Downloads](https://img.shields.io/npm/dm/ssh-mcp)](https://www.npmjs.com/package/ssh-mcp)
5
+ [![Node Version](https://img.shields.io/node/v/ssh-mcp)](https://nodejs.org/)
6
+ [![License](https://img.shields.io/github/license/tufantunc/ssh-mcp)](./LICENSE)
7
+ [![GitHub Stars](https://img.shields.io/github/stars/tufantunc/ssh-mcp?style=social)](https://github.com/tufantunc/ssh-mcp/stargazers)
8
+ [![GitHub Forks](https://img.shields.io/github/forks/tufantunc/ssh-mcp?style=social)](https://github.com/tufantunc/ssh-mcp/forks)
9
+ [![Build Status](https://github.com/tufantunc/ssh-mcp/actions/workflows/ci.yml/badge.svg)](https://github.com/tufantunc/ssh-mcp/actions)
10
+ [![GitHub issues](https://img.shields.io/github/issues/tufantunc/ssh-mcp)](https://github.com/tufantunc/ssh-mcp/issues)
5
11
 
6
12
  **SSH MCP Server** is a local Model Context Protocol (MCP) server that exposes SSH control for Linux and Windows systems, enabling LLMs and other MCP clients to execute shell commands securely via SSH.
7
13
 
@@ -28,10 +34,18 @@
28
34
  - Execute shell commands on remote Linux and Windows systems
29
35
  - Secure authentication via password or SSH key
30
36
  - Built with TypeScript and the official MCP SDK
37
+ - **Configurable timeout protection** with automatic process abortion
38
+ - **Graceful timeout handling** - attempts to kill hanging processes before closing connections
31
39
 
32
40
  ### Tools
33
41
 
34
42
  - `exec`: Execute a shell command on the remote server
43
+ - **Parameters:**
44
+ - `command` (required): Shell command to execute on the remote SSH server
45
+ - **Timeout Configuration:**
46
+ - Timeout is configured via command line argument `--timeout` (in milliseconds)
47
+ - Default timeout: 60000ms (1 minute)
48
+ - When a command times out, the server automatically attempts to abort the running process before closing the connection
35
49
 
36
50
  ## Installation
37
51
 
@@ -48,11 +62,16 @@
48
62
  ## Client Setup
49
63
 
50
64
  You can configure Claude Desktop to use this MCP Server.
51
- - `host`: Hostname or IP of the Linux or Windows server
52
- - `port`: SSH port (default: 22)
53
- - `user`: SSH username
54
- - `password`: SSH password (or use `key` for key-based auth) (optional)
55
- - `key`: Path to private SSH key (optional)
65
+
66
+ **Required Parameters:**
67
+ - `host`: Hostname or IP of the Linux or Windows server
68
+ - `user`: SSH username
69
+
70
+ **Optional Parameters:**
71
+ - `port`: SSH port (default: 22)
72
+ - `password`: SSH password (or use `key` for key-based auth)
73
+ - `key`: Path to private SSH key
74
+ - `timeout`: Command execution timeout in milliseconds (default: 60000ms = 1 minute)
56
75
 
57
76
 
58
77
  ```commandline
@@ -68,7 +87,8 @@ You can configure Claude Desktop to use this MCP Server.
68
87
  "--port=22",
69
88
  "--user=root",
70
89
  "--password=pass",
71
- "--key=path/to/key"
90
+ "--key=path/to/key",
91
+ "--timeout=30000"
72
92
  ]
73
93
  }
74
94
  }
@@ -87,6 +107,14 @@ npm run inspect
87
107
 
88
108
  SSH MCP Server is provided under the [MIT License](./LICENSE). Use at your own risk. This project is not affiliated with or endorsed by any SSH or MCP provider.
89
109
 
110
+ ## Contributing
111
+
112
+ We welcome contributions! Please see our [Contributing Guidelines](./CONTRIBUTING.md) for more information.
113
+
114
+ ## Code of Conduct
115
+
116
+ This project follows a [Code of Conduct](./CODE_OF_CONDUCT.md) to ensure a welcoming environment for everyone.
117
+
90
118
  ## Support
91
119
 
92
120
  If you find SSH MCP Server helpful, consider starring the repository or contributing! Pull requests and feedback are welcome.
package/build/index.js CHANGED
@@ -4,7 +4,7 @@ import { McpError, ErrorCode } from "@modelcontextprotocol/sdk/types.js";
4
4
  import { Client as SSHClient } from 'ssh2';
5
5
  import { z } from 'zod';
6
6
  import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
7
- // Example usage: node build/index.js --host=1.2.3.4 --port=22 --user=root --password=pass --key=path/to/key
7
+ // Example usage: node build/index.js --host=1.2.3.4 --port=22 --user=root --password=pass --key=path/to/key --timeout=5000
8
8
  function parseArgv() {
9
9
  const args = process.argv.slice(2);
10
10
  const config = {};
@@ -22,6 +22,7 @@ const PORT = argvConfig.port ? parseInt(argvConfig.port) : 22;
22
22
  const USER = argvConfig.user;
23
23
  const PASSWORD = argvConfig.password;
24
24
  const KEY = argvConfig.key;
25
+ const DEFAULT_TIMEOUT = argvConfig.timeout ? parseInt(argvConfig.timeout) : 60000; // 60 seconds default timeout
25
26
  function validateConfig(config) {
26
27
  const errors = [];
27
28
  if (!config.host)
@@ -37,7 +38,7 @@ function validateConfig(config) {
37
38
  validateConfig(argvConfig);
38
39
  const server = new McpServer({
39
40
  name: 'SSH MCP Server',
40
- version: '1.0.5',
41
+ version: '1.0.7',
41
42
  capabilities: {
42
43
  resources: {},
43
44
  tools: {},
@@ -76,27 +77,61 @@ server.tool("exec", "Execute a shell command on the remote SSH server and return
76
77
  async function execSshCommand(sshConfig, command) {
77
78
  return new Promise((resolve, reject) => {
78
79
  const conn = new SSHClient();
80
+ let timeoutId;
81
+ let isResolved = false;
82
+ // Set up timeout
83
+ timeoutId = setTimeout(() => {
84
+ if (!isResolved) {
85
+ isResolved = true;
86
+ // Try to abort the running command before closing connection
87
+ const abortTimeout = setTimeout(() => {
88
+ // If abort command itself times out, force close connection
89
+ conn.end();
90
+ }, 5000); // 5 second timeout for abort command
91
+ conn.exec('timeout 3s pkill -f "' + command + '" 2>/dev/null || true', (err, abortStream) => {
92
+ if (abortStream) {
93
+ abortStream.on('close', () => {
94
+ clearTimeout(abortTimeout);
95
+ conn.end();
96
+ });
97
+ }
98
+ else {
99
+ clearTimeout(abortTimeout);
100
+ conn.end();
101
+ }
102
+ });
103
+ reject(new McpError(ErrorCode.InternalError, `Command execution timed out after ${DEFAULT_TIMEOUT}ms`));
104
+ }
105
+ }, DEFAULT_TIMEOUT);
79
106
  conn.on('ready', () => {
80
107
  conn.exec(command, (err, stream) => {
81
108
  if (err) {
82
- reject(new McpError(ErrorCode.InternalError, `SSH exec error: ${err.message}`));
109
+ if (!isResolved) {
110
+ isResolved = true;
111
+ clearTimeout(timeoutId);
112
+ reject(new McpError(ErrorCode.InternalError, `SSH exec error: ${err.message}`));
113
+ }
83
114
  conn.end();
84
115
  return;
85
116
  }
86
117
  let stdout = '';
87
118
  let stderr = '';
88
119
  stream.on('close', (code, signal) => {
89
- conn.end();
90
- if (stderr) {
91
- reject(new McpError(ErrorCode.InternalError, `Error (code ${code}):\n${stderr}`));
92
- }
93
- else {
94
- resolve({
95
- content: [{
96
- type: 'text',
97
- text: stdout,
98
- }],
99
- });
120
+ if (!isResolved) {
121
+ isResolved = true;
122
+ clearTimeout(timeoutId);
123
+ conn.end();
124
+ if (stderr) {
125
+ reject(new McpError(ErrorCode.InternalError, `Error (code ${code}):\n${stderr}`));
126
+ }
127
+ else {
128
+ resolve({
129
+ content: [{
130
+ type: 'text',
131
+ text: stdout,
132
+ }],
133
+ });
134
+ }
100
135
  }
101
136
  });
102
137
  stream.on('data', (data) => {
@@ -108,7 +143,11 @@ async function execSshCommand(sshConfig, command) {
108
143
  });
109
144
  });
110
145
  conn.on('error', (err) => {
111
- reject(new McpError(ErrorCode.InternalError, `SSH connection error: ${err.message}`));
146
+ if (!isResolved) {
147
+ isResolved = true;
148
+ clearTimeout(timeoutId);
149
+ reject(new McpError(ErrorCode.InternalError, `SSH connection error: ${err.message}`));
150
+ }
112
151
  });
113
152
  conn.connect(sshConfig);
114
153
  });
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "ssh-mcp",
3
3
  "license": "MIT",
4
- "version": "1.0.5",
4
+ "version": "1.0.7",
5
5
  "description": "MCP server exposing SSH control for Linux and Windows systems via Model Context Protocol.",
6
6
  "type": "module",
7
7
  "bin": {
@@ -25,5 +25,29 @@
25
25
  "@types/ssh2": "^1.15.5",
26
26
  "shx": "^0.4.0",
27
27
  "typescript": "^5.4.5"
28
+ },
29
+ "homepage": "https://github.com/tufantunc/ssh-mcp#readme",
30
+ "repository": {
31
+ "type": "git",
32
+ "url": "https://github.com/tufantunc/ssh-mcp.git"
33
+ },
34
+ "bugs": {
35
+ "url": "https://github.com/tufantunc/ssh-mcp/issues"
36
+ },
37
+ "keywords": [
38
+ "ssh",
39
+ "mcp",
40
+ "model-context-protocol",
41
+ "server",
42
+ "windows",
43
+ "linux",
44
+ "automation",
45
+ "remote",
46
+ "cli",
47
+ "typescript"
48
+ ],
49
+ "author": "tufantunc",
50
+ "engines": {
51
+ "node": ">=18"
28
52
  }
29
53
  }