Remove unused MCP resources and update documentation

- Remove array_status, system_info, notifications_overview, and parity_status resources
- Keep only logs_stream resource (unraid://logs/stream) which is working properly
- Update README.md with current resource documentation and modern docker compose syntax
- Fix import path issues that were causing subscription errors
- Update environment configuration examples
- Clean up subscription manager to only include working log streaming

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
Jacob Magar
2025-08-11 14:19:27 -04:00
parent f355511fe6
commit b00d78f408
29 changed files with 3641 additions and 2561 deletions

View File

@@ -0,0 +1 @@
"""Configuration management for Unraid MCP Server."""

View File

@@ -0,0 +1,92 @@
"""Logging configuration for Unraid MCP Server.
This module sets up structured logging with console and rotating file handlers
that can be used consistently across all modules.
"""
import logging
import sys
from logging.handlers import RotatingFileHandler
from .settings import LOG_LEVEL_STR, LOG_FILE_PATH
def setup_logger(name: str = "UnraidMCPServer") -> logging.Logger:
"""Set up and configure the logger with console and file handlers.
Args:
name: Logger name (defaults to UnraidMCPServer)
Returns:
Configured logger instance
"""
# Get numeric log level
numeric_log_level = getattr(logging, LOG_LEVEL_STR, logging.INFO)
# Define the logger
logger = logging.getLogger(name)
logger.setLevel(numeric_log_level)
logger.propagate = False # Prevent root logger from duplicating handlers
# Clear any existing handlers
logger.handlers.clear()
# Console Handler
console_handler = logging.StreamHandler(sys.stdout)
console_handler.setLevel(numeric_log_level)
console_formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
console_handler.setFormatter(console_formatter)
logger.addHandler(console_handler)
# File Handler with Rotation
# Rotate logs at 5MB, keep 3 backup logs
file_handler = RotatingFileHandler(
LOG_FILE_PATH,
maxBytes=5*1024*1024,
backupCount=3,
encoding='utf-8'
)
file_handler.setLevel(numeric_log_level)
file_formatter = logging.Formatter(
'%(asctime)s - %(name)s - %(levelname)s - %(module)s - %(funcName)s - %(lineno)d - %(message)s'
)
file_handler.setFormatter(file_formatter)
logger.addHandler(file_handler)
return logger
def log_configuration_status(logger: logging.Logger) -> None:
"""Log configuration status at startup.
Args:
logger: Logger instance to use for logging
"""
from .settings import get_config_summary
logger.info(f"Logging initialized (console and file: {LOG_FILE_PATH}).")
config = get_config_summary()
# Log configuration status
if config['api_url_configured']:
logger.info(f"UNRAID_API_URL loaded: {config['api_url_preview']}")
else:
logger.warning("UNRAID_API_URL not found in environment or .env file.")
if config['api_key_configured']:
logger.info("UNRAID_API_KEY loaded: ****") # Don't log the key itself
else:
logger.warning("UNRAID_API_KEY not found in environment or .env file.")
logger.info(f"UNRAID_MCP_PORT set to: {config['server_port']}")
logger.info(f"UNRAID_MCP_HOST set to: {config['server_host']}")
logger.info(f"UNRAID_MCP_TRANSPORT set to: {config['transport']}")
logger.info(f"UNRAID_MCP_LOG_LEVEL set to: {config['log_level']}")
if not config['config_valid']:
logger.error(f"Missing required configuration: {config['missing_config']}")
# Global logger instance - modules can import this directly
logger = setup_logger()

View File

@@ -0,0 +1,104 @@
"""Configuration management for Unraid MCP Server.
This module handles loading environment variables from multiple .env locations
and provides all configuration constants used throughout the application.
"""
import os
from pathlib import Path
from typing import Union
from dotenv import load_dotenv
# Get the script directory (config module location)
SCRIPT_DIR = Path(__file__).parent # /home/user/code/unraid-mcp/unraid_mcp/config/
UNRAID_MCP_DIR = SCRIPT_DIR.parent # /home/user/code/unraid-mcp/unraid_mcp/
PROJECT_ROOT = UNRAID_MCP_DIR.parent # /home/user/code/unraid-mcp/
# Load environment variables from .env file
# In container: First try /app/.env.local (mounted), then project root .env
dotenv_paths = [
Path('/app/.env.local'), # Container mount point
PROJECT_ROOT / '.env.local', # Project root .env.local
PROJECT_ROOT / '.env', # Project root .env
UNRAID_MCP_DIR / '.env' # Local .env in unraid_mcp/
]
for dotenv_path in dotenv_paths:
if dotenv_path.exists():
load_dotenv(dotenv_path=dotenv_path)
break
# Core API Configuration
UNRAID_API_URL = os.getenv("UNRAID_API_URL")
UNRAID_API_KEY = os.getenv("UNRAID_API_KEY")
# Server Configuration
UNRAID_MCP_PORT = int(os.getenv("UNRAID_MCP_PORT", "6970"))
UNRAID_MCP_HOST = os.getenv("UNRAID_MCP_HOST", "0.0.0.0")
UNRAID_MCP_TRANSPORT = os.getenv("UNRAID_MCP_TRANSPORT", "streamable-http").lower()
# SSL Configuration
raw_verify_ssl = os.getenv("UNRAID_VERIFY_SSL", "true").lower()
if raw_verify_ssl in ["false", "0", "no"]:
UNRAID_VERIFY_SSL: Union[bool, str] = False
elif raw_verify_ssl in ["true", "1", "yes"]:
UNRAID_VERIFY_SSL = True
else: # Path to CA bundle
UNRAID_VERIFY_SSL = raw_verify_ssl
# Logging Configuration
LOG_LEVEL_STR = os.getenv('UNRAID_MCP_LOG_LEVEL', 'INFO').upper()
LOG_FILE_NAME = os.getenv("UNRAID_MCP_LOG_FILE", "unraid-mcp.log")
LOGS_DIR = PROJECT_ROOT / "logs"
LOG_FILE_PATH = LOGS_DIR / LOG_FILE_NAME
# Ensure logs directory exists
LOGS_DIR.mkdir(parents=True, exist_ok=True)
# HTTP Client Configuration
TIMEOUT_CONFIG = {
'default': 30,
'disk_operations': 90, # Longer timeout for SMART data queries
}
def validate_required_config() -> bool:
"""Validate that required configuration is present.
Returns:
bool: True if all required config is present, False otherwise.
"""
required_vars = [
("UNRAID_API_URL", UNRAID_API_URL),
("UNRAID_API_KEY", UNRAID_API_KEY)
]
missing = []
for name, value in required_vars:
if not value:
missing.append(name)
return len(missing) == 0, missing
def get_config_summary() -> dict:
"""Get a summary of current configuration (safe for logging).
Returns:
dict: Configuration summary with sensitive data redacted.
"""
is_valid, missing = validate_required_config()
return {
'api_url_configured': bool(UNRAID_API_URL),
'api_url_preview': UNRAID_API_URL[:20] + '...' if UNRAID_API_URL else None,
'api_key_configured': bool(UNRAID_API_KEY),
'server_host': UNRAID_MCP_HOST,
'server_port': UNRAID_MCP_PORT,
'transport': UNRAID_MCP_TRANSPORT,
'ssl_verify': UNRAID_VERIFY_SSL,
'log_level': LOG_LEVEL_STR,
'log_file': str(LOG_FILE_PATH),
'config_valid': is_valid,
'missing_config': missing if not is_valid else None
}