config.py•5.52 kB
# config.py
#
# Configuration Management for Alpaca MCP Server
# Location: /src/alpaca_mcp_server/config.py
# Purpose: Handles environment configuration, API keys, and .env file management
import os
from pathlib import Path
from typing import Optional, Dict, Any
from dotenv import load_dotenv
class ConfigManager:
"""
Manages configuration for the Alpaca MCP Server.
Handles loading environment variables from .env files,
creating configuration files, and validating API credentials.
"""
def __init__(self, env_file: Optional[Path] = None):
"""
Initialize configuration manager.
Args:
env_file: Path to .env file (defaults to current directory)
"""
# Use current directory .env if not specified
self.env_file = env_file or Path(".env")
# Load existing environment variables if .env file exists
if self.env_file.exists():
load_dotenv(self.env_file)
else:
# Still allow configuration purely from environment variables
load_dotenv(override=False)
def get_api_config(self) -> Dict[str, Any]:
"""
Get current API configuration from environment variables.
Returns:
Dictionary containing all Alpaca API configuration
"""
return {
'api_key': os.getenv('ALPACA_API_KEY', ''),
'secret_key': os.getenv('ALPACA_SECRET_KEY', ''),
'paper_trade': os.getenv('ALPACA_PAPER_TRADE', 'True').lower() == 'true',
'trade_api_url': os.getenv('TRADE_API_URL'),
'trade_api_wss': os.getenv('TRADE_API_WSS'),
'data_api_url': os.getenv('DATA_API_URL'),
'stream_data_wss': os.getenv('STREAM_DATA_WSS')
}
def validate_config(self) -> bool:
"""
Validate that required configuration is present.
Returns:
True if configuration is valid, False otherwise
"""
config = self.get_api_config()
# Check for required API keys
if not config['api_key'] or not config['secret_key']:
return False
return True
def setup_env_file(self, api_key: Optional[str] = None,
secret_key: Optional[str] = None,
paper_trade: bool = True) -> bool:
"""
Create or update .env file with API configuration.
Args:
api_key: Alpaca API key (prompts if not provided)
secret_key: Alpaca secret key (prompts if not provided)
paper_trade: Whether to use paper trading (default: True)
Returns:
True if .env file was created/updated successfully
"""
try:
# Prompt for API keys if not provided
if not api_key:
api_key = input("Enter your Alpaca API key: ").strip()
if not secret_key:
secret_key = input("Enter your Alpaca secret key: ").strip()
# Confirm paper trading mode
if paper_trade:
confirm = input("Use paper trading mode? [Y/n]: ").strip().lower()
if confirm in ['n', 'no']:
paper_trade = False
print("WARNING: Live trading mode selected - this will use real money!")
final_confirm = input("Are you sure? [y/N]: ").strip().lower()
if final_confirm not in ['y', 'yes']:
paper_trade = True
print("Reverting to paper trading mode for safety.")
# Create .env file content with clear comments
env_content = f"""
# Alpaca MCP Server Configuration
# Generated by alpaca-mcp init command
#
# IMPORTANT: Keep these credentials secure and never commit to version control
# Alpaca API Credentials
# Get these from: https://app.alpaca.markets/paper/dashboard/overview
ALPACA_API_KEY={api_key}
ALPACA_SECRET_KEY={secret_key}
# Trading Configuration
# True = Paper trading (safe for testing), False = Live trading (real money)
ALPACA_PAPER_TRADE={paper_trade}
# API Endpoints (leave as None to use Alpaca defaults)
TRADE_API_URL=None
TRADE_API_WSS=None
DATA_API_URL=None
STREAM_DATA_WSS=None
"""
# Write the .env file
self.env_file.write_text(env_content)
print(f"Configuration saved to {self.env_file}")
if not api_key or not secret_key:
print("Note: API keys are empty. Please edit .env file to add your credentials.")
return True
except Exception as e:
print(f"Error creating .env file: {e}")
return False
def get_config_summary(self) -> str:
"""
Get a summary of current configuration (without exposing secrets).
Returns:
Human-readable configuration summary
"""
config = self.get_api_config()
# Mask sensitive information
api_key_display = f"{config['api_key'][:8]}..." if config['api_key'] else "Not set"
secret_key_display = f"{config['secret_key'][:8]}..." if config['secret_key'] else "Not set"
return f"""Current Configuration:
- API Key: {api_key_display}
- Secret Key: {secret_key_display}
- Paper Trading: {config['paper_trade']}
- Config File: {self.env_file}
- File Exists: {self.env_file.exists()}"""