Skip to main content
Glama

Voice Mode

by mbailey
uninstall.py•9.12 kB
"""Uninstall tool for Kokoro TTS service.""" import os import shutil import subprocess import platform import logging from pathlib import Path from typing import Dict, Any, Union from voice_mode.server import mcp from voice_mode.config import BASE_DIR from voice_mode.utils.services.common import find_process_by_port logger = logging.getLogger("voicemode") @mcp.tool() async def kokoro_uninstall( remove_models: Union[bool, str] = False, remove_all_data: Union[bool, str] = False ) -> Dict[str, Any]: """Uninstall kokoro-fastapi and optionally remove models and data. This tool will: 1. Stop any running Kokoro service 2. Remove service configurations (launchd/systemd) 3. Remove the kokoro-fastapi installation 4. Optionally remove downloaded Kokoro models 5. Optionally remove all Kokoro-related data Args: remove_models: Also remove downloaded Kokoro models (default: False) remove_all_data: Remove all Kokoro data including logs and cache (default: False) Returns: Dictionary with uninstall status and details """ system = platform.system() removed_items = [] errors = [] try: # 1. Stop any running Kokoro service logger.info("Checking for running Kokoro service...") proc = find_process_by_port(8880) if proc: try: logger.info(f"Stopping Kokoro service (PID: {proc.pid})...") proc.terminate() proc.wait(timeout=5) removed_items.append("Stopped running Kokoro service") except Exception as e: logger.warning(f"Failed to stop Kokoro service: {e}") # 2. Remove service configurations if system == "Darwin": # Remove launchd plist plist_path = Path.home() / "Library" / "LaunchAgents" / "com.voicemode.kokoro.plist" if plist_path.exists(): try: # Unload if loaded subprocess.run( ["launchctl", "unload", str(plist_path)], capture_output=True ) # Remove file plist_path.unlink() removed_items.append("Removed launchd configuration") logger.info(f"Removed {plist_path}") except Exception as e: errors.append(f"Failed to remove {plist_path}: {e}") # Also check for any port-specific instances launchctl_result = subprocess.run( ["launchctl", "list"], capture_output=True, text=True ) if launchctl_result.returncode == 0: for line in launchctl_result.stdout.splitlines(): if "com.voicemode.kokoro-" in line: parts = line.split() if len(parts) >= 3: service_name = parts[2] try: subprocess.run( ["launchctl", "remove", service_name], capture_output=True ) removed_items.append(f"Removed launchctl instance: {service_name}") except Exception: pass elif system == "Linux": # Remove systemd service service_path = Path.home() / ".config" / "systemd" / "user" / "voicemode-kokoro.service" if service_path.exists(): try: # Stop and disable service subprocess.run( ["systemctl", "--user", "stop", "voicemode-kokoro.service"], capture_output=True ) subprocess.run( ["systemctl", "--user", "disable", "voicemode-kokoro.service"], capture_output=True ) # Remove file service_path.unlink() # Reload systemd subprocess.run(["systemctl", "--user", "daemon-reload"], check=True) removed_items.append("Removed systemd service") logger.info(f"Removed {service_path}") except Exception as e: errors.append(f"Failed to remove {service_path}: {e}") # 3. Remove kokoro-fastapi installation # Check new location first, then legacy location kokoro_dirs = [ BASE_DIR / "services" / "kokoro", # New location BASE_DIR / "kokoro-fastapi" # Legacy location ] for kokoro_dir in kokoro_dirs: if kokoro_dir.exists(): try: shutil.rmtree(kokoro_dir) removed_items.append(f"Removed kokoro-fastapi installation: {kokoro_dir}") logger.info(f"Removed {kokoro_dir}") except Exception as e: errors.append(f"Failed to remove {kokoro_dir}: {e}") # 4. Optionally remove models if remove_models: # Kokoro models directory models_dir = BASE_DIR / "models" / "kokoro" if models_dir.exists(): try: shutil.rmtree(models_dir) removed_items.append(f"Removed Kokoro models: {models_dir}") logger.info(f"Removed {models_dir}") except Exception as e: errors.append(f"Failed to remove {models_dir}: {e}") # Also check for kokoro-models directory kokoro_models_dir = BASE_DIR / "kokoro-models" if kokoro_models_dir.exists(): try: shutil.rmtree(kokoro_models_dir) removed_items.append(f"Removed Kokoro models: {kokoro_models_dir}") logger.info(f"Removed {kokoro_models_dir}") except Exception as e: errors.append(f"Failed to remove {kokoro_models_dir}: {e}") # 5. Optionally remove all data if remove_all_data: # Remove logs log_files = [ BASE_DIR / "logs" / "kokoro.out.log", BASE_DIR / "logs" / "kokoro.err.log", BASE_DIR / "kokoro.log", BASE_DIR / "kokoro.stdout.log", BASE_DIR / "kokoro.stderr.log" ] for log_file in log_files: if log_file.exists(): try: log_file.unlink() removed_items.append(f"Removed log file: {log_file.name}") except Exception as e: errors.append(f"Failed to remove {log_file}: {e}") # Remove cache cache_dir = BASE_DIR / "cache" / "kokoro" if cache_dir.exists(): try: shutil.rmtree(cache_dir) removed_items.append(f"Removed Kokoro cache: {cache_dir}") logger.info(f"Removed {cache_dir}") except Exception as e: errors.append(f"Failed to remove {cache_dir}: {e}") # Remove any kokoro-specific audio files audio_dir = BASE_DIR / "audio" if audio_dir.exists(): for file in audio_dir.glob("kokoro_*.wav"): try: file.unlink() removed_items.append(f"Removed audio file: {file.name}") except Exception as e: errors.append(f"Failed to remove {file}: {e}") # Prepare response success = len(errors) == 0 if success: message = "Kokoro has been successfully uninstalled" if remove_models: message += " (including models)" if remove_all_data: message += " (including all data)" else: message = "Kokoro uninstall completed with some errors" return { "success": success, "message": message, "removed_items": removed_items, "errors": errors, "summary": { "items_removed": len(removed_items), "errors_encountered": len(errors) } } except Exception as e: logger.error(f"Unexpected error during Kokoro uninstall: {e}") return { "success": False, "message": f"Failed to uninstall Kokoro: {str(e)}", "removed_items": removed_items, "errors": errors + [str(e)], "summary": { "items_removed": len(removed_items), "errors_encountered": len(errors) + 1 } }

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/mbailey/voicemode'

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