Skip to main content
Glama

execute_ssh_command

Execute commands on remote servers via SSH to manage systems, run scripts, or perform administrative tasks using persistent or new sessions.

Instructions

Execute a command on a remote server via SSH. Commands run in a persistent SSH session by default. Use newSession: true to run in a new session.

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
hostYes
portNo
usernameYes
passwordNo
privateKeyNo
commandYes
newSessionNo

Implementation Reference

  • Zod schema defining the input parameters for the execute_ssh_command tool, including host, port, username, authentication, command, and newSession option.
    const SSHConnectionArgsSchema = z.object({
        host: z.string(),
        port: z.number().default(22),
        username: z.string(),
        password: z.string().optional(),
        privateKey: z.string().optional(),
        command: z.string(),
        newSession: z.boolean().optional(),
    });
  • The primary handler function that executes the SSH command. It uses the ssh2 Client to connect (reusing persistent connection if possible), runs the command, captures output and errors, and returns structured result with exit code.
    async function executeSSHCommand(config: z.infer<typeof SSHConnectionArgsSchema>): Promise<{ output: string, code: number }> {
        return new Promise((resolve) => {
            const useNewSession = config.newSession || false;
            
            // Check if we need a new session or if connection details changed
            if (!useNewSession && persistentSSH?.ready && persistentSSHConfig &&
                persistentSSHConfig.host === config.host &&
                persistentSSHConfig.port === config.port &&
                persistentSSHConfig.username === config.username &&
                persistentSSHConfig.password === config.password &&
                persistentSSHConfig.privateKey === config.privateKey) {
                
                // Use existing connection
                persistentSSH.client.exec(config.command, (err: Error | undefined, stream: ClientChannel) => {
                    if (err) {
                        resolve({ output: `SSH exec error: ${err.message}`, code: 1 });
                        return;
                    }
    
                    let output = '';
                    let errorOutput = '';
    
                    stream.on('close', (code: number) => {
                        resolve({ 
                            output: output + (errorOutput ? `\nErrors:\n${errorOutput}` : ''),
                            code: code || 0
                        });
                    }).on('data', (data: Buffer) => {
                        output += data.toString('utf8');
                    }).stderr.on('data', (data: Buffer) => {
                        errorOutput += data.toString('utf8');
                    });
                });
                return;
            }
    
            // Need to create new connection
            const client = new Client();
            const sshConfig = {
                host: config.host,
                port: config.port,
                username: config.username,
                ...(config.password ? { password: config.password } : {}),
                ...(config.privateKey ? { privateKey: config.privateKey } : {}),
            };
    
            let output = '';
            let errorOutput = '';
    
            client.on('ready', () => {
                // If this is a persistent session, save the connection
                if (!useNewSession) {
                    if (persistentSSH?.client) {
                        persistentSSH.client.end();
                    }
                    persistentSSH = { client, ready: true };
                    persistentSSHConfig = sshConfig;
                }
    
                client.exec(config.command, (err: Error | undefined, stream: ClientChannel) => {
                    if (err) {
                        resolve({ output: `SSH exec error: ${err.message}`, code: 1 });
                        if (useNewSession) client.end();
                        return;
                    }
    
                    stream.on('close', (code: number) => {
                        resolve({ 
                            output: output + (errorOutput ? `\nErrors:\n${errorOutput}` : ''),
                            code: code || 0
                        });
                        if (useNewSession) client.end();
                    }).on('data', (data: Buffer) => {
                        output += data.toString('utf8');
                    }).stderr.on('data', (data: Buffer) => {
                        errorOutput += data.toString('utf8');
                    });
                });
            })
            .on('error', (err: Error) => {
                resolve({ output: `SSH connection error: ${err.message}`, code: 1 });
                if (useNewSession) client.end();
            })
            .connect(sshConfig);
        });
    }
  • src/index.ts:169-175 (registration)
    Tool registration in the ListTools response, specifying name, description, and input schema derived from Zod.
    {
        name: "execute_ssh_command",
        description: "Execute a command on a remote server via SSH. " +
            "Commands run in a persistent SSH session by default. " +
            "Use newSession: true to run in a new session.",
        inputSchema: zodToJsonSchema(SSHConnectionArgsSchema),
    },
  • src/index.ts:302-316 (registration)
    Dispatch logic in the CallTool request handler that validates arguments using the schema and invokes the executeSSHCommand handler.
    } else if (name === "execute_ssh_command") {
        const parsed = SSHConnectionArgsSchema.safeParse(args);
        if (!parsed.success) {
            throw new Error(`Invalid arguments for execute_ssh_command: ${parsed.error}`);
        }
    
        const { output, code } = await executeSSHCommand(parsed.data);
        return {
            content: [{ 
                type: "text", 
                text: output || `Command completed with code ${code}`
            }],
            isError: code !== 0,
        };
    }
Behavior2/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

With no annotations provided, the description carries full burden for behavioral disclosure. It mentions the persistent session behavior and newSession option, which is helpful. However, it doesn't cover critical aspects like security implications, error handling, timeout behavior, or output format. For a tool that executes remote commands with authentication parameters, this leaves significant gaps.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is appropriately concise with two sentences. The first sentence states the core purpose, and the second provides important behavioral context about session persistence. No wasted words, though it could be more comprehensive given the tool's complexity.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness2/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a tool with 7 parameters, 0% schema description coverage, no annotations, and no output schema, the description is insufficient. It doesn't explain authentication methods (password vs. privateKey), command execution context, error scenarios, or return values. The description should provide more guidance given the tool's security-sensitive nature and complexity.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters2/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 0%, so the description must compensate for all 7 parameters. The description only mentions the 'newSession' parameter explicitly. It doesn't explain the purpose or relationships of host, port, username, password, privateKey, or command parameters. The description adds minimal value beyond what the bare schema provides.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose4/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description clearly states the tool's purpose: 'Execute a command on a remote server via SSH.' It specifies the action (execute), resource (remote server), and method (SSH). However, it doesn't explicitly differentiate from the sibling tool 'execute_command' (which might be for local execution or different protocols).

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines3/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description provides some usage context: 'Commands run in a persistent SSH session by default. Use newSession: true to run in a new session.' This gives guidance on session behavior but doesn't explain when to use this tool versus the sibling 'execute_command' or address authentication method selection (password vs. privateKey).

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

Install Server

Other 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/PhialsBasement/CMD-MCP-Server'

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