# Terminal Module Documentation
## Overview
The Terminal module provides 4 MCP tools for managing shell operations on Komodo servers with stateful WebSocket sessions.
## Features
- **WebSocket-based terminal sessions** - Real-time bidirectional communication
- **Stateful session management** - Persistent terminal sessions with automatic cleanup
- **Command execution** - Execute shell commands with full control
- **Output streaming** - Retrieve stdout/stderr with flexible options
- **Session lifecycle** - Connect, execute, retrieve output, and disconnect
## Architecture
### Session Management
The module maintains an in-memory session store tracking active WebSocket connections:
```typescript
interface TerminalSession {
sessionId: string; // Unique session identifier
serverId: string; // Target server ID
ws?: WebSocket; // WebSocket connection
status: string; // Connection status
createdAt: number; // Creation timestamp
lastActivity: number; // Last activity timestamp
}
```
### Session Lifecycle
```
1. Connect → Creates WebSocket connection and session
2. Execute → Runs commands in the session
3. GetOutput → Retrieves command output
4. Disconnect → Closes connection and cleans up
```
### Automatic Cleanup
- Sessions track last activity timestamp
- `cleanupExpiredSessions()` removes stale sessions
- Default expiry: 1 hour of inactivity
- Called on disconnect and periodically
## MCP Tools
### 1. komodo_terminal_Connect
Opens a WebSocket terminal session to a server.
**API Mapping:** `WS /terminal/{server_id}`
**Input Schema:**
```json
{
"server_id": "server-123",
"options": {
"shell": "bash",
"cwd": "/home/user",
"env": {
"PATH": "/usr/bin:/usr/local/bin"
},
"timeout": 30
}
}
```
**Response:**
```json
{
"success": true,
"sessionId": "term_server-123_1234567890_abc123",
"status": "connected",
"message": "Terminal session established on server server-123"
}
```
**Options:**
- `shell` - Shell to use (bash, sh, zsh, fish, powershell, cmd)
- `cwd` - Starting working directory
- `env` - Environment variables for the session
- `timeout` - Connection timeout in seconds
**Use Cases:**
- Interactive server administration
- Long-running maintenance tasks
- Multiple command sequences
- Real-time monitoring
### 2. komodo_terminal_Execute
Executes a command in a terminal session.
**API Mapping:** `POST /terminal/{server_id}/exec`
**Input Schema:**
```json
{
"server_id": "server-123",
"command": "ls -la /var/log",
"session_id": "term_server-123_1234567890_abc123",
"options": {
"cwd": "/var/log",
"env": {
"LANG": "en_US.UTF-8"
},
"timeout": 300,
"stdin": "input data",
"captureOutput": true
}
}
```
**Response:**
```json
{
"success": true,
"executionId": "exec_1234567890_xyz789",
"sessionId": "term_server-123_1234567890_abc123",
"status": "running",
"message": "Command execution started on server server-123"
}
```
**Options:**
- `cwd` - Working directory for the command
- `env` - Additional environment variables
- `timeout` - Execution timeout in seconds (default: 300)
- `stdin` - Standard input to pass to the command
- `captureOutput` - Capture stdout/stderr (default: true)
**Behavior:**
- If `session_id` is provided, uses existing session
- If `session_id` is omitted, creates a new session
- Returns immediately with execution ID
- Use `GetOutput` to retrieve results
**Use Cases:**
- Execute system commands
- Run scripts and programs
- Perform administrative tasks
- Batch operations
### 3. komodo_terminal_GetOutput
Retrieves output from a terminal session or command execution.
**API Mapping:** `GET /terminal/{session_id}/output`
**Input Schema:**
```json
{
"session_id": "term_server-123_1234567890_abc123",
"options": {
"lines": 100,
"tail": true,
"follow": false,
"since": "2024-01-26T12:00:00Z"
}
}
```
**Response:**
```json
{
"success": true,
"output": {
"stdout": "file1.log\nfile2.log\nfile3.log\n",
"stderr": "",
"exitCode": 0,
"isComplete": true,
"timestamp": "2024-01-26T12:30:00Z"
},
"message": "Output retrieved successfully"
}
```
**Options:**
- `lines` - Number of lines to retrieve (default: all)
- `tail` - Retrieve from end of output (default: false)
- `follow` - Stream output as it arrives (default: false)
- `since` - Retrieve output since timestamp (ISO 8601)
**Output Fields:**
- `stdout` - Standard output content
- `stderr` - Standard error content
- `exitCode` - Process exit code (when complete)
- `isComplete` - Whether execution has finished
- `timestamp` - Output retrieval timestamp
**Use Cases:**
- Monitor command progress
- Retrieve command results
- Debug failed executions
- Stream real-time output
### 4. komodo_terminal_Disconnect
Closes a terminal session and cleans up resources.
**API Mapping:** `DELETE /terminal/{session_id}`
**Input Schema:**
```json
{
"session_id": "term_server-123_1234567890_abc123",
"options": {
"force": false,
"killProcesses": true,
"timeout": 10
}
}
```
**Response:**
```json
{
"success": true,
"sessionId": "term_server-123_1234567890_abc123",
"status": "disconnected",
"message": "Terminal session closed successfully"
}
```
**Options:**
- `force` - Force disconnect even if processes are running
- `killProcesses` - Kill running processes before disconnecting (default: true)
- `timeout` - Graceful shutdown timeout in seconds (default: 10)
**Behavior:**
- Closes WebSocket connection
- Terminates running processes (if `killProcesses` is true)
- Removes session from active sessions
- Cleans up server-side resources
**Use Cases:**
- Clean up after operations
- Free server resources
- End interactive sessions
- Prevent resource leaks
## Usage Examples
### Basic Command Execution
```javascript
// Connect to server
const connectResult = await handleConnect({
server_id: 'prod-server-1',
options: {
shell: 'bash',
},
});
const sessionId = connectResult.sessionId;
// Execute command
const execResult = await handleExecute({
server_id: 'prod-server-1',
command: 'df -h',
session_id: sessionId,
});
// Get output
const outputResult = await handleGetOutput({
session_id: sessionId,
});
console.log(outputResult.output.stdout);
// Disconnect
await handleDisconnect({
session_id: sessionId,
});
```
### Quick Single Command
```javascript
// Execute without pre-existing session (auto-creates)
const result = await handleExecute({
server_id: 'prod-server-1',
command: 'uptime',
});
// Get results
const output = await handleGetOutput({
session_id: result.sessionId,
});
// Cleanup
await handleDisconnect({
session_id: result.sessionId,
});
```
### Interactive Session
```javascript
// Open interactive session
const session = await handleConnect({
server_id: 'dev-server-2',
options: {
shell: 'bash',
cwd: '/home/user/project',
},
});
// Run multiple commands
const commands = [
'git status',
'npm install',
'npm test',
];
for (const command of commands) {
await handleExecute({
server_id: 'dev-server-2',
command,
session_id: session.sessionId,
});
const output = await handleGetOutput({
session_id: session.sessionId,
});
console.log(`Output: ${output.output.stdout}`);
}
// Close session
await handleDisconnect({
session_id: session.sessionId,
});
```
### Streaming Output
```javascript
// Execute long-running command
const execResult = await handleExecute({
server_id: 'prod-server-1',
command: 'tail -f /var/log/app.log',
options: {
captureOutput: true,
},
});
// Poll for output updates
const interval = setInterval(async () => {
const output = await handleGetOutput({
session_id: execResult.sessionId,
options: {
tail: true,
lines: 10,
},
});
console.log(output.output.stdout);
if (output.output.isComplete) {
clearInterval(interval);
}
}, 1000);
// Later: disconnect
setTimeout(() => {
handleDisconnect({
session_id: execResult.sessionId,
options: {
force: true,
},
});
}, 60000);
```
## Session Management Best Practices
### 1. Always Disconnect
```javascript
try {
const session = await handleConnect({ server_id: 'server-1' });
// ... operations ...
} finally {
await handleDisconnect({ session_id: sessionId });
}
```
### 2. Handle Errors
```javascript
const result = await handleExecute({
server_id: 'server-1',
command: 'risky-command',
});
if (!result.success) {
console.error(`Error: ${result.error}`);
return;
}
```
### 3. Monitor Session Count
```javascript
import { getActiveSessionCount, cleanupExpiredSessions } from './modules/terminal';
// Periodically clean up
setInterval(() => {
const count = cleanupExpiredSessions(3600000); // 1 hour
console.log(`Cleaned up ${count} expired sessions`);
}, 300000); // Every 5 minutes
// Monitor active sessions
console.log(`Active sessions: ${getActiveSessionCount()}`);
```
### 4. Use Appropriate Timeouts
```javascript
// Short timeout for quick commands
await handleExecute({
server_id: 'server-1',
command: 'ls',
options: {
timeout: 10,
},
});
// Long timeout for slow operations
await handleExecute({
server_id: 'server-1',
command: 'npm install',
options: {
timeout: 600, // 10 minutes
},
});
```
## Error Handling
### Common Errors
| Error | Cause | Solution |
|-------|-------|----------|
| `Session not found` | Invalid session ID | Verify session exists or create new |
| `Connection timeout` | Network issues | Increase timeout, check connectivity |
| `Command timeout` | Long-running command | Increase execution timeout |
| `WebSocket error` | Connection dropped | Reconnect and retry |
### Error Response Format
```json
{
"success": false,
"error": "Session not found",
"message": "Terminal session term_xyz does not exist or has expired"
}
```
### Handling Disconnections
```javascript
const result = await handleGetOutput({
session_id: sessionId,
});
if (!result.success && result.error === 'Session not found') {
// Session expired or was disconnected
// Create new session and retry
const newSession = await handleConnect({
server_id: serverId,
});
// ... retry operation ...
}
```
## Security Considerations
### 1. Command Injection Prevention
```javascript
// GOOD: Use parameterized input
const safeCommand = `ls ${JSON.stringify(userInput)}`;
// BAD: Direct string interpolation
const unsafeCommand = `ls ${userInput}`;
```
### 2. Environment Variable Sanitization
```javascript
// Validate environment variables
const env = {
PATH: sanitizePath(userPath),
HOME: sanitizeHome(userHome),
};
```
### 3. Session Timeout
```javascript
// Enforce maximum session lifetime
cleanupExpiredSessions(3600000); // 1 hour max
```
### 4. Resource Limits
```javascript
// Limit concurrent sessions per server
const maxSessionsPerServer = 5;
const serverSessions = getSessionsByServer(serverId);
if (serverSessions.length >= maxSessionsPerServer) {
throw new Error('Maximum sessions reached for this server');
}
```
## Performance Optimization
### 1. Session Reuse
```javascript
// Reuse session for multiple commands
const session = await handleConnect({ server_id: 'server-1' });
for (const cmd of commands) {
await handleExecute({
server_id: 'server-1',
command: cmd,
session_id: session.sessionId,
});
}
await handleDisconnect({ session_id: session.sessionId });
```
### 2. Output Buffering
```javascript
// Don't retrieve output too frequently
const output = await handleGetOutput({
session_id: sessionId,
options: {
lines: 1000, // Buffer more lines
},
});
```
### 3. Lazy Cleanup
```javascript
// Clean up sessions in background
setInterval(() => {
cleanupExpiredSessions(3600000);
}, 300000); // Every 5 minutes
```
## Troubleshooting
### Session Not Found
```javascript
// Check session exists
import { getSessionInfo } from './modules/terminal';
const info = getSessionInfo(sessionId);
if (!info) {
console.error('Session does not exist');
}
```
### Output Not Available
```javascript
// Wait for execution to complete
const maxRetries = 10;
let retries = 0;
while (retries < maxRetries) {
const output = await handleGetOutput({ session_id: sessionId });
if (output.output.isComplete) {
break;
}
await new Promise(resolve => setTimeout(resolve, 1000));
retries++;
}
```
### WebSocket Connection Issues
```javascript
// Retry connection with backoff
async function connectWithRetry(serverId, maxRetries = 3) {
for (let i = 0; i < maxRetries; i++) {
try {
const result = await handleConnect({ server_id: serverId });
if (result.success) {
return result;
}
} catch (error) {
if (i === maxRetries - 1) throw error;
await new Promise(resolve => setTimeout(resolve, 1000 * (i + 1)));
}
}
}
```
## Testing
See `tests/terminal.test.ts` for comprehensive test suite including:
- Tool definition validation
- Session lifecycle testing
- Command execution scenarios
- Error handling verification
- Integration workflows
- Concurrent session management
## API Reference
### Exported Functions
- `handleConnect(args)` - Create terminal session
- `handleExecute(args)` - Execute command
- `handleGetOutput(args)` - Retrieve output
- `handleDisconnect(args)` - Close session
- `cleanupExpiredSessions(maxAgeMs)` - Clean up stale sessions
- `getActiveSessionCount()` - Get active session count
- `getSessionInfo(sessionId)` - Get session details
### Exported Constants
- `terminalTools` - Array of MCP tool definitions
- `TERMINAL_TOOL_COUNT` - Number of tools (4)
- `terminalHandlers` - Map of tool handlers
## Future Enhancements
1. **WebSocket Implementation** - Real WebSocket support (currently simulated)
2. **Output Streaming** - True real-time output streaming
3. **Session Persistence** - Persist sessions across restarts
4. **Multi-user Support** - Session isolation per user
5. **Session Recording** - Record and replay sessions
6. **Resource Quotas** - CPU/memory limits per session
7. **Audit Logging** - Track all command executions
8. **Interactive TTY** - Full terminal emulation support