Skip to main content
Glama
XRDS76354

SUMO-MCP Server

by XRDS76354
sumo.py5.79 kB
import glob import logging import os import shutil import sys from pathlib import Path from typing import Optional import sumolib logger = logging.getLogger(__name__) def find_sumo_binary(name: str) -> Optional[str]: """ Find a SUMO binary by name. Resolution order: 1) `sumolib.checkBinary()` (respects SUMO_HOME when set) 2) `shutil.which()` (respects PATH) Returns: The resolved absolute executable path, or None if it cannot be located. """ resolved: Optional[str] = None try: candidate = sumolib.checkBinary(name) except (SystemExit, OSError, FileNotFoundError, ImportError) as exc: logger.debug("sumolib.checkBinary failed for %s: %s", name, exc) candidate = None if candidate and candidate != name: resolved = candidate if resolved: # Trust sumolib's result if it looks like an absolute path # Use os.path.isabs for cross-platform compatibility (handles /usr/bin on Windows) if os.path.isabs(resolved): return resolved return shutil.which(resolved) return shutil.which(name) def _candidate_sumo_home_from_binary(sumo_binary: Optional[str]) -> Optional[Path]: if not sumo_binary: return None path = Path(sumo_binary) if not path.is_absolute(): return None # Typical layout: <SUMO_HOME>/bin/sumo(.exe) if path.parent.name.lower() == "bin": return path.parent.parent return None def find_sumo_home() -> Optional[str]: """ Resolve SUMO_HOME. Priority: 1) SUMO_HOME environment variable 2) Derive from `sumo` executable location when it matches <SUMO_HOME>/bin/sumo 3) Platform-specific common locations """ env_home = os.environ.get("SUMO_HOME") if env_home: home = Path(env_home).expanduser() if home.exists(): logger.debug("Resolved SUMO_HOME from env: %s", home) return str(home) logger.debug("SUMO_HOME env set but path does not exist: %s", home) sumo_binary = find_sumo_binary("sumo") candidate = _candidate_sumo_home_from_binary(sumo_binary) if candidate and candidate.exists(): logger.debug("Resolved SUMO_HOME from sumo binary: %s", candidate) return str(candidate) if sys.platform == "win32": win_paths = [ Path("C:/Program Files/Eclipse/sumo"), Path("C:/Program Files (x86)/Eclipse/sumo"), Path("D:/sumo"), Path("C:/sumo"), ] for path in win_paths: if path.exists() and (path / "tools").exists(): logger.debug("Resolved SUMO_HOME from Windows common paths: %s", path) return str(path) # Windows Registry (optional) try: import winreg key_path = r"SOFTWARE\Eclipse\SUMO" for hive in (winreg.HKEY_LOCAL_MACHINE, winreg.HKEY_CURRENT_USER): try: key = winreg.OpenKey(hive, key_path) install_path, _ = winreg.QueryValueEx(key, "InstallPath") winreg.CloseKey(key) if not install_path: continue reg_home = Path(install_path) if reg_home.exists() and (reg_home / "tools").exists(): logger.debug("Resolved SUMO_HOME from Windows Registry: %s", reg_home) return str(reg_home) except FileNotFoundError: continue except OSError: continue except ImportError: pass if sys.platform == "darwin": patterns = [ "/usr/local/Cellar/sumo/*/share/sumo", "/opt/homebrew/Cellar/sumo/*/share/sumo", ] matches: list[str] = [] for pattern in patterns: matches.extend(glob.glob(pattern)) for raw in sorted(matches, reverse=True): home = Path(raw) if home.exists() and (home / "tools").exists(): logger.debug("Resolved SUMO_HOME from Homebrew cellar: %s", home) return str(home) linux_home = Path("/usr/share/sumo") if linux_home.exists() and (linux_home / "tools").exists(): logger.debug("Resolved SUMO_HOME from Linux common path: %s", linux_home) return str(linux_home) return None def find_sumo_tools_dir() -> Optional[str]: """Return the SUMO tools directory if it can be located.""" sumo_home = find_sumo_home() if not sumo_home: return None tools_dir = Path(sumo_home) / "tools" if tools_dir.exists(): return str(tools_dir) return None def find_sumo_tool_script(script_name: str) -> Optional[str]: """Find a SUMO python tool script (e.g. randomTrips.py) under SUMO tools dir.""" tools_dir = find_sumo_tools_dir() if not tools_dir: return None script = Path(tools_dir) / script_name if script.exists(): return str(script) return None def build_sumo_diagnostics(binary_name: str = "sumo") -> str: """ Build a short, multi-line diagnostic string about SUMO discovery. This is intended for inclusion in user-facing error messages. """ env_home = os.environ.get("SUMO_HOME") or "Not Set" which_bin = shutil.which(binary_name) or "Not Found" detected_home = find_sumo_home() or "Not Found" tools_dir = find_sumo_tools_dir() or "Not Found" return "\n".join( [ "Diagnostics:", f" - SUMO_HOME env: {env_home}", f" - which({binary_name}): {which_bin}", f" - find_sumo_home(): {detected_home}", f" - find_sumo_tools_dir(): {tools_dir}", ] )

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/XRDS76354/SUMO-MCP-Server'

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