Skip to main content
Glama

Neolibrarian MCP

by pshap
calibre_config.py6.06 kB
"""Calibre Configuration Management Handles configuration loading, validation, and path management. """ import json import logging from pathlib import Path from typing import Dict, Any logger = logging.getLogger(__name__) class CalibreConfig: """Manages Calibre client configuration and validation.""" def __init__(self, config_path: str = "config.json"): """Initialize configuration from file. Resolution order: 1) Environment variable CALIBRE_CONFIG_PATH (if set) 2) Provided config_path 3) If the above does not exist and ends with config.json, try sibling config.local.json """ self.config_path = config_path self.config = self._load_config() self.library_path = Path(self.config["calibre_library_path"]) self.calibre_path = Path(self.config["calibre_installation_path"]) self.calibre_debug = self.calibre_path / "calibre-debug.exe" self._validate_setup() logger.info(f"Configuration loaded for library: {self.library_path}") def _resolve_config_path(self) -> Path: """Resolve the configuration file path with sensible fallbacks.""" import os # 1) Environment override env_path = os.environ.get("CALIBRE_CONFIG_PATH") if env_path and env_path.strip(): p = Path(os.path.expanduser(os.path.expandvars(env_path.strip()))) if p.exists(): self.config_path = str(p) return p # 2) Provided path (with local override preference) p = Path(self.config_path) # If the provided path looks like config.json and a sibling config.local.json exists, # prefer the local override even if config.json exists. This avoids using placeholder # defaults when a real local config is present. try: if p.name == "config.json": candidate = p.with_name("config.local.json") if candidate.exists(): self.config_path = str(candidate) return candidate except Exception: pass if p.exists(): return p # 3) As a final fallback, if provided path ends with config.json, try sibling config.local.json try: if p.name == "config.json": candidate = p.with_name("config.local.json") if candidate.exists(): self.config_path = str(candidate) return candidate except Exception: pass return p def _load_config(self) -> Dict[str, Any]: """Load configuration from JSON file.""" config_file = self._resolve_config_path() if not config_file.exists(): raise FileNotFoundError(f"Config file not found: {config_file}") with config_file.open('r', encoding='utf-8') as f: return json.load(f) def _validate_setup(self) -> None: """Validate Calibre installation and library paths.""" if not self.calibre_debug.exists(): raise FileNotFoundError(f"Calibre executable not found: {self.calibre_debug}") if not self.library_path.exists(): raise FileNotFoundError(f"Library path not found: {self.library_path}") metadata_db = self.library_path / "metadata.db" if not metadata_db.exists(): raise FileNotFoundError(f"Calibre library database not found: {metadata_db}") def get_metadata_db_path(self) -> Path: """Get path to the Calibre metadata database.""" return self.library_path / "metadata.db" def get_fts_db_path(self) -> Path: """Get path to the Calibre full-text search database.""" # Check if FTS path is explicitly configured if "calibre_fts_db_path" in self.config: return Path(self.config["calibre_fts_db_path"]) # Default to library path + full-text-search.db return self.library_path / "full-text-search.db" def has_fts_database(self) -> bool: """Check if full-text search database exists.""" return self.get_fts_db_path().exists() def ping(self) -> Dict[str, Any]: """Test configuration and paths.""" try: calibre_exists = self.calibre_debug.exists() library_exists = self.library_path.exists() metadata_db_exists = self.get_metadata_db_path().exists() fts_db_exists = self.has_fts_database() if calibre_exists and library_exists and metadata_db_exists: return { "status": "connected", "library_path": str(self.library_path), "message": "All paths exist and accessible", "calibre_executable": str(self.calibre_debug), "fts_database_path": str(self.get_fts_db_path()), "checks": { "calibre_exe": calibre_exists, "library_path": library_exists, "metadata_db": metadata_db_exists, "fts_db": fts_db_exists } } else: return { "status": "error", "library_path": str(self.library_path), "message": "Some required paths are missing", "fts_database_path": str(self.get_fts_db_path()), "checks": { "calibre_exe": calibre_exists, "library_path": library_exists, "metadata_db": metadata_db_exists, "fts_db": fts_db_exists } } except Exception as e: return { "status": "error", "library_path": str(self.library_path), "message": f"Ping failed: {str(e)}" }

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/pshap/mcp-neolibrarian'

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