"""
Configuration management for ThePornDB MCP Service.
Handles CLI argument parsing, logging configuration, and API token management.
"""
import argparse
import logging
import os
import sys
from typing import Optional
# Default public token for ThePornDB API
DEFAULT_PUBLIC_TOKEN = "Bearer 2w6yveu23t4j3t8uvegvt9ve"
# ThePornDB API base URL
API_BASE_URL = "https://api.theporndb.net"
def setup_logging(log_level: str) -> None:
"""
Configure logging with appropriate level and format.
Args:
log_level: Logging level (DEBUG, INFO, WARNING, ERROR)
"""
level = getattr(logging, log_level.upper(), logging.INFO)
logging.basicConfig(
level=level,
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
datefmt='%Y-%m-%d %H:%M:%S'
)
logger = logging.getLogger(__name__)
logger.info(f"Logging initialized at {log_level} level")
def parse_args() -> argparse.Namespace:
"""
Parse command-line arguments.
Returns:
Parsed arguments namespace
"""
parser = argparse.ArgumentParser(
description="ThePornDB MCP Server - Exposes ThePornDB API via Model Context Protocol",
formatter_class=argparse.RawDescriptionHelpFormatter,
epilog="""
Examples:
# Start with your API token
theporndb-mcp --token=your_api_token_here
# Start with environment variable
THEPORNDB_API_KEY=your_token theporndb-mcp
# Start with debug logging
theporndb-mcp --token=your_token --log-level=DEBUG
# Start with HTTP transport
theporndb-mcp --token=your_token --transport=streamable-http
# Start with default public token
theporndb-mcp
"""
)
parser.add_argument(
'--token',
type=str,
default=None,
help='ThePornDB API token (uses default public token if not provided)'
)
parser.add_argument(
'--log-level',
type=str,
default='INFO',
choices=['DEBUG', 'INFO', 'WARNING', 'ERROR'],
help='Logging level (default: INFO)'
)
parser.add_argument(
'--transport',
type=str,
default='stdio',
choices=['stdio', 'streamable-http'],
help='MCP transport type (default: stdio)'
)
return parser.parse_args()
class Config:
"""
Application configuration.
Manages API token, logging, and other settings.
"""
def __init__(self, args: argparse.Namespace):
"""
Initialize configuration from CLI arguments and environment variables.
Priority:
1. Environment variable THEPORNDB_API_KEY
2. Command-line argument --token
3. Default public token
Args:
args: Parsed command-line arguments
"""
self.args = args
# Set API token (priority: env var > CLI arg > default)
env_token = os.getenv('THEPORNDB_API_KEY')
if env_token:
self.token = env_token
self.token_source = "environment variable"
elif args.token:
self.token = args.token
self.token_source = "command-line argument"
else:
self.token = DEFAULT_PUBLIC_TOKEN
self.token_source = "default public token"
# Set logging level
self.log_level = args.log_level
# Set transport type
self.transport = args.transport
# Setup logging
setup_logging(self.log_level)
self._log_token_info()
def _log_token_info(self) -> None:
"""
Log token presence and validation status without exposing the token value.
Security: NEVER logs the actual token value, only source.
"""
logger = logging.getLogger(__name__)
logger.info(f"Token source: {self.token_source}")
def get_auth_headers(self) -> dict:
"""
Get authentication headers for API requests.
Returns:
Dictionary with Authorization header
"""
return {
'Authorization': f'Bearer {self.token}',
'Accept': 'application/json'
}
def log_request(self, url: str) -> None:
"""
Log API request at INFO level (without token).
Args:
url: Request URL
"""
logger = logging.getLogger(__name__)
logger.info(f"API Request: {url}")
def log_request_debug(self, url: str, headers: dict) -> None:
"""
Log API request at DEBUG level with headers (token redacted).
Args:
url: Request URL
headers: Request headers
"""
logger = logging.getLogger(__name__)
# Redact Authorization header for security
redacted_headers = headers.copy()
if 'Authorization' in redacted_headers:
redacted_headers['Authorization'] = 'Bearer **REDACTED**'
logger.debug(f"Request URL: {url}")
logger.debug(f"Request Headers: {redacted_headers}")
def log_response(self, status_code: int, result_count: Optional[int] = None) -> None:
"""
Log API response at INFO level.
Args:
status_code: HTTP status code
result_count: Number of results returned (if applicable)
"""
logger = logging.getLogger(__name__)
if result_count is not None:
logger.info(f"API Response: status={status_code}, results={result_count}")
else:
logger.info(f"API Response: status={status_code}")
def log_error(self, error: Exception) -> None:
"""
Log error at ERROR level.
Args:
error: Exception to log
"""
logger = logging.getLogger(__name__)
logger.error(f"Error: {type(error).__name__}: {error}")