Skip to main content
Glama

runCommandBatch

Executes multiple shell commands sequentially on an SSH host via the MCP SSH Agent, enabling automated and secure remote task execution.

Instructions

Executes multiple shell commands sequentially on an SSH host

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
commandsYesList of shell commands to execute
hostAliasYesAlias or hostname of the SSH host

Implementation Reference

  • The core handler function implementing the 'runCommandBatch' MCP tool. It connects to an SSH host, runs each command in the provided batch, collects stdout/stderr/code for each, determines if all succeeded, and returns structured results.
    async runCommandBatch(hostAlias: string, commands: string[]): Promise<BatchCommandResult> { try { await this.connectToHost(hostAlias); const results: CommandResult[] = []; let success = true; for (const command of commands) { const result = await this.ssh.execCommand(command); const cmdResult: CommandResult = { stdout: result.stdout, stderr: result.stderr, code: result.code || 0 }; results.push(cmdResult); if (cmdResult.code !== 0) { success = false; // We don't abort, execute all commands } } this.ssh.dispose(); return { results, success }; } catch (error) { console.error(`Error during batch execution on ${hostAlias}:`, error); return { results: [{ stdout: '', stderr: error instanceof Error ? error.message : String(error), code: 1 }], success: false }; } }
  • TypeScript type definitions serving as the schema for the tool's input (hostAlias: string, commands: string[]) and output (BatchCommandResult containing array of CommandResult and success boolean).
    // Result of a remote command export interface CommandResult { stdout: string; stderr: string; code: number; } // SSH connection status export interface ConnectionStatus { connected: boolean; message: string; } // Batch result of remote commands export interface BatchCommandResult { results: CommandResult[]; success: boolean; }
  • Private helper method used by runCommandBatch to connect to the SSH host by parsing host info from ~/.ssh/config and establishing the NodeSSH connection.
    private async connectToHost(hostAlias: string): Promise<void> { // Get host information const hostInfo = await this.getHostInfo(hostAlias); if (!hostInfo) { throw new Error(`Host ${hostAlias} not found`); } // Create connection configuration const connectionConfig = { host: hostInfo.hostname, username: hostInfo.user, port: hostInfo.port || 22, privateKeyPath: hostInfo.identityFile }; try { await this.ssh.connect(connectionConfig); } catch (error) { throw new Error(`Connection to ${hostAlias} failed: ${error instanceof Error ? error.message : String(error)}`); } }
  • Supporting class that parses ~/.ssh/config and known_hosts files to provide host information required for connecting in runCommandBatch.
    export class SSHConfigParser { private configPath: string; private knownHostsPath: string; constructor() { const homeDir = homedir(); this.configPath = join(homeDir, '.ssh', 'config'); this.knownHostsPath = join(homeDir, '.ssh', 'known_hosts'); } /** * Reads and parses the SSH config file */ async parseConfig(): Promise<SSHHostInfo[]> { try { const content = await readFile(this.configPath, 'utf-8'); const config = sshConfig.parse(content); return this.extractHostsFromConfig(config); } catch (error) { console.error('Error reading SSH config:', error); return []; } } /** * Extracts host information from SSH Config */ private extractHostsFromConfig(config: any[]): SSHHostInfo[] { const hosts: SSHHostInfo[] = []; for (const section of config) { if (section.param === 'Host' && section.value !== '*') { const hostInfo: SSHHostInfo = { hostname: '', alias: section.value, }; // Search all entries for this host for (const param of section.config) { switch (param.param.toLowerCase()) { case 'hostname': hostInfo.hostname = param.value; break; case 'user': hostInfo.user = param.value; break; case 'port': hostInfo.port = parseInt(param.value, 10); break; case 'identityfile': hostInfo.identityFile = param.value; break; default: // Store other parameters hostInfo[param.param.toLowerCase()] = param.value; } } // Only add hosts with complete information if (hostInfo.hostname) { hosts.push(hostInfo); } } } return hosts; } /** * Reads the known_hosts file and extracts hostnames */ async parseKnownHosts(): Promise<string[]> { try { const content = await readFile(this.knownHostsPath, 'utf-8'); const knownHosts = content .split('\n') .filter(line => line.trim() !== '') .map(line => { // Format: hostname[,hostname2...] key-type public-key const parts = line.split(' ')[0]; return parts.split(',')[0]; }); return knownHosts; } catch (error) { console.error('Error reading known_hosts file:', error); return []; } } /** * Consolidates information from config and known_hosts */ async getAllKnownHosts(): Promise<SSHHostInfo[]> { const configHosts = await this.parseConfig(); const knownHostnames = await this.parseKnownHosts(); // Add hosts from known_hosts that aren't in the config for (const hostname of knownHostnames) { if (!configHosts.some(host => host.hostname === hostname || host.alias === hostname)) { configHosts.push({ hostname: hostname }); } } return configHosts; } }

Other Tools

Related Tools

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/AiondaDotCom/mcp-ssh'

If you have feedback or need assistance with the MCP directory API, please join our Discord server