Skip to main content
Glama

Security MCP Server

by nordeim
entrypoint.sh18 kB
#!/usr/bin/env bash #============================================================================== # MCP Network Tools Server - Docker Entrypoint Script #============================================================================== # Purpose: Container initialization, validation, and server startup # # Responsibilities: # - Pre-flight environment checks # - Configuration validation # - Dependency verification # - Signal handling setup # - Graceful shutdown management # - Server process execution # # Signal Handling: # SIGTERM - Graceful shutdown (Kubernetes, Docker stop) # SIGINT - Interrupt (Ctrl+C) # SIGHUP - Reload configuration # # Exit Codes: # 0 - Success # 1 - General error # 2 - Configuration error # 3 - Dependency error # 4 - Permission error # 130 - Terminated by SIGINT # 143 - Terminated by SIGTERM # # Author: MCP Network Tools Team # Version: 2.0.0 #============================================================================== set -euo pipefail # Exit on error, undefined vars, pipe failures #------------------------------------------------------------------------------ # Configuration #------------------------------------------------------------------------------ # Application settings APP_HOME="${MCP_HOME:-/app}" APP_LOG_DIR="${MCP_LOG_DIR:-/app/logs}" APP_DATA_DIR="${MCP_DATA_DIR:-/app/data}" APP_CONFIG_DIR="${MCP_CONFIG_DIR:-/app/config}" # Server settings TRANSPORT="${MCP_SERVER_TRANSPORT:-stdio}" LOG_LEVEL="${LOG_LEVEL:-INFO}" SHUTDOWN_TIMEOUT="${MCP_SERVER_SHUTDOWN_GRACE_PERIOD:-30}" # Process tracking PID_FILE="/tmp/mcp_server.pid" SERVER_PID="" # Colors for output (disabled in production) if [ -t 1 ]; then RED='\033[0;31m' GREEN='\033[0;32m' YELLOW='\033[1;33m' BLUE='\033[0;34m' NC='\033[0m' else RED='' GREEN='' YELLOW='' BLUE='' NC='' fi #------------------------------------------------------------------------------ # Logging Functions #------------------------------------------------------------------------------ log_info() { echo -e "${BLUE}[INFO]${NC} $(date -u '+%Y-%m-%dT%H:%M:%SZ') - entrypoint - $*" } log_success() { echo -e "${GREEN}[SUCCESS]${NC} $(date -u '+%Y-%m-%dT%H:%M:%SZ') - entrypoint - $*" } log_warning() { echo -e "${YELLOW}[WARNING]${NC} $(date -u '+%Y-%m-%dT%H:%M:%SZ') - entrypoint - $*" } log_error() { echo -e "${RED}[ERROR]${NC} $(date -u '+%Y-%m-%dT%H:%M:%SZ') - entrypoint - $*" >&2 } log_debug() { if [ "${LOG_LEVEL}" = "DEBUG" ]; then echo -e "[DEBUG] $(date -u '+%Y-%m-%dT%H:%M:%SZ') - entrypoint - $*" fi } #------------------------------------------------------------------------------ # Error Handling #------------------------------------------------------------------------------ # Trap errors and exit trap 'error_handler $? $LINENO' ERR error_handler() { local exit_code=$1 local line_number=$2 log_error "Script failed at line $line_number with exit code $exit_code" cleanup exit "$exit_code" } #------------------------------------------------------------------------------ # Signal Handlers #------------------------------------------------------------------------------ # Graceful shutdown handler shutdown_handler() { local signal=$1 log_info "Received signal: $signal" log_info "Initiating graceful shutdown..." if [ -n "$SERVER_PID" ] && kill -0 "$SERVER_PID" 2>/dev/null; then log_info "Stopping server process (PID: $SERVER_PID)..." # Send SIGTERM to server kill -TERM "$SERVER_PID" 2>/dev/null || true # Wait for graceful shutdown local timeout=$SHUTDOWN_TIMEOUT local elapsed=0 while kill -0 "$SERVER_PID" 2>/dev/null && [ $elapsed -lt $timeout ]; do sleep 1 elapsed=$((elapsed + 1)) if [ $((elapsed % 5)) -eq 0 ]; then log_info "Waiting for shutdown... (${elapsed}s/${timeout}s)" fi done # Force kill if still running if kill -0 "$SERVER_PID" 2>/dev/null; then log_warning "Graceful shutdown timeout, forcing termination..." kill -KILL "$SERVER_PID" 2>/dev/null || true sleep 1 fi log_success "Server stopped" else log_debug "No server process to stop" fi cleanup # Exit with appropriate code case $signal in SIGTERM) exit 143 ;; SIGINT) exit 130 ;; *) exit 0 ;; esac } # Setup signal traps setup_signal_handlers() { log_debug "Setting up signal handlers..." trap 'shutdown_handler SIGTERM' SIGTERM trap 'shutdown_handler SIGINT' SIGINT trap 'reload_config' SIGHUP log_debug "Signal handlers configured" } # Configuration reload handler reload_config() { log_info "Received SIGHUP - configuration reload requested" if [ -n "$SERVER_PID" ] && kill -0 "$SERVER_PID" 2>/dev/null; then # Forward SIGHUP to server process log_info "Forwarding reload signal to server (PID: $SERVER_PID)..." kill -HUP "$SERVER_PID" 2>/dev/null || true else log_warning "Server not running, reload ignored" fi } #------------------------------------------------------------------------------ # Pre-flight Checks #------------------------------------------------------------------------------ check_environment() { log_info "Checking environment..." # Check Python version if ! command -v python &> /dev/null; then log_error "Python not found in PATH" return 3 fi local python_version python_version=$(python --version 2>&1 | awk '{print $2}') log_info "Python version: $python_version" # Check virtual environment if [ -n "${VIRTUAL_ENV:-}" ]; then log_info "Virtual environment: $VIRTUAL_ENV" else log_debug "No virtual environment detected (using system Python)" fi # Check required environment variables local required_vars=("MCP_HOME") for var in "${required_vars[@]}"; do if [ -z "${!var:-}" ]; then log_error "Required environment variable not set: $var" return 2 fi done log_success "Environment check passed" return 0 } check_directories() { log_info "Checking directories..." # Check application home if [ ! -d "$APP_HOME" ]; then log_error "Application home not found: $APP_HOME" return 1 fi # Create log directory if needed if [ ! -d "$APP_LOG_DIR" ]; then log_info "Creating log directory: $APP_LOG_DIR" mkdir -p "$APP_LOG_DIR" || { log_error "Failed to create log directory" return 4 } fi # Create data directory if needed if [ ! -d "$APP_DATA_DIR" ]; then log_info "Creating data directory: $APP_DATA_DIR" mkdir -p "$APP_DATA_DIR" || { log_error "Failed to create data directory" return 4 } fi # Check permissions if [ ! -w "$APP_LOG_DIR" ]; then log_error "Log directory not writable: $APP_LOG_DIR" return 4 fi if [ ! -w "$APP_DATA_DIR" ]; then log_error "Data directory not writable: $APP_DATA_DIR" return 4 fi log_success "Directory check passed" return 0 } check_dependencies() { log_info "Checking dependencies..." # Check Python package if ! python -c "import mcp_server" 2>/dev/null; then log_error "mcp_server package not found" log_error "Install with: pip install -e ." return 3 fi log_info "mcp_server package found" # Check optional dependencies local optional_deps=("psutil" "prometheus_client" "fastapi" "uvicorn") local missing_optional=() for dep in "${optional_deps[@]}"; do if ! python -c "import $dep" 2>/dev/null; then missing_optional+=("$dep") fi done if [ ${#missing_optional[@]} -gt 0 ]; then log_warning "Missing optional dependencies: ${missing_optional[*]}" log_info "Some features may be unavailable" fi # Check system tools local tools=("nmap") local missing_tools=() for tool in "${tools[@]}"; do if ! command -v "$tool" &> /dev/null; then missing_tools+=("$tool") fi done if [ ${#missing_tools[@]} -gt 0 ]; then log_warning "Missing system tools: ${missing_tools[*]}" log_info "Some tools may not be available" else log_info "All system tools available" fi log_success "Dependency check passed" return 0 } validate_configuration() { log_info "Validating configuration..." # Validate transport mode if [ "$TRANSPORT" != "stdio" ] && [ "$TRANSPORT" != "http" ]; then log_error "Invalid transport mode: $TRANSPORT (must be stdio or http)" return 2 fi log_info "Transport mode: $TRANSPORT" # Validate HTTP-specific settings if [ "$TRANSPORT" = "http" ]; then local port="${MCP_SERVER_PORT:-8080}" if ! [[ "$port" =~ ^[0-9]+$ ]] || [ "$port" -lt 1 ] || [ "$port" -gt 65535 ]; then log_error "Invalid port number: $port" return 2 fi log_info "HTTP server port: $port" fi # Check configuration file if specified if [ -n "${MCP_CONFIG_FILE:-}" ]; then if [ ! -f "$MCP_CONFIG_FILE" ]; then log_error "Configuration file not found: $MCP_CONFIG_FILE" return 2 fi log_info "Configuration file: $MCP_CONFIG_FILE" # Validate configuration file format if [[ "$MCP_CONFIG_FILE" == *.yaml ]] || [[ "$MCP_CONFIG_FILE" == *.yml ]]; then if command -v python &> /dev/null; then if ! python -c "import yaml; yaml.safe_load(open('$MCP_CONFIG_FILE'))" 2>/dev/null; then log_error "Invalid YAML configuration file" return 2 fi fi elif [[ "$MCP_CONFIG_FILE" == *.json ]]; then if ! python -c "import json; json.load(open('$MCP_CONFIG_FILE'))" 2>/dev/null; then log_error "Invalid JSON configuration file" return 2 fi fi log_success "Configuration file validated" fi log_success "Configuration validation passed" return 0 } #------------------------------------------------------------------------------ # Database Migration (if needed) #------------------------------------------------------------------------------ run_migrations() { log_debug "Checking for database migrations..." # Example: Run Alembic migrations if database is configured if [ -n "${MCP_DATABASE_URL:-}" ]; then log_info "Database URL configured, checking migrations..." if command -v alembic &> /dev/null; then log_info "Running database migrations..." cd "$APP_HOME" || return 1 alembic upgrade head || { log_error "Database migration failed" return 1 } log_success "Database migrations completed" else log_warning "Alembic not found, skipping migrations" fi else log_debug "No database configured, skipping migrations" fi return 0 } #------------------------------------------------------------------------------ # Dependency Waiting (for multi-container deployments) #------------------------------------------------------------------------------ wait_for_dependencies() { log_debug "Checking for external dependencies..." # Example: Wait for database if [ -n "${MCP_DATABASE_URL:-}" ]; then log_info "Waiting for database to be ready..." local max_attempts=30 local attempt=1 while [ $attempt -le $max_attempts ]; do if python -c "import psycopg2; psycopg2.connect('$MCP_DATABASE_URL')" 2>/dev/null; then log_success "Database is ready" break fi if [ $attempt -eq $max_attempts ]; then log_error "Database not ready after $max_attempts attempts" return 3 fi log_info "Attempt $attempt/$max_attempts - waiting for database..." sleep 2 attempt=$((attempt + 1)) done fi # Example: Wait for Redis if [ -n "${MCP_REDIS_URL:-}" ]; then log_info "Waiting for Redis to be ready..." local max_attempts=30 local attempt=1 while [ $attempt -le $max_attempts ]; do if timeout 1 bash -c "echo > /dev/tcp/${MCP_REDIS_HOST:-localhost}/${MCP_REDIS_PORT:-6379}" 2>/dev/null; then log_success "Redis is ready" break fi if [ $attempt -eq $max_attempts ]; then log_error "Redis not ready after $max_attempts attempts" return 3 fi log_info "Attempt $attempt/$max_attempts - waiting for Redis..." sleep 2 attempt=$((attempt + 1)) done fi return 0 } #------------------------------------------------------------------------------ # Server Startup #------------------------------------------------------------------------------ start_server() { log_info "Starting MCP server..." log_info "Transport: $TRANSPORT" log_info "Log level: $LOG_LEVEL" # Change to application directory cd "$APP_HOME" || { log_error "Failed to change to application directory" return 1 } # Execute server with proper command if [ $# -gt 0 ]; then # Custom command provided log_info "Executing custom command: $*" exec "$@" else # Default server command log_info "Executing default server command..." exec python -m mcp_server.server fi } #------------------------------------------------------------------------------ # Cleanup #------------------------------------------------------------------------------ cleanup() { log_debug "Performing cleanup..." # Remove PID file if [ -f "$PID_FILE" ]; then rm -f "$PID_FILE" log_debug "PID file removed" fi # Additional cleanup tasks # - Close connections # - Flush buffers # - Save state log_debug "Cleanup completed" } #------------------------------------------------------------------------------ # Health Check (internal) #------------------------------------------------------------------------------ internal_health_check() { log_debug "Running internal health check..." # Check if server process is running if [ -n "$SERVER_PID" ] && kill -0 "$SERVER_PID" 2>/dev/null; then log_debug "Server process is running (PID: $SERVER_PID)" return 0 else log_error "Server process not running" return 1 fi } #------------------------------------------------------------------------------ # Main Execution Flow #------------------------------------------------------------------------------ main() { log_info "==========================================" log_info " MCP Network Tools Server - Starting" log_info "==========================================" log_info "Version: 2.0.0" log_info "User: $(whoami)" log_info "Home: $APP_HOME" log_info "Transport: $TRANSPORT" log_info "" # Setup signal handlers first setup_signal_handlers # Run pre-flight checks log_info "Running pre-flight checks..." check_environment || exit $? check_directories || exit $? check_dependencies || exit $? validate_configuration || exit $? log_success "All pre-flight checks passed" log_info "" # Wait for external dependencies (if any) if [ "${WAIT_FOR_DEPENDENCIES:-false}" = "true" ]; then wait_for_dependencies || exit $? fi # Run database migrations (if configured) if [ "${RUN_MIGRATIONS:-false}" = "true" ]; then run_migrations || exit $? fi # Display startup information log_info "==========================================" log_info " Configuration Summary" log_info "==========================================" log_info "Transport: $TRANSPORT" log_info "Log Level: $LOG_LEVEL" log_info "Log Directory: $APP_LOG_DIR" log_info "Data Directory: $APP_DATA_DIR" log_info "Config Directory: $APP_CONFIG_DIR" if [ "$TRANSPORT" = "http" ]; then log_info "HTTP Host: ${MCP_SERVER_HOST:-0.0.0.0}" log_info "HTTP Port: ${MCP_SERVER_PORT:-8080}" fi log_info "==========================================" log_info "" # Start server log_success "Starting server..." start_server "$@" } #------------------------------------------------------------------------------ # Entry Point #------------------------------------------------------------------------------ # Execute main function with all arguments main "$@" #============================================================================== # Exit Codes Reference #============================================================================== # 0 - Success # 1 - General error # 2 - Configuration error # 3 - Dependency error # 4 - Permission error # 130 - Terminated by SIGINT (Ctrl+C) # 143 - Terminated by SIGTERM (graceful shutdown) #==============================================================================

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/nordeim/Security-MCP-Server-v3'

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