config.py•6.5 kB
"""
Configuration management for the MCP server
Handles loading configuration from environment variables and files
"""
import os
import json
import logging
from typing import Dict, Any, Optional
from pathlib import Path
try:
from .error_handler import ConfigurationError
except ImportError:
# Handle case when run as script
import sys
import os
sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
from utils.error_handler import ConfigurationError
logger = logging.getLogger(__name__)
def load_config() -> Dict[str, Any]:
"""
Load configuration from environment variables or config file
Environment variables take precedence over config file
"""
config = {}
# Try to load from environment variables first (more secure)
jira_config = load_jira_config_from_env()
github_config = load_github_config_from_env()
if jira_config and github_config:
config = {
"jira": jira_config,
"github": github_config
}
logger.info("Configuration loaded from environment variables")
else:
# Fall back to config file
config = load_config_from_file()
logger.warning("Configuration loaded from file - consider using environment variables for better security")
# Validate configuration
validate_config(config)
return config
def load_jira_config_from_env() -> Optional[Dict[str, str]]:
"""Load Jira configuration from environment variables"""
base_url = os.getenv("JIRA_BASE_URL")
email = os.getenv("JIRA_EMAIL")
api_token = os.getenv("JIRA_API_TOKEN")
if all([base_url, email, api_token]):
return {
"base_url": base_url,
"email": email,
"api_token": api_token
}
return None
def load_github_config_from_env() -> Optional[Dict[str, str]]:
"""Load GitHub configuration from environment variables"""
base_url = os.getenv("GITHUB_BASE_URL", "https://api.github.com")
access_token = os.getenv("GITHUB_ACCESS_TOKEN")
if access_token:
return {
"base_url": base_url,
"access_token": access_token
}
return None
def load_config_from_file() -> Dict[str, Any]:
"""Load configuration from config.json file"""
config_path = Path("config.json")
if not config_path.exists():
raise ConfigurationError(
"Configuration file 'config.json' not found and environment variables not set",
{"config_path": str(config_path)}
)
try:
with open(config_path, 'r') as f:
config = json.load(f)
logger.info(f"Configuration loaded from {config_path}")
return config
except json.JSONDecodeError as e:
raise ConfigurationError(
f"Invalid JSON in configuration file: {str(e)}",
{"config_path": str(config_path), "json_error": str(e)}
)
except Exception as e:
raise ConfigurationError(
f"Failed to load configuration file: {str(e)}",
{"config_path": str(config_path)}
)
def validate_config(config: Dict[str, Any]) -> None:
"""Validate the loaded configuration"""
# Check for required top-level keys
required_keys = ["jira", "github"]
missing_keys = [key for key in required_keys if key not in config]
if missing_keys:
raise ConfigurationError(
f"Missing required configuration sections: {', '.join(missing_keys)}",
{"missing_keys": missing_keys}
)
# Validate Jira configuration
jira_config = config["jira"]
required_jira_fields = ["base_url", "email", "api_token"]
missing_jira_fields = [field for field in required_jira_fields if field not in jira_config or not jira_config[field]]
if missing_jira_fields:
raise ConfigurationError(
f"Missing required Jira configuration fields: {', '.join(missing_jira_fields)}",
{"missing_fields": missing_jira_fields, "section": "jira"}
)
# Validate GitHub configuration
github_config = config["github"]
required_github_fields = ["base_url", "access_token"]
missing_github_fields = [field for field in required_github_fields if field not in github_config or not github_config[field]]
if missing_github_fields:
raise ConfigurationError(
f"Missing required GitHub configuration fields: {', '.join(missing_github_fields)}",
{"missing_fields": missing_github_fields, "section": "github"}
)
# Validate URL formats
if not jira_config["base_url"].startswith(("http://", "https://")):
raise ConfigurationError(
"Jira base_url must start with http:// or https://",
{"invalid_url": jira_config["base_url"]}
)
if not github_config["base_url"].startswith(("http://", "https://")):
raise ConfigurationError(
"GitHub base_url must start with http:// or https://",
{"invalid_url": github_config["base_url"]}
)
logger.info("Configuration validation passed")
def get_env_template() -> str:
"""
Return a template for environment variables
Useful for documentation and setup
"""
return """
# Jira Configuration
JIRA_BASE_URL=https://yourcompany.atlassian.net
JIRA_EMAIL=your-email@example.com
JIRA_API_TOKEN=your-jira-api-token
# GitHub Configuration
GITHUB_BASE_URL=https://api.github.com
GITHUB_ACCESS_TOKEN=your-github-personal-access-token
"""
def create_sample_config() -> None:
"""Create a sample config.json file"""
sample_config = {
"jira": {
"base_url": "https://yourcompany.atlassian.net",
"email": "your-email@example.com",
"api_token": "your-jira-api-token"
},
"github": {
"base_url": "https://api.github.com",
"access_token": "your-github-personal-access-token"
}
}
config_path = Path("config.json.sample")
with open(config_path, 'w') as f:
json.dump(sample_config, f, indent=2)
logger.info(f"Sample configuration created at {config_path}")
if __name__ == "__main__":
# Create sample config when run directly
create_sample_config()
print("Sample configuration file created: config.json.sample")
print("\nEnvironment variables template:")
print(get_env_template())