Skip to main content
Glama
terminal.py6.39 kB
""" Terminal API endpoints for interactive terminal sessions. """ import asyncio import logging from typing import Annotated from fastapi import APIRouter, Depends, HTTPException try: from ...core.terminal import TerminalManager except ImportError: try: from mcp_terminal.core.terminal import TerminalManager except ImportError: # Fallback for older structure from core.terminal import TerminalManager from ..models.terminal import ( TerminalCreate, TerminalInput, TerminalListResponse, TerminalResize, TerminalResponse, TerminalSnapshot, ) logger = logging.getLogger(__name__) router = APIRouter(prefix="/terminals", tags=["terminals"]) # Singleton terminal manager _terminal_manager: TerminalManager = None def get_terminal_manager() -> TerminalManager: """ Get or create terminal manager instance. Returns: TerminalManager singleton """ global _terminal_manager if _terminal_manager is None: _terminal_manager = TerminalManager() return _terminal_manager TerminalManagerDep = Annotated[TerminalManager, Depends(get_terminal_manager)] @router.post("/", response_model=TerminalResponse, status_code=201) async def create_terminal( request: TerminalCreate, manager: TerminalManagerDep, ): """ Create a new terminal session. Args: request: Terminal creation parameters manager: Terminal manager dependency Returns: Terminal creation response """ try: terminal_id = await manager.create( rows=request.rows, cols=request.cols, shell_command=request.shell_command, ) return TerminalResponse( success=True, terminal_id=terminal_id, message="Terminal created successfully", ) except Exception as e: logger.error(f"Error creating terminal: {e}") raise HTTPException(status_code=500, detail=str(e)) @router.get("/", response_model=TerminalListResponse) async def list_terminals(manager: TerminalManagerDep): """ List all active terminal sessions. Args: manager: Terminal manager dependency Returns: List of active terminals """ try: terminals = await manager.list_terminals() return TerminalListResponse( success=True, terminals=terminals, count=len(terminals) ) except Exception as e: logger.error(f"Error listing terminals: {e}") raise HTTPException(status_code=500, detail=str(e)) @router.get("/{terminal_id}/snapshot", response_model=TerminalSnapshot) async def get_terminal_snapshot(terminal_id: str, manager: TerminalManagerDep): """ Get visual snapshot of terminal. Args: terminal_id: Terminal session ID manager: Terminal manager dependency Returns: Terminal snapshot with display and cursor info """ try: # Add 5 second timeout snapshot = await asyncio.wait_for( manager.get_snapshot(terminal_id), timeout=5.0 ) return TerminalSnapshot(**snapshot) except asyncio.TimeoutError: raise HTTPException(status_code=408, detail="Request timeout") except ValueError as e: raise HTTPException(status_code=404, detail=str(e)) except Exception as e: logger.error(f"Error getting snapshot: {e}") raise HTTPException(status_code=500, detail=str(e)) @router.post("/{terminal_id}/input", response_model=TerminalResponse) async def send_terminal_input( terminal_id: str, request: TerminalInput, manager: TerminalManagerDep, ): """ Send input to terminal session. Args: terminal_id: Terminal session ID request: Input data manager: Terminal manager dependency Returns: Operation response """ try: # Add 5 second timeout await asyncio.wait_for( manager.send_input(terminal_id, request.data), timeout=5.0 ) return TerminalResponse( success=True, terminal_id=terminal_id, message="Input sent successfully" ) except asyncio.TimeoutError: raise HTTPException(status_code=408, detail="Request timeout") except ValueError as e: raise HTTPException(status_code=404, detail=str(e)) except Exception as e: logger.error(f"Error sending input: {e}") raise HTTPException(status_code=500, detail=str(e)) @router.put("/{terminal_id}/resize", response_model=TerminalResponse) async def resize_terminal( terminal_id: str, request: TerminalResize, manager: TerminalManagerDep, ): """ Resize terminal session. Args: terminal_id: Terminal session ID request: New size manager: Terminal manager dependency Returns: Operation response """ try: # Add 5 second timeout await asyncio.wait_for( manager.resize(terminal_id, request.rows, request.cols), timeout=5.0 ) return TerminalResponse( success=True, terminal_id=terminal_id, message="Terminal resized" ) except asyncio.TimeoutError: raise HTTPException(status_code=408, detail="Request timeout") except ValueError as e: raise HTTPException(status_code=404, detail=str(e)) except Exception as e: logger.error(f"Error resizing terminal: {e}") raise HTTPException(status_code=500, detail=str(e)) @router.delete("/{terminal_id}", response_model=TerminalResponse) async def close_terminal(terminal_id: str, manager: TerminalManagerDep): """ Close terminal session. Args: terminal_id: Terminal session ID manager: Terminal manager dependency Returns: Operation response """ try: # Add 5 second timeout await asyncio.wait_for(manager.close(terminal_id), timeout=5.0) return TerminalResponse( success=True, terminal_id=terminal_id, message="Terminal closed" ) except asyncio.TimeoutError: raise HTTPException(status_code=408, detail="Request timeout") except ValueError as e: raise HTTPException(status_code=404, detail=str(e)) except Exception as e: logger.error(f"Error closing terminal: {e}") raise HTTPException(status_code=500, detail=str(e))

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/alejoair/mcp-terminal'

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