ssh-mcp 1.0.3 → 1.0.5

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 (2) hide show
  1. package/build/index.js +34 -11
  2. package/package.json +2 -2
package/build/index.js CHANGED
@@ -1,11 +1,9 @@
1
- // This file should be renamed to index.ts for TypeScript support
2
- import 'dotenv/config';
1
+ #!/usr/bin/env node
3
2
  import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
4
3
  import { McpError, ErrorCode } from "@modelcontextprotocol/sdk/types.js";
5
4
  import { Client as SSHClient } from 'ssh2';
6
5
  import { z } from 'zod';
7
6
  import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
8
- // Parse SSH config from process.argv
9
7
  // Example usage: node build/index.js --host=1.2.3.4 --port=22 --user=root --password=pass --key=path/to/key
10
8
  function parseArgv() {
11
9
  const args = process.argv.slice(2);
@@ -24,9 +22,22 @@ const PORT = argvConfig.port ? parseInt(argvConfig.port) : 22;
24
22
  const USER = argvConfig.user;
25
23
  const PASSWORD = argvConfig.password;
26
24
  const KEY = argvConfig.key;
25
+ function validateConfig(config) {
26
+ const errors = [];
27
+ if (!config.host)
28
+ errors.push('Missing required --host');
29
+ if (!config.user)
30
+ errors.push('Missing required --user');
31
+ if (config.port && isNaN(Number(config.port)))
32
+ errors.push('Invalid --port');
33
+ if (errors.length > 0) {
34
+ throw new Error('Configuration error:\n' + errors.join('\n'));
35
+ }
36
+ }
37
+ validateConfig(argvConfig);
27
38
  const server = new McpServer({
28
39
  name: 'SSH MCP Server',
29
- version: '1.0.0',
40
+ version: '1.0.5',
30
41
  capabilities: {
31
42
  resources: {},
32
43
  tools: {},
@@ -35,20 +46,32 @@ const server = new McpServer({
35
46
  server.tool("exec", "Execute a shell command on the remote SSH server and return the output.", {
36
47
  command: z.string().describe("Shell command to execute on the remote SSH server"),
37
48
  }, async ({ command }) => {
49
+ // Sanitize command input
50
+ if (typeof command !== 'string' || !command.trim()) {
51
+ throw new McpError(ErrorCode.InternalError, 'Command must be a non-empty string.');
52
+ }
38
53
  const sshConfig = {
39
54
  host: HOST,
40
55
  port: PORT,
41
56
  username: USER,
42
57
  };
43
- if (PASSWORD) {
44
- sshConfig.password = PASSWORD;
58
+ try {
59
+ if (PASSWORD) {
60
+ sshConfig.password = PASSWORD;
61
+ }
62
+ else if (KEY) {
63
+ const fs = await import('fs/promises');
64
+ sshConfig.privateKey = await fs.readFile(KEY, 'utf8');
65
+ }
66
+ const result = await execSshCommand(sshConfig, command);
67
+ return result;
45
68
  }
46
- else if (KEY) {
47
- const fs = await import('fs/promises');
48
- sshConfig.privateKey = await fs.readFile(KEY, 'utf8');
69
+ catch (err) {
70
+ // Wrap unexpected errors
71
+ if (err instanceof McpError)
72
+ throw err;
73
+ throw new McpError(ErrorCode.InternalError, `Unexpected error: ${err?.message || err}`);
49
74
  }
50
- const result = await execSshCommand(sshConfig, command);
51
- return result;
52
75
  });
53
76
  async function execSshCommand(sshConfig, command) {
54
77
  return new Promise((resolve, reject) => {
package/package.json CHANGED
@@ -1,6 +1,7 @@
1
1
  {
2
2
  "name": "ssh-mcp",
3
- "version": "1.0.3",
3
+ "license": "MIT",
4
+ "version": "1.0.5",
4
5
  "description": "MCP server exposing SSH control for Linux and Windows systems via Model Context Protocol.",
5
6
  "type": "module",
6
7
  "bin": {
@@ -16,7 +17,6 @@
16
17
  ],
17
18
  "dependencies": {
18
19
  "@modelcontextprotocol/sdk": "^1.10.2",
19
- "dotenv": "^16.3.1",
20
20
  "ssh2": "^1.11.0",
21
21
  "zod": "^3.24.3"
22
22
  },