Skip to main content
Glama
config_migration.py4.83 kB
# # Copyright (C) 2024 Billy Bryant # Portions copyright (C) 2024 Sergey Parfenyuk (original MIT-licensed author) # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU Affero General Public License for more details. # # You should have received a copy of the GNU Affero General Public License # along with this program. If not, see <https://www.gnu.org/licenses/>. # # MIT License attribution: Portions of this file were originally licensed # under the MIT License by Sergey Parfenyuk (2024). # """Configuration directory migration utility. This module handles migrating from legacy configuration directory structure to XDG Base Directory Specification compliant paths. Legacy: ~/.foxxy-bridge/ New: ~/.config/foxxy-bridge/ """ import logging import os import shutil from pathlib import Path logger = logging.getLogger(__name__) def get_config_directories() -> tuple[Path, Path]: """Get legacy and new configuration directory paths. Returns: Tuple of (legacy_dir, new_dir) paths """ home = Path.home() legacy_dir = home / ".foxxy-bridge" # Check for custom OAuth config directory (for tests) oauth_config_dir = os.environ.get("MCP_OAUTH_CONFIG_DIR") if oauth_config_dir: custom_dir = Path(oauth_config_dir) # Validate path to prevent traversal custom_str = str(custom_dir) if ".." in custom_str and ("/../" in custom_str or custom_str.endswith("/..")): raise ValueError(f"Path traversal attempt detected: {custom_dir}") new_dir = custom_dir else: # Use XDG Base Directory Specification xdg_config_home = os.environ.get("XDG_CONFIG_HOME") new_dir = Path(xdg_config_home) / "foxxy-bridge" if xdg_config_home else home / ".config" / "foxxy-bridge" return legacy_dir, new_dir def needs_migration() -> bool: """Check if configuration migration is needed. Returns: True if migration is needed, False otherwise """ legacy_dir, new_dir = get_config_directories() # Migration needed if legacy directory exists and new directory doesn't return legacy_dir.exists() and not new_dir.exists() def migrate_config_directory() -> bool: """Migrate configuration directory from legacy to XDG location. Returns: True if migration was successful or not needed, False if failed """ if not needs_migration(): return True legacy_dir, new_dir = get_config_directories() try: logger.info("Migrating configuration directory from %s to %s", legacy_dir, new_dir) # Ensure parent directory exists new_dir.parent.mkdir(parents=True, exist_ok=True) # Move the entire directory shutil.move(str(legacy_dir), str(new_dir)) logger.info("Configuration directory migration completed successfully") return True except Exception: logger.exception("Configuration directory migration failed") logger.info("You may need to manually move %s to %s", legacy_dir, new_dir) return False def ensure_config_directory() -> Path: """Ensure configuration directory exists and migrate if needed. Returns: Path to the configuration directory """ # Attempt migration first migrate_config_directory() # Get the new directory path _, new_dir = get_config_directories() # Ensure it exists new_dir.mkdir(parents=True, exist_ok=True) return new_dir def get_config_dir() -> Path: """Get the main configuration directory for the application. This is the primary function that all modules should use to get the configuration directory. It handles migration automatically. Returns: Path to the configuration directory (~/.config/foxxy-bridge/) """ return ensure_config_directory() def get_auth_dir() -> Path: """Get the OAuth authentication directory. Returns: Path to the auth directory (~/.config/foxxy-bridge/auth/) """ return get_config_dir() / "auth" def get_logs_dir() -> Path: """Get the logs directory. Returns: Path to the logs directory (~/.config/foxxy-bridge/logs/) """ return get_config_dir() / "logs" def get_server_logs_dir() -> Path: """Get the MCP server logs directory. Returns: Path to the server logs directory (~/.config/foxxy-bridge/logs/mcp-servers/) """ return get_logs_dir() / "mcp-servers"

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/billyjbryant/mcp-foxxy-bridge'

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