Skip to main content
Glama
mcp_server_wrapper.py5.32 kB
#!/usr/bin/env python3 """ MCP Server Wrapper for auggie compatibility This wrapper ensures environment variables are properly inherited and the server starts correctly """ import os import sys from pathlib import Path # Robust autodiscovery: locate project root (ex-mcp-server), .venv, and server.py from any cwd cur = Path(__file__).resolve() # Try typical structure: scripts/ is under project root project_dir = cur.parent.parent if cur.parent.name == 'scripts' else cur.parent # Fallback: search upwards for server.py if not (project_dir / 'server.py').exists(): p = project_dir while p != p.parent: if (p / 'server.py').exists(): project_dir = p break p = p.parent # Ensure logs dir exists (project_dir / 'logs').mkdir(exist_ok=True) # Single-instance lock (PID file) to avoid duplicate servers try: import time, atexit lock_dir = project_dir / 'logs' lock_path = lock_dir / 'exai_server.pid' disable_lock = os.getenv('EXAI_LOCK_DISABLE', 'false').strip().lower() in {'1','true','yes','on'} if not disable_lock: stale_sec = float(os.getenv('EXAI_LOCK_STALE_SECONDS', '900')) # 15 minutes default pid_in_file: int | None = None if lock_path.exists(): try: # Try PID-aware check first txt = (lock_path.read_text(encoding='utf-8').strip() or '') pid_in_file = int(txt) if txt.isdigit() else None except Exception: pid_in_file = None # If we found a PID, attempt to verify process existence pid_active = False if pid_in_file is not None: try: try: import psutil # type: ignore pid_active = psutil.pid_exists(pid_in_file) except Exception: # Fallback: mtime-based check if psutil not available pid_active = False except Exception: pid_active = False if pid_in_file is not None and pid_active: # Active PID holds the lock; refuse to start with open(lock_dir / 'wrapper_error.log', 'a', encoding='utf-8') as f: f.write(f'Refusing to start: active EXAI MCP server appears to be running (pid={pid_in_file}). Set EXAI_LOCK_DISABLE=true to bypass.\n') sys.exit(1) else: # No active PID; check staleness window as final guard try: mtime = lock_path.stat().st_mtime if (time.time() - mtime) < stale_sec and pid_in_file is not None: # Recently created but PID not found (e.g., race) — still refuse briefly to avoid flapping with open(lock_dir / 'wrapper_error.log', 'a', encoding='utf-8') as f: f.write(f'Refusing to start briefly: recent lock file with missing pid={pid_in_file}. Wait or set EXAI_LOCK_STALE_SECONDS lower.\n') sys.exit(1) else: # Stale lock or no valid PID — remove and continue lock_path.unlink(missing_ok=True) except Exception: pass # Create lock and write PID with open(lock_path, 'w', encoding='utf-8') as lf: lf.write(str(os.getpid())) # Ensure cleanup on exit def _cleanup_lock(): try: if lock_path.exists(): lock_path.unlink() except Exception: pass atexit.register(_cleanup_lock) except Exception: # Never block startup due to lock errors pass # Prefer venv python if available — auto re-exec to ensure deps are available to MCP clients # This preserves stdio handles and environment; VS Code expects the server to start with all deps venv_py = project_dir / '.venv' / 'Scripts' / 'python.exe' if venv_py.exists() and sys.executable != str(venv_py): # preserve unbuffered mode for MCP stdio; keep same script path os.execv(str(venv_py), [str(venv_py), "-u", str(__file__)]) # Set working dir and sys.path os.chdir(project_dir) sys.path.insert(0, str(project_dir)) # Set Python path for child imports os.environ.setdefault('PYTHONPATH', str(project_dir)) # Logging defaults: prefer INFO for CLI/streaming contexts unless explicitly overridden if 'LOG_LEVEL' not in os.environ: if os.getenv('AUGGIE_CLI', '').strip().lower() == 'true' or os.getenv('STREAM_PROGRESS', 'true').strip().lower() == 'true': os.environ['LOG_LEVEL'] = 'INFO' else: os.environ['LOG_LEVEL'] = 'ERROR' # Import and run the server try: from server import run except Exception as e: with open(project_dir / "logs" / "wrapper_error.log", "a", encoding="utf-8") as f: f.write(f"Wrapper import error: {e}\n") sys.exit(1) if __name__ == "__main__": try: run() except Exception as e: # Write error and ensure a non-zero exit so MCP client shows 'Server disconnected' with cause with open(project_dir / "logs" / "wrapper_error.log", "a", encoding="utf-8") as f: f.write(f"Wrapper run error: {e}\n") sys.exit(1)

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/Zazzles2908/EX_AI-mcp-server'

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