import os import logging import subprocess logger = logging.getLogger('gaze') # Path to the MCP docker-compose projects, relative to the main app file. MCP_TOOLS_DIR = os.path.join(os.path.dirname(os.path.dirname(os.path.abspath(__file__))), 'tools') MCP_COMPOSE_DIR = os.path.join(MCP_TOOLS_DIR, 'danbooru-mcp') MCP_REPO_URL = 'https://git.liveaodh.com/aodhan/danbooru-mcp' CHAR_MCP_COMPOSE_DIR = os.path.join(MCP_TOOLS_DIR, 'character-mcp') CHAR_MCP_REPO_URL = 'https://git.liveaodh.com/aodhan/character-mcp.git' def _ensure_repo(compose_dir, repo_url, name): """Clone or update an MCP source repository inside tools/. - If the directory does not exist, clone from repo_url. - If it already exists, run ``git pull`` to fetch the latest changes. Errors are non-fatal. """ os.makedirs(MCP_TOOLS_DIR, exist_ok=True) try: if not os.path.isdir(compose_dir): logger.info('Cloning %s from %s …', name, repo_url) subprocess.run( ['git', 'clone', repo_url, compose_dir], timeout=120, check=True, ) logger.info('%s cloned successfully.', name) else: logger.info('Updating %s via git pull …', name) subprocess.run( ['git', 'pull'], cwd=compose_dir, timeout=60, check=True, ) logger.info('%s updated.', name) except FileNotFoundError: logger.warning('git not found on PATH — %s repo will not be cloned/updated.', name) except subprocess.CalledProcessError as e: logger.warning('git operation failed for %s: %s', name, e) except subprocess.TimeoutExpired: logger.warning('git timed out while cloning/updating %s.', name) except Exception as e: logger.warning('Could not clone/update %s repo: %s', name, e) def _ensure_server_running(compose_dir, repo_url, container_name, name): """Ensure an MCP repo is present/up-to-date, then start the Docker container if it is not already running. Uses ``docker compose up -d`` so the image is built automatically on first run. Errors are non-fatal — the app will still start even if Docker is unavailable. Skipped when ``SKIP_MCP_AUTOSTART=true`` (set by docker-compose, where the MCP service is managed by compose instead). """ if os.environ.get('SKIP_MCP_AUTOSTART', '').lower() == 'true': logger.info('SKIP_MCP_AUTOSTART set — skipping %s auto-start.', name) return _ensure_repo(compose_dir, repo_url, name) try: result = subprocess.run( ['docker', 'ps', '--filter', f'name={container_name}', '--format', '{{.Names}}'], capture_output=True, text=True, timeout=10, ) if container_name in result.stdout: logger.info('%s container already running.', name) return logger.info('Starting %s container via docker compose …', name) subprocess.run( ['docker', 'compose', 'up', '-d'], cwd=compose_dir, timeout=120, ) logger.info('%s container started.', name) except FileNotFoundError: logger.warning('docker not found on PATH — %s will not be started automatically.', name) except subprocess.TimeoutExpired: logger.warning('docker timed out while starting %s.', name) except Exception as e: logger.warning('Could not ensure %s is running: %s', name, e) def ensure_mcp_server_running(): """Ensure the danbooru-mcp Docker container is running.""" _ensure_server_running(MCP_COMPOSE_DIR, MCP_REPO_URL, 'danbooru-mcp', 'danbooru-mcp') def ensure_character_mcp_server_running(): """Ensure the character-mcp Docker container is running.""" _ensure_server_running(CHAR_MCP_COMPOSE_DIR, CHAR_MCP_REPO_URL, 'character-mcp', 'character-mcp')