Skip to main content
Glama
handlers.ts5.39 kB
/** * Tool handlers for the Terminally-mcp server * Implements the business logic for each MCP tool */ import { ErrorCode, McpError } from '@modelcontextprotocol/sdk/types.js'; import { TmuxManager, TmuxWindow } from '../services/tmuxManager.js'; /** * Base handler class with common functionality */ abstract class ToolHandler<TArgs = any, TReturn = any> { protected tmuxManager: TmuxManager; constructor(tmuxManager: TmuxManager) { this.tmuxManager = tmuxManager; } abstract handle(args: TArgs): Promise<TReturn>; protected validateArgs(args: any, required: string[]) { for (const key of required) { if (args[key] === undefined || args[key] === null) { throw new McpError(ErrorCode.InvalidParams, `Missing required parameter: ${key}`); } } } } /** * Handler for creating a new terminal tab */ export class CreateTabToolHandler extends ToolHandler< { name?: string; cwd?: string; env?: Record<string, string>; login?: boolean }, { window_id: string; name: string } > { async handle(args: { name?: string; cwd?: string; env?: Record<string, string>; login?: boolean }): Promise<{ window_id: string; name: string }> { try { const result = await this.tmuxManager.createTab(args); return result; } catch (error) { console.error('Error in CreateTabToolHandler:', error); throw error; } } } /** * Handler for listing all terminal tabs */ export class ListTabsToolHandler extends ToolHandler<{}, { tabs: Array<{ window_id: string, name: string, active: boolean }> }> { async handle(): Promise<{ tabs: Array<{ window_id: string, name: string, active: boolean }> }> { try { const windows = await this.tmuxManager.listTabs(); // Convert TmuxWindow[] to the expected output format const tabs = windows.map((window: TmuxWindow) => ({ window_id: window.id, name: window.name, active: window.active })); return { tabs }; } catch (error) { console.error('Error in ListTabsToolHandler:', error); throw new McpError(ErrorCode.InternalError, (error as Error).message); } } } /** * Handler for reading logs from a terminal tab */ export class ReadLogsFromTabToolHandler extends ToolHandler< { window_id: string; lines?: number; strip_ansi?: boolean }, { content: string; returned_lines: number; truncated: boolean } > { async handle(args: { window_id: string; lines?: number; strip_ansi?: boolean }): Promise<{ content: string; returned_lines: number; truncated: boolean }> { this.validateArgs(args, ['window_id']); try { const result = await this.tmuxManager.readLogsFromTab( args.window_id, args.lines || 500, args.strip_ansi || false ); return result; } catch (error) { console.error('Error in ReadLogsFromTabToolHandler:', error); throw new McpError(ErrorCode.InternalError, `Error reading logs: ${(error as Error).message}`); } } } /** * Handler for executing a command in a terminal tab */ export class ExecuteCommandToolHandler extends ToolHandler< { window_id: string; command: string; timeout_ms?: number; strip_ansi?: boolean }, { output: string; exit_code: number; timed_out: boolean } > { async handle(args: { window_id: string; command: string; timeout_ms?: number; strip_ansi?: boolean }): Promise<{ output: string; exit_code: number; timed_out: boolean }> { this.validateArgs(args, ['window_id', 'command']); try { const result = await this.tmuxManager.executeCommand( args.window_id, args.command, args.timeout_ms || 10000, args.strip_ansi || false ); return result; } catch (error) { console.error('Error in ExecuteCommandToolHandler:', error); throw new McpError(ErrorCode.InternalError, `Error executing command: ${(error as Error).message}`); } } } /** * Handler for starting a long-running process */ export class StartProcessToolHandler extends ToolHandler< { window_id: string; command: string; append_newline?: boolean }, { started: boolean } > { async handle(args: { window_id: string; command: string; append_newline?: boolean }): Promise<{ started: boolean }> { this.validateArgs(args, ['window_id', 'command']); try { const result = await this.tmuxManager.startProcess( args.window_id, args.command, args.append_newline !== false ); return result; } catch (error) { console.error('Error in StartProcessToolHandler:', error); throw new McpError(ErrorCode.InternalError, `Error starting process: ${(error as Error).message}`); } } } /** * Handler for stopping a process */ export class StopProcessToolHandler extends ToolHandler< { window_id: string; signal?: string }, { success: boolean } > { async handle(args: { window_id: string; signal?: string }): Promise<{ success: boolean }> { this.validateArgs(args, ['window_id']); try { const result = await this.tmuxManager.stopProcess( args.window_id, args.signal || 'SIGINT' ); return result; } catch (error) { console.error('Error in StopProcessToolHandler:', error); throw new McpError(ErrorCode.InternalError, `Error stopping process: ${(error as Error).message}`); } } }

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/NightTrek/Terminally-mcp'

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