ssh_connect
Establish SSH connections to remote servers using saved server configurations. This tool enables secure command execution and server management through Claude Code.
Instructions
Connect to an SSH server
Input Schema
| Name | Required | Description | Default |
|---|---|---|---|
| serverId | Yes | Server ID from ssh_list_servers | |
| timeout | No | Connection timeout in milliseconds (optional) |
Implementation Reference
- src/index.ts:312-347 (handler)The 'ssh_connect' request handler in src/index.ts. It retrieves the server configuration, calls sshManager.connect(), and handles the connection response.
case 'ssh_connect': { const serverId = args.serverId as string; const serverConfig = config.servers.find(s => s.id === serverId); if (!serverConfig) { return { content: [ { type: 'text', text: JSON.stringify({ error: `Server ${serverId} not found` }, null, 2), }, ], isError: true, }; } try { const connectionId = await sshManager.connect(serverConfig); return { content: [ { type: 'text', text: JSON.stringify({ connectionId, status: 'connected' }, null, 2), }, ], }; } catch (err: unknown) { return { content: [ { type: 'text', text: JSON.stringify({ error: err instanceof Error ? err.message : String(err) }, null, 2), }, ], isError: true, }; } } - src/ssh-manager.ts:54-139 (handler)The actual implementation of the connection logic using the ssh2 library. It handles the promise-based connection process and stores the connection in a Map.
async connect(serverConfig: ServerConfig): Promise<string> { // Check max connections limit if (this.connections.size >= this.maxConnections) { throw new Error(`Max connections limit reached (${this.maxConnections})`); } const connectionId = uuidv4(); const client = new Client(); return new Promise((resolve, reject) => { const timeoutMs = serverConfig.connectTimeout || 30000; let isResolved = false; const timeout = setTimeout(() => { if (!isResolved) { client.end(); // Clean up resources on timeout reject(new Error('Connection timeout')); } }, timeoutMs); client.on('ready', () => { clearTimeout(timeout); isResolved = true; const connection: SSHConnection = { id: connectionId, serverId: serverConfig.id, client, connectedAt: new Date(), lastActivity: new Date(), isBusy: false, serverConfig: { ...serverConfig }, // Store for auto-reconnect }; this.connections.set(connectionId, connection); this.lastConnectionId = connectionId; resolve(connectionId); }); client.on('error', (err) => { clearTimeout(timeout); if (!isResolved) { reject(err); } }); // Handle connection end/close const handleConnectionClose = () => { if (this.logCommands) { console.error(`[SSH] Connection ${connectionId} closed`); } this.connections.delete(connectionId); if (this.lastConnectionId === connectionId) { const remaining = Array.from(this.connections.keys()); this.lastConnectionId = remaining.length > 0 ? remaining[remaining.length - 1] : null; } }; client.on('end', handleConnectionClose); client.on('close', handleConnectionClose); // Build connect options const connectOptions: ConnectConfig = { host: serverConfig.host, port: serverConfig.port, username: serverConfig.username, }; // Auth method if (serverConfig.authMethod === 'agent') { connectOptions.agent = this.getAgentPath(); connectOptions.agentForward = true; } else if (serverConfig.authMethod === 'key' && serverConfig.privateKeyPath) { const keyPath = expandUser(serverConfig.privateKeyPath); connectOptions.privateKey = fs.readFileSync(keyPath); } else if (serverConfig.authMethod === 'password' && serverConfig.password) { connectOptions.password = serverConfig.password; } if (serverConfig.keepaliveInterval !== undefined) { connectOptions.keepaliveInterval = serverConfig.keepaliveInterval; } else if (this.keepaliveInterval > 0) { connectOptions.keepaliveInterval = this.keepaliveInterval; } client.connect(connectOptions); }); }