ssh_execute
Run commands on remote systems securely via SSH, enabling direct management and execution of tasks on connected devices without manual intervention.
Instructions
Execute a command on a remote host via SSH
Example usage:
{
"connectionId": "raspberry-pi",
"command": "uname -a"
}
Configuration required in config.json:
{
"ssh": {
"enabled": true,
"connections": {
"raspberry-pi": {
"host": "raspberrypi.local",
"port": 22,
"username": "pi",
"password": "raspberry"
}
}
}
}
Input Schema
TableJSON Schema
| Name | Required | Description | Default |
|---|---|---|---|
| command | Yes | Command to execute | |
| connectionId | Yes | ID of the SSH connection to use |
Implementation Reference
- src/index.ts:471-541 (handler)Main MCP tool handler for 'ssh_execute': validates input, retrieves SSH connection from pool, executes command, logs to history, returns output and metadata.case "ssh_execute": { if (!this.config.ssh.enabled) { throw new McpError( ErrorCode.InvalidRequest, "SSH support is disabled in configuration" ); } const args = z.object({ connectionId: z.string(), command: z.string() }).parse(request.params.arguments); const connectionConfig = this.config.ssh.connections[args.connectionId]; if (!connectionConfig) { throw new McpError( ErrorCode.InvalidRequest, `Unknown SSH connection ID: ${args.connectionId}` ); } try { // Validate command this.validateCommand('cmd', args.command); const connection = await this.sshPool.getConnection(args.connectionId, connectionConfig); const { output, exitCode } = await connection.executeCommand(args.command); // Store in history if enabled if (this.config.security.logCommands) { this.commandHistory.push({ command: args.command, output, timestamp: new Date().toISOString(), exitCode, connectionId: args.connectionId }); if (this.commandHistory.length > this.config.security.maxHistorySize) { this.commandHistory = this.commandHistory.slice(-this.config.security.maxHistorySize); } } return { content: [{ type: "text", text: output || 'Command completed successfully (no output)' }], isError: exitCode !== 0, metadata: { exitCode, connectionId: args.connectionId } }; } catch (error) { const errorMessage = error instanceof Error ? error.message : String(error); if (this.config.security.logCommands) { this.commandHistory.push({ command: args.command, output: `SSH error: ${errorMessage}`, timestamp: new Date().toISOString(), exitCode: -1, connectionId: args.connectionId }); } throw new McpError( ErrorCode.InternalError, `SSH error: ${errorMessage}` ); } }
- src/index.ts:208-251 (schema)Tool schema definition including name, description, and input schema for 'ssh_execute' in the ListTools response.{ name: "ssh_execute", description: `Execute a command on a remote host via SSH Example usage: \`\`\`json { "connectionId": "raspberry-pi", "command": "uname -a" } \`\`\` Configuration required in config.json: \`\`\`json { "ssh": { "enabled": true, "connections": { "raspberry-pi": { "host": "raspberrypi.local", "port": 22, "username": "pi", "password": "raspberry" } } } } \`\`\``, inputSchema: { type: "object", properties: { connectionId: { type: "string", description: "ID of the SSH connection to use", enum: Object.keys(this.config.ssh.connections) }, command: { type: "string", description: "Command to execute" } }, required: ["connectionId", "command"] } },
- src/utils/ssh.ts:96-131 (helper)Core implementation of SSH command execution using ssh2 Client.exec, capturing stdout/stderr and exit code.async executeCommand(command: string): Promise<{ output: string; exitCode: number }> { this.lastActivity = Date.now(); // Check connection and attempt reconnect if needed if (!this.isConnected) { await this.connect(); } return new Promise((resolve, reject) => { this.client.exec(command, (err, stream) => { if (err) { reject(err); return; } let output = ''; let errorOutput = ''; stream .on('data', (data: Buffer) => { output += data.toString(); }) .stderr.on('data', (data: Buffer) => { errorOutput += data.toString(); }); stream.on('close', (code: number) => { this.lastActivity = Date.now(); resolve({ output: output || errorOutput, exitCode: code || 0 }); }); }); }); }
- src/utils/ssh.ts:151-182 (helper)SSHConnectionPool class that manages persistent SSH connections, providing getConnection for reuse and reconnection logic.export class SSHConnectionPool { private connections: Map<string, SSHConnection> = new Map(); async getConnection(connectionId: string, config: SSHConnectionConfig): Promise<SSHConnection> { let connection = this.connections.get(connectionId); if (!connection) { connection = new SSHConnection(config); this.connections.set(connectionId, connection); await connection.connect(); } else if (!connection.isActive()) { await connection.connect(); } return connection; } async closeConnection(connectionId: string): Promise<void> { const connection = this.connections.get(connectionId); if (connection) { connection.disconnect(); this.connections.delete(connectionId); } } closeAll(): void { for (const connection of this.connections.values()) { connection.disconnect(); } this.connections.clear(); } }