Skip to main content
Glama
ollama_server_control.py13.8 kB
""" Ollama Server Control Module Manages starting, stopping, and monitoring the local Ollama server for MCP v0.9. Based on the working Bridge v1.0 implementation. """ import asyncio import logging import os import platform import psutil import subprocess import time from typing import Dict, List, Optional, Any, Tuple from dataclasses import dataclass @dataclass class OllamaProcessInfo: """Information about the Ollama process.""" pid: Optional[int] status: str # "running", "stopped", "unknown" port: int uptime_seconds: Optional[int] memory_mb: Optional[float] cpu_percent: Optional[float] class OllamaServerController: """Controller to manage the Ollama server.""" def __init__(self, host: str = "http://localhost:11434"): self.host = host self.port = 11434 self.logger = logging.getLogger(__name__) async def get_server_status(self) -> OllamaProcessInfo: """Get detailed status of the Ollama server.""" try: # Find the Ollama process ollama_pid = None ollama_process = None for proc in psutil.process_iter(['pid', 'name', 'cmdline', 'create_time']): try: if proc.info['name'] and 'ollama' in proc.info['name'].lower(): # Verify it's the server (not client commands) cmdline = proc.info.get('cmdline', []) if any('serve' in str(cmd).lower() for cmd in cmdline) or \ any('11434' in str(cmd) for cmd in cmdline): ollama_pid = proc.info['pid'] ollama_process = proc break except (psutil.NoSuchProcess, psutil.AccessDenied): continue if ollama_process: try: # Get detailed process info memory_info = ollama_process.memory_info() memory_mb = memory_info.rss / 1024 / 1024 cpu_percent = ollama_process.cpu_percent() create_time = ollama_process.create_time() uptime = int(time.time() - create_time) return OllamaProcessInfo( pid=ollama_pid, status="running", port=self.port, uptime_seconds=uptime, memory_mb=memory_mb, cpu_percent=cpu_percent ) except (psutil.NoSuchProcess, psutil.AccessDenied) as e: self.logger.warning(f"Could not get process details: {e}") return OllamaProcessInfo( pid=ollama_pid, status="running", port=self.port, uptime_seconds=None, memory_mb=None, cpu_percent=None ) else: return OllamaProcessInfo( pid=None, status="stopped", port=self.port, uptime_seconds=None, memory_mb=None, cpu_percent=None ) except Exception as e: self.logger.error(f"Error getting Ollama status: {e}") return OllamaProcessInfo( pid=None, status="unknown", port=self.port, uptime_seconds=None, memory_mb=None, cpu_percent=None ) async def start_server(self) -> Dict[str, Any]: """Start the Ollama server.""" try: # Check if already running status = await self.get_server_status() if status.status == "running": return { "success": True, "message": "Ollama server is already running.", "pid": status.pid, "already_running": True } # Determine start command based on OS if platform.system() == "Windows": # On Windows, 'ollama serve' starts in the background process = subprocess.Popen( ["ollama", "serve"], stdout=subprocess.PIPE, stderr=subprocess.PIPE, creationflags=subprocess.CREATE_NO_WINDOW ) else: # On Linux/macOS process = subprocess.Popen( ["ollama", "serve"], stdout=subprocess.PIPE, stderr=subprocess.PIPE ) # Wait for the server to start (max 10 seconds) for i in range(10): await asyncio.sleep(1) status = await self.get_server_status() if status.status == "running": return { "success": True, "message": "Ollama server started successfully.", "pid": status.pid, "startup_time_seconds": i + 1 } # If it hasn't started within 10 seconds return { "success": False, "message": "Timeout: Ollama server did not start within 10 seconds.", "error": "startup_timeout" } except FileNotFoundError: return { "success": False, "message": "Ollama not found. Please ensure it is installed and in your system's PATH.", "error": "ollama_not_found", "install_guide": self._get_install_guide() } except Exception as e: self.logger.error(f"Error starting Ollama server: {e}") return { "success": False, "message": f"Error starting server: {str(e)}", "error": str(e) } async def stop_server(self) -> Dict[str, Any]: """Stop the Ollama server.""" try: status = await self.get_server_status() if status.status != "running": return { "success": True, "message": "Ollama server is not running.", "already_stopped": True } if status.pid is None: return { "success": False, "message": "Could not find the PID of the Ollama process.", "error": "pid_not_found" } # Terminate the process try: process = psutil.Process(status.pid) process.terminate() # Wait for the process to terminate (max 5 seconds) for i in range(5): await asyncio.sleep(1) if not process.is_running(): return { "success": True, "message": "Ollama server stopped successfully.", "shutdown_time_seconds": i + 1 } # If it didn't stop, force kill it process.kill() await asyncio.sleep(1) if not process.is_running(): return { "success": True, "message": "Ollama server stopped forcefully.", "forced_shutdown": True } else: return { "success": False, "message": "Failed to stop the Ollama server.", "error": "shutdown_failed" } except psutil.NoSuchProcess: return { "success": True, "message": "Ollama server was already stopped.", "already_stopped": True } except Exception as e: self.logger.error(f"Error stopping Ollama server: {e}") return { "success": False, "message": f"Error stopping server: {str(e)}", "error": str(e) } async def restart_server(self) -> Dict[str, Any]: """Restart the Ollama server.""" try: # First, stop the server stop_result = await self.stop_server() if not stop_result["success"] and not stop_result.get("already_stopped", False): return { "success": False, "message": "Could not stop the server for restart.", "error": stop_result.get("error", "stop_failed") } # Wait a moment before restarting await asyncio.sleep(2) # Then, start it again start_result = await self.start_server() if start_result["success"]: return { "success": True, "message": "Ollama server restarted successfully.", "restart_completed": True, "new_pid": start_result.get("pid") } else: return { "success": False, "message": "Restart failed: could not start the server.", "error": start_result.get("error", "restart_failed") } except Exception as e: self.logger.error(f"Error restarting Ollama server: {e}") return { "success": False, "message": f"Error restarting server: {str(e)}", "error": str(e) } def _get_install_guide(self) -> Dict[str, str]: """Returns an installation guide for the current OS.""" os_name = platform.system() if os_name == "Windows": return { "download_url": "https://ollama.com/download/windows", "instructions": "1. Download Ollama for Windows from the link above.\n2. Run the installer.\n3. Restart your terminal.\n4. Type: ollama serve" } elif os_name == "Darwin": # macOS return { "download_url": "https://ollama.com/download/mac", "instructions": "1. Download Ollama for macOS from the link above.\n2. Drag Ollama to your Applications folder.\n3. Open Terminal and type: ollama serve" } else: # Linux return { "download_url": "https://ollama.com/download/linux", "instructions": "1. Run: curl -fsSL https://ollama.com/install.sh | sh\n2. Or download manually from the link above.\n3. Start with: ollama serve" } def format_uptime(self, seconds: Optional[int]) -> str: """Formats uptime in a human-readable way.""" if seconds is None: return "N/A" if seconds < 60: return f"{seconds}s" elif seconds < 3600: minutes = seconds // 60 secs = seconds % 60 return f"{minutes}m {secs}s" else: hours = seconds // 3600 minutes = (seconds % 3600) // 60 return f"{hours}h {minutes}m" async def get_diagnostic_info(self) -> Dict[str, Any]: """Get comprehensive diagnostic information.""" diagnostic = { "ollama_installed": False, "ollama_in_path": False, "server_running": False, "port_accessible": False, "system_resources": {}, "recommendations": [] } try: # Check if Ollama is installed result = subprocess.run(["ollama", "--version"], capture_output=True, text=True, timeout=5) if result.returncode == 0: diagnostic["ollama_installed"] = True diagnostic["ollama_in_path"] = True diagnostic["ollama_version"] = result.stdout.strip() except (FileNotFoundError, subprocess.TimeoutExpired): diagnostic["recommendations"].append("Install Ollama from the official website.") # Check server status status = await self.get_server_status() diagnostic["server_running"] = status.status == "running" if status.status == "running": diagnostic["server_info"] = { "pid": status.pid, "uptime": self.format_uptime(status.uptime_seconds), "memory_mb": status.memory_mb, "cpu_percent": status.cpu_percent } else: diagnostic["recommendations"].append("Start the Ollama server with: ollama serve") # System information try: diagnostic["system_resources"] = { "cpu_count": psutil.cpu_count(), "memory_total_gb": psutil.virtual_memory().total / (1024**3), "memory_available_gb": psutil.virtual_memory().available / (1024**3), "disk_free_gb": psutil.disk_usage('.').free / (1024**3) } except Exception: pass return diagnostic

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/paolodalprato/ollama-mcp-server'

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