Skip to main content
Glama

MCP Server for Splunk

Apache 2.0
16
  • Apple
  • Linux
build_and_run.sh49.9 kB
#!/bin/bash # Build and run MCP Server for Splunk # This script builds the Docker image and runs it with docker-compose, or falls back to local mode # # Usage: # ./build_and_run.sh # Interactive mode - choose deployment method # ./build_and_run.sh --docker # Force Docker deployment # ./build_and_run.sh --local # Force local deployment # # Arguments: # --docker Force Docker deployment (skip choice dialog) # --local Force local deployment (skip choice dialog) # --help Show this help message set -e # Exit on error # Parse command line arguments first (before any other logic) DEPLOYMENT_MODE="" FORCE_MODE=false STOP_MODE=false while [[ $# -gt 0 ]]; do case $1 in --docker) DEPLOYMENT_MODE="docker" FORCE_MODE=true shift ;; --local) DEPLOYMENT_MODE="local" FORCE_MODE=true shift ;; --stop) STOP_MODE=true shift ;; --help|-h) echo "Usage: $0 [OPTIONS]" echo echo "Options:" echo " --docker Force Docker deployment (skip choice dialog)" echo " --local Force local deployment (skip choice dialog)" echo " --stop Stop all Docker services and clean up" echo " --help, -h Show this help message" echo echo "Examples:" echo " $0 # Interactive mode - choose deployment method" echo " $0 --docker # Force Docker deployment" echo " $0 --local # Force local deployment" echo " $0 --stop # Stop all Docker services" echo exit 0 ;; *) echo "Error: Unknown option: $1" >&2 echo "Use --help for usage information" >&2 exit 1 ;; esac done # Change to the project root directory (parent of scripts) cd "$(dirname "$0")/.." echo "🚀 Building and Running MCP Server for Splunk" echo "=============================================" echo echo "📚 Need help with prerequisites? See: docs/getting-started/installation.md" echo # Colors for output RED='\033[0;31m' GREEN='\033[0;32m' YELLOW='\033[1;33m' BLUE='\033[0;34m' PURPLE='\033[0;35m' NC='\033[0m' # No Color # Function to print colored output print_status() { echo -e "${BLUE}[INFO]${NC} $1" } print_success() { echo -e "${GREEN}[SUCCESS]${NC} $1" } print_warning() { echo -e "${YELLOW}[WARNING]${NC} $1" } print_error() { echo -e "${RED}[ERROR]${NC} $1" } print_local() { echo -e "${PURPLE}[LOCAL]${NC} $1" } # Function to ensure logs directory exists ensure_logs_dir() { if [ ! -d "logs" ]; then print_local "Creating logs directory..." mkdir -p logs fi } # Function to prompt for Splunk configuration and update .env file prompt_splunk_config() { local is_docker_mode=${1:-false} echo print_status "🔧 Splunk Configuration Setup" echo "==================================" echo # Check if .env file exists, create from example if not if [ ! -f .env ]; then if [ -f env.example ]; then print_local "Creating .env file from env.example..." cp env.example .env print_success ".env file created from env.example" else print_error "env.example not found. Cannot create .env file." return 1 fi fi # Read current values from .env file local current_host=$(grep "^SPLUNK_HOST=" .env 2>/dev/null | cut -d'=' -f2 | sed -e 's/^"//' -e 's/"$//' -e "s/^'//" -e "s/'$//" || echo "") local current_port=$(grep "^SPLUNK_PORT=" .env 2>/dev/null | cut -d'=' -f2 | sed -e 's/^"//' -e 's/"$//' -e "s/^'//" -e "s/'$//" || echo "8089") local current_username=$(grep "^SPLUNK_USERNAME=" .env 2>/dev/null | cut -d'=' -f2 | sed -e 's/^"//' -e 's/"$//' -e "s/^'//" -e "s/'$//" || echo "admin") local current_password=$(grep "^SPLUNK_PASSWORD=" .env 2>/dev/null | cut -d'=' -f2 | sed -e 's/^"//' -e 's/"$//' -e "s/^'//" -e "s/'$//" || echo "") echo "Current Splunk configuration:" echo " Host: ${current_host:-'Not set'}" echo " Port: ${current_port:-'8089'}" echo " Username: ${current_username:-'admin'}" echo " Password: ${current_password:+'***'}" echo # If in Docker mode and SPLUNK_HOST is not 'so1', offer to restore Docker defaults local docker_defaults_restored=false if [ "$is_docker_mode" = true ] && [ "$current_host" != "so1" ] && [ -n "$current_host" ]; then echo "🚢 Docker Mode Detected:" echo " Current SPLUNK_HOST ($current_host) is different from Docker default (so1)" echo " This will use external Splunk instead of the included Docker container" echo read -p "Restore Docker defaults (so1) to include Splunk container? (y/N): " restore_docker if [[ "$restore_docker" =~ ^[Yy]$ ]]; then current_host="so1" current_port="8089" current_username="admin" current_password="Chang3d!" docker_defaults_restored=true print_success "Restored Docker defaults: SPLUNK_HOST=so1, SPLUNK_PORT=8089, SPLUNK_USERNAME=admin, SPLUNK_PASSWORD=Chang3d!" echo fi fi # Prompt for new values (skip if Docker defaults were restored) if [ "$docker_defaults_restored" = true ]; then # Use restored Docker defaults directly local final_host="$current_host" local final_port="$current_port" local final_username="$current_username" local final_password="$current_password" print_local "Using restored Docker defaults, skipping user input prompts..." else # Prompt for new values read -p "Enter Splunk host/URL (current: ${current_host:-'Not set'}, press Enter to keep): " new_host read -p "Enter Splunk port (current: ${current_port:-'8089'}, press Enter to keep): " new_port read -p "Enter Splunk username (current: ${current_username:-'admin'}, press Enter to keep): " new_username read -s -p "Enter Splunk password (press Enter to keep current): " new_password echo # Use new values if provided, otherwise keep current local final_host=${new_host:-$current_host} local final_port=${new_port:-$current_port} local final_username=${new_username:-$current_username} local final_password=${new_password:-$current_password} fi # Parse URL if provided (strip protocol and extract hostname) if [ -n "$new_host" ]; then if [[ "$new_host" =~ ^https?://(.+)$ ]]; then final_host="${BASH_REMATCH[1]}" print_local "Extracted hostname from URL: $final_host" print_local "Note: SSL verification setting unchanged (preserves private CA configuration)" fi fi # Validate required fields if [ -z "$final_host" ]; then print_error "SPLUNK_HOST is required. Please provide a value." return 1 fi if [ -z "$final_username" ]; then print_error "SPLUNK_USERNAME is required. Please provide a value." return 1 fi if [ -z "$final_password" ]; then print_error "SPLUNK_PASSWORD is required. Please provide a value." return 1 fi # Check if there are actual changes to make local has_changes=false if [ "$docker_defaults_restored" = true ]; then has_changes=true elif [ "$new_host" != "" ] || [ "$new_port" != "" ] || [ "$new_username" != "" ] || [ "$new_password" != "" ]; then has_changes=true fi # Show final configuration echo echo "Final Splunk configuration:" echo " Host: $final_host" echo " Port: $final_port" echo " Username: $final_username" echo " Password: ***" echo # Only ask for confirmation if there are actual changes if [ "$has_changes" = true ]; then # If Docker defaults were restored, automatically update without asking if [ "$docker_defaults_restored" = true ]; then print_status "Automatically updating .env file with restored Docker defaults..." # Update .env file local temp_env=".env.tmp" # Process .env file line by line while IFS= read -r line || [ -n "$line" ]; do if [[ "$line" =~ ^SPLUNK_HOST= ]]; then echo "SPLUNK_HOST='$final_host'" >> "$temp_env" elif [[ "$line" =~ ^SPLUNK_PORT= ]]; then echo "SPLUNK_PORT='$final_port'" >> "$temp_env" elif [[ "$line" =~ ^SPLUNK_USERNAME= ]]; then echo "SPLUNK_USERNAME='$final_username'" >> "$temp_env" elif [[ "$line" =~ ^SPLUNK_PASSWORD= ]]; then echo "SPLUNK_PASSWORD='$final_password'" >> "$temp_env" else echo "$line" >> "$temp_env" fi done < .env # Replace original .env with updated version mv "$temp_env" .env print_success ".env file updated successfully with Docker defaults!" else # Ask for confirmation for other types of changes read -p "Update .env file with these settings? (y/N): " confirm if [[ "$confirm" =~ ^[Yy]$ ]]; then # Update .env file local temp_env=".env.tmp" # Process .env file line by line while IFS= read -r line || [ -n "$line" ]; do if [[ "$line" =~ ^SPLUNK_HOST= ]]; then echo "SPLUNK_HOST='$final_host'" >> "$temp_env" elif [[ "$line" =~ ^SPLUNK_PORT= ]]; then echo "SPLUNK_PORT='$final_port'" >> "$temp_env" elif [[ "$line" =~ ^SPLUNK_USERNAME= ]]; then echo "SPLUNK_USERNAME='$final_username'" >> "$temp_env" elif [[ "$line" =~ ^SPLUNK_PASSWORD= ]]; then echo "SPLUNK_PASSWORD='$final_password'" >> "$temp_env" else echo "$line" >> "$temp_env" fi done < .env # Replace original .env with updated version mv "$temp_env" .env print_success ".env file updated successfully!" else print_warning "Configuration update cancelled. Using existing values." fi fi else print_local "No changes detected. Using existing configuration." fi # Always export the values for current session (whether updated or existing) export SPLUNK_HOST="$final_host" export SPLUNK_PORT="$final_port" export SPLUNK_USERNAME="$final_username" export SPLUNK_PASSWORD="$final_password" print_success "Splunk configuration loaded for current session." return 0 } # Function to load environment variables from .env file load_env_file() { if [ -f .env ]; then print_local "Loading environment variables from .env file..." # Export variables from .env file, handling comments and empty lines while IFS= read -r line || [ -n "$line" ]; do # Skip empty lines and comments if [[ "$line" =~ ^[[:space:]]*$ ]] || [[ "$line" =~ ^[[:space:]]*# ]]; then continue fi # Check if line contains an assignment if [[ "$line" =~ ^[[:space:]]*([A-Za-z_][A-Za-z0-9_]*)=(.*)$ ]]; then local var_name="${BASH_REMATCH[1]}" local var_value="${BASH_REMATCH[2]}" # Remove surrounding quotes if present var_value=$(echo "$var_value" | sed -e 's/^"//' -e 's/"$//' -e "s/^'//" -e "s/'$//") # Export the variable export "$var_name=$var_value" # Only show non-sensitive variables if [[ "$var_name" == *"PASSWORD"* ]] || [[ "$var_name" == *"SECRET"* ]] || [[ "$var_name" == *"TOKEN"* ]]; then print_local "Loaded: $var_name=***" else print_local "Loaded: $var_name=$var_value" fi fi done < .env print_success "Environment variables loaded from .env file!" # Show summary of important Splunk configuration echo print_status "📋 Splunk Configuration Summary:" echo " 🌐 Host: ${SPLUNK_HOST:-'Not set'}" echo " 🔌 Port: ${SPLUNK_PORT:-'8089 (default)'}" echo " 👤 User: ${SPLUNK_USERNAME:-'Not set'}" # Show last 3 chars of password for verification if [ -n "$SPLUNK_PASSWORD" ]; then local pass_display="***${SPLUNK_PASSWORD: -3}" echo " 🔐 Pass: $pass_display" else echo " 🔐 Pass: Not set" fi echo " 🔒 SSL: ${SPLUNK_VERIFY_SSL:-'Not set'}" # Check for alternative MCP_SPLUNK_* variables if [ -n "$MCP_SPLUNK_HOST" ] || [ -n "$MCP_SPLUNK_USERNAME" ] || [ -n "$MCP_SPLUNK_PASSWORD" ]; then echo print_status "📋 Alternative MCP Splunk Configuration Found:" echo " 🌐 Host: ${MCP_SPLUNK_HOST:-'Not set'}" echo " 👤 User: ${MCP_SPLUNK_USERNAME:-'Not set'}" # Show last 3 chars of MCP password for verification if [ -n "$MCP_SPLUNK_PASSWORD" ]; then local mcp_pass_display="***${MCP_SPLUNK_PASSWORD: -3}" echo " 🔐 Pass: $mcp_pass_display" else echo " 🔐 Pass: Not set" fi print_local "These MCP_SPLUNK_* variables will override the SPLUNK_* variables in the MCP server." fi echo else print_warning "No .env file found. Using system environment variables only." # Check if any Splunk environment variables are set in the system if [ -n "$SPLUNK_HOST" ] || [ -n "$MCP_SPLUNK_HOST" ]; then print_status "📋 System Environment Splunk Configuration:" echo " 🌐 Host: ${SPLUNK_HOST:-${MCP_SPLUNK_HOST:-'Not set'}}" echo " 👤 User: ${SPLUNK_USERNAME:-${MCP_SPLUNK_USERNAME:-'Not set'}}" # Show last 3 chars of password for verification local sys_password="${SPLUNK_PASSWORD:-$MCP_SPLUNK_PASSWORD}" if [ -n "$sys_password" ]; then local sys_pass_display="***${sys_password: -3}" echo " 🔐 Pass: $sys_pass_display" else echo " 🔐 Pass: Not set" fi else print_warning "No Splunk configuration found in environment variables." print_warning "The MCP server may not be able to connect to Splunk without configuration." fi fi } # Function to check if uv is installed check_uv() { if command -v uv &> /dev/null; then return 0 else return 1 fi } # Function to install uv install_uv() { print_status "Installing uv package manager..." if command -v curl &> /dev/null; then curl -LsSf https://astral.sh/uv/install.sh | sh # Source the shell to get uv in PATH export PATH="$HOME/.cargo/bin:$PATH" elif command -v wget &> /dev/null; then wget -qO- https://astral.sh/uv/install.sh | sh export PATH="$HOME/.cargo/bin:$PATH" else print_error "Neither curl nor wget found. Please install uv manually:" print_error " pip install uv" echo print_error "📚 For detailed installation instructions, see:" print_error " docs/getting-started/installation.md#-uv-package-manager-installation" exit 1 fi # Verify installation if check_uv; then print_success "uv installed successfully!" else print_error "Failed to install uv. Please install manually and try again." echo print_error "📚 For detailed installation instructions, see:" print_error " docs/getting-started/installation.md#-uv-package-manager-installation" exit 1 fi } # Function to setup local environment setup_local_env() { print_local "Setting up local development environment..." # Check if uv is available if ! check_uv; then print_warning "uv not found. Installing uv..." install_uv fi # Check if virtual environment and dependencies are installed if [ ! -d ".venv" ] || [ ! -f ".venv/pyvenv.cfg" ]; then print_local "Creating virtual environment and installing dependencies..." uv sync --dev else print_local "Virtual environment exists. Checking if sync is needed..." # Check if uv.lock is newer than .venv if [ "uv.lock" -nt ".venv/pyvenv.cfg" ] || [ "pyproject.toml" -nt ".venv/pyvenv.cfg" ]; then print_local "Dependencies may be outdated. Running uv sync..." uv sync --dev else print_local "Dependencies are up to date." fi fi # Check if .env file exists if [ ! -f .env ]; then print_warning ".env file not found. Creating from env.example..." cp env.example .env print_warning "Created .env file. You may want to edit it with your Splunk configuration." print_warning "For local development, you can also use MCP_SPLUNK_* environment variables." fi # Prompt for Splunk configuration with local mode prompt_splunk_config false # Load environment variables from .env file load_env_file print_success "Local environment setup complete!" } # Function to run local server run_local_server() { print_local "Starting MCP server locally with FastMCP CLI..." # Define cleanup function early to be available for error handling cleanup() { print_local "Cleaning up..." # Kill MCP Inspector if PID file exists if [ -f .inspector_pid ]; then local pid=$(cat .inspector_pid) if kill -0 $pid 2>/dev/null; then print_local "Stopping MCP Inspector (PID: $pid)..." kill $pid 2>/dev/null fi rm -f .inspector_pid fi # Clean up any server processes pkill -f "fastmcp run" 2>/dev/null || true # Clean up log files rm -f logs/inspector.log logs/mcp_server.log print_success "Cleanup complete!" } # Check if Node.js/npm is available for MCP Inspector (start after server port is known) local inspector_available=false local inspector_supported=false if command -v node &> /dev/null && command -v npx &> /dev/null; then # Check Node.js version (MCP Inspector 0.16.x requires Node.js 22+) local node_version=$(node --version 2>/dev/null | sed 's/v//') local node_major=$(echo "$node_version" | cut -d. -f1) if [ "$node_major" -ge 22 ] 2>/dev/null; then inspector_supported=true print_local "Node.js v$node_version detected. MCP Inspector will be started after the MCP server is running..." else print_warning "Node.js v$node_version detected, but MCP Inspector 0.16.x requires Node.js 22+" print_warning "MCP Inspector will not be available." print_warning "To upgrade Node.js: https://nodejs.org/" echo print_warning "📚 For detailed installation instructions, see:" print_warning " docs/getting-started/installation.md#-nodejs-installation-optional---for-mcp-inspector" fi else print_warning "Node.js/npx not found. MCP Inspector will not be available." print_warning "To install Node.js: https://nodejs.org/" echo print_warning "📚 For detailed installation instructions, see:" print_warning " docs/getting-started/installation.md#-nodejs-installation-optional---for-mcp-inspector" fi echo print_status "Starting MCP server..." # Test if the FastMCP command works first print_local "Testing FastMCP installation..." if ! uv run python -c "import fastmcp; print('FastMCP import successful')"; then print_error "FastMCP import failed. Checking installation..." print_local "Installing FastMCP..." uv add fastmcp uv sync --dev fi # Start the server with uv and better error handling print_local "Finding available port for MCP server..." local preferred_port=${MCP_SERVER_PORT:-8003} print_local "Preferred port from MCP_SERVER_PORT: $preferred_port" # Start from preferred port to avoid conflict with Splunk Web UI (port 8000) local mcp_port=$(find_available_port $preferred_port) if [ "$mcp_port" -ne $preferred_port ] 2>/dev/null; then print_warning "Port $preferred_port is in use. Using port $mcp_port instead." else print_local "Using port $mcp_port for MCP server." fi print_local "Starting MCP server on port $mcp_port..." print_local "Command: uv run fastmcp run src/server.py --transport http --port $mcp_port" # Ensure logs directory exists ensure_logs_dir # Start server in background to check if it starts successfully uv run fastmcp run src/server.py --transport http --port $mcp_port > logs/mcp_server.log 2>&1 & local server_pid=$! # Give server time to start print_local "Waiting for MCP server to start..." sleep 3 # Check if the server process is still running if ! kill -0 $server_pid 2>/dev/null; then print_error "MCP server failed to start. Check the logs:" echo if [ -f logs/mcp_server.log ]; then print_error "=== MCP Server Log ===" cat logs/mcp_server.log echo fi print_error "Troubleshooting steps:" echo "1. Check if src/server.py exists and is valid" echo "2. Verify FastMCP installation: uv run python -c 'import fastmcp'" echo "3. Try running manually: uv run fastmcp run src/server.py --help" echo "4. Check Python environment: uv run python --version" echo print_error "📚 For prerequisite installation help, see:" print_error " docs/getting-started/installation.md" echo print_error "🔧 Run the prerequisite checker to see what's missing:" print_error " ./scripts/check-prerequisites.sh" cleanup exit 1 fi # Check if the chosen port is actually listening print_local "Checking if MCP server is listening on port $mcp_port..." local port_check_attempts=0 local max_port_attempts=5 local server_listening=false while [ $port_check_attempts -lt $max_port_attempts ]; do if command -v lsof &> /dev/null; then if lsof -i :$mcp_port &> /dev/null; then server_listening=true break fi elif command -v netstat &> /dev/null; then if netstat -an | grep ":$mcp_port " | grep LISTEN &> /dev/null; then server_listening=true break fi elif command -v curl &> /dev/null; then # Try to actually connect to the server if curl -s -o /dev/null -w "%{http_code}" "http://localhost:$mcp_port" | grep -q "200\|404\|500"; then server_listening=true break fi fi sleep 2 port_check_attempts=$((port_check_attempts + 1)) print_local "Port check attempt $port_check_attempts/$max_port_attempts..." done if [ "$server_listening" = true ]; then print_success "MCP server is listening on port $mcp_port!" # Also test basic connectivity if command -v curl &> /dev/null; then print_local "Testing server connectivity..." local response=$(curl -s -o /dev/null -w "%{http_code}" "http://localhost:$mcp_port" 2>/dev/null || echo "failed") if [ "$response" != "failed" ]; then print_success "Server responds with HTTP $response" else print_warning "Server running but HTTP request failed" fi fi # Start MCP Inspector now that the actual port is known if [ "$inspector_supported" = true ]; then print_status "Starting MCP Inspector..." # If inspector is already running, reuse it if (command -v lsof &> /dev/null && lsof -i :6274 &> /dev/null) || (command -v netstat &> /dev/null && netstat -an | grep ":6274 " | grep LISTEN &> /dev/null); then print_warning "MCP Inspector appears to already be running on port 6274" print_success "Using existing MCP Inspector instance" inspector_available=true else # Configure environment for inspector (env vars, not CLI flags) export DANGEROUSLY_OMIT_AUTH=true export MCP_AUTO_OPEN_ENABLED=false # Use command-line arguments for more reliable configuration local inspector_url="http://localhost:$mcp_port/mcp/" print_local "Configuring MCP Inspector to connect to: $inspector_url" ensure_logs_dir # Install and start MCP Inspector 0.16.5 specifically print_local "Installing MCP Inspector 0.16.5..." npx --yes @modelcontextprotocol/inspector@0.16.5 --transport streamable-http --server-url "$inspector_url" > logs/inspector.log 2>&1 & local inspector_pid=$! print_local "Waiting for MCP Inspector to start..." local attempts=0 local max_attempts=10 local inspector_running=false local last_log_size=0 while [ $attempts -lt $max_attempts ]; do sleep 2 if ! kill -0 $inspector_pid 2>/dev/null; then print_error "MCP Inspector process died. Check inspector.log for details." break fi if [ -f logs/inspector.log ]; then local current_log_size=$(wc -c < logs/inspector.log 2>/dev/null || echo 0) if [ $current_log_size -gt $last_log_size ]; then local last_line=$(tail -1 logs/inspector.log | grep -v "^$" || true) if [ -n "$last_line" ]; then print_local "Inspector: $last_line" fi last_log_size=$current_log_size fi fi # Enhanced health check: Wait for HTTP endpoint to be accessible if command -v curl &> /dev/null; then print_local "Testing MCP Inspector HTTP endpoint (http://localhost:6274)..." if curl -s -f "http://localhost:6274" > /dev/null 2>&1; then inspector_running=true print_success "MCP Inspector HTTP endpoint is responding!" break else print_local "HTTP endpoint not yet responding (attempt $attempts/$max_attempts)..." fi elif command -v lsof &> /dev/null; then if lsof -i :6274 &> /dev/null; then inspector_running=true break fi elif command -v netstat &> /dev/null; then if netstat -an | grep ":6274 " | grep LISTEN &> /dev/null; then inspector_running=true break fi else if [ -f logs/inspector.log ] && grep -q "MCP Inspector is up and running" logs/inspector.log; then inspector_running=true break fi if [ $attempts -ge 5 ]; then inspector_running=true break fi fi attempts=$((attempts + 1)) print_local "Attempt $attempts/$max_attempts - waiting for inspector..." done if [ "$inspector_running" = true ]; then # Final verification: Ensure the HTTP endpoint is fully accessible if command -v curl &> /dev/null; then print_local "Final verification: Testing MCP Inspector accessibility..." local final_attempts=0 local max_final_attempts=5 local final_success=false while [ $final_attempts -lt $max_final_attempts ]; do if curl -s -f "http://localhost:6274" > /dev/null 2>&1; then final_success=true break fi sleep 1 final_attempts=$((final_attempts + 1)) print_local "Final verification attempt $final_attempts/$max_final_attempts..." done if [ "$final_success" = true ]; then print_success "MCP Inspector started successfully on port 6274 and is fully accessible!" echo $inspector_pid > .inspector_pid inspector_available=true else print_warning "MCP Inspector started but HTTP endpoint is not fully accessible" inspector_available=false fi else print_success "MCP Inspector started successfully on port 6274" echo $inspector_pid > .inspector_pid inspector_available=true fi else print_warning "Failed to start MCP Inspector" if [ -f logs/inspector.log ]; then print_warning "Inspector log (last 10 lines):" tail -10 logs/inspector.log echo fi if kill -0 $inspector_pid 2>/dev/null; then kill $inspector_pid 2>/dev/null fi inspector_available=false fi fi fi else print_error "MCP server is not listening on port $mcp_port" print_error "Server process ID: $server_pid" # Show server logs for debugging if [ -f logs/mcp_server.log ]; then print_error "=== MCP Server Log (last 20 lines) ===" tail -20 logs/mcp_server.log echo fi print_error "Attempting to restart server in foreground for debugging..." kill $server_pid 2>/dev/null sleep 2 print_local "Running server in foreground mode for debugging..." print_local "If this works, there might be a background process issue..." # Try running in foreground exec uv run fastmcp run src/server.py --transport http --port $mcp_port fi # Update cleanup function to include server PID cleanup() { print_local "Shutting down services..." # Kill MCP Server if it was started by this script if [ -n "$server_pid" ] && kill -0 $server_pid 2>/dev/null; then print_local "Stopping MCP Server (PID: $server_pid)..." kill $server_pid 2>/dev/null fi # Kill MCP Inspector if it was started by this script if [ -f .inspector_pid ]; then local pid=$(cat .inspector_pid) if kill -0 $pid 2>/dev/null; then print_local "Stopping MCP Inspector (PID: $pid)..." kill $pid 2>/dev/null fi rm -f .inspector_pid fi # Clean up log files rm -f logs/inspector.log logs/inspector_alt.log logs/mcp_server.log print_success "Cleanup complete!" exit 0 } # Re-set up signal handling for cleanup with updated function trap cleanup INT TERM # Keep the script running and monitoring print_success "✅ MCP Server setup complete!" # Show final summary with access points echo print_status "🎉 Local MCP Server Ready!" echo print_status "📋 Access Points:" echo " 🔌 MCP Server (stdio): Available for MCP clients" echo " 🔌 MCP Server (HTTP): http://localhost:$mcp_port" if [ "$inspector_available" = true ]; then echo " 📊 MCP Inspector: http://localhost:6274" echo print_status "🎯 Testing Instructions:" echo " 1. Open http://localhost:6274 in your browser" echo " 2. Ensure that Streamable HTTP is set" echo " 3. MCP Inspector is pre-configured to connect to port $mcp_port" echo " 4. Click 'Connect' button at the bottom" echo " 5. Test tools and resources interactively" else echo print_status "🎯 Alternative Testing:" echo " 1. Open https://inspector.mcp.dev/ in your browser" echo " 2. Ensure that Streamable HTTP is set" echo " 3. Enter server URL: http://localhost:$mcp_port/mcp/" echo " 4. Click 'Connect' button at the bottom" echo " 5. Test tools and resources interactively" echo print_warning "💡 MCP Inspector Troubleshooting:" echo " • Check if Node.js is installed: node --version (requires v22+)" echo " • Check inspector logs: cat logs/inspector.log" echo " • Try manual install: npm install -g @modelcontextprotocol/inspector@0.16.5" echo " • Manual start: DANGEROUSLY_OMIT_AUTH=true DEFAULT_TRANSPORT=streamable-http DEFAULT_SERVER_URL=http://localhost:$mcp_port/mcp/ npx @modelcontextprotocol/inspector@0.16.5" fi echo print_status "📊 Log Files:" echo " 📄 MCP Server: logs/mcp_server.log" if [ "$inspector_available" = true ]; then echo " 📄 MCP Inspector: logs/inspector.log" fi echo print_status "🛑 To stop the server:" echo " Press Ctrl+C or run: pkill -f 'fastmcp run'" echo print_local "Server is running in background. Use Ctrl+C to stop." # Monitor both processes while true; do # Check if server is still running if ! kill -0 $server_pid 2>/dev/null; then print_error "MCP server process died unexpectedly!" if [ -f logs/mcp_server.log ]; then print_error "=== Recent server logs ===" tail -10 logs/mcp_server.log fi break fi sleep 5 done } # Function to determine if Splunk should be run in Docker should_run_splunk_docker() { # Check if SPLUNK_HOST is not set or is set to 'so1' (our Docker Splunk container) if [ -z "$SPLUNK_HOST" ] || [ "$SPLUNK_HOST" = "so1" ]; then return 0 # true - run Splunk in Docker else return 1 # false - use external Splunk fi } # Function to get Docker compose command get_docker_compose_cmd() { local mode=$1 local base_cmd="docker compose" case $mode in "dev") base_cmd="docker compose -f docker-compose-dev.yml" ;; "prod"|"") base_cmd="docker compose" ;; esac echo "$base_cmd" } # Function to stop Docker services stop_docker_services() { print_status "Stopping Docker services..." # Check if docker-compose is available if ! command -v docker-compose &> /dev/null; then print_error "docker-compose not found. Cannot stop services." exit 1 fi # Load environment variables from .env file for Docker Compose if [ -f .env ]; then load_env_file fi # Determine which profiles to use based on current configuration local compose_cmd compose_cmd=$(get_docker_compose_cmd "dev") print_status "Using command: $compose_cmd down" # Stop all services if eval "$compose_cmd down"; then print_success "All Docker services stopped successfully!" # Check if there are any remaining resources print_status "Checking for remaining resources..." if docker network ls | grep -q "mcp-server-for-splunk"; then print_warning "Some networks may still exist. You can remove them manually with:" print_warning " docker network prune" fi if docker volume ls | grep -q "mcp-server-for-splunk"; then print_warning "Some volumes may still exist. You can remove them manually with:" print_warning " docker volume prune" fi else print_error "Failed to stop some Docker services" print_warning "You may need to stop them manually:" print_warning " docker ps -a | grep mcp-server-for-splunk" print_warning " docker stop <container_id>" exit 1 fi } # Function to run Docker setup run_docker_setup() { print_status "Using Docker deployment mode..." # Check if docker-compose is available if ! command -v docker-compose &> /dev/null; then print_error "docker-compose not found. Please install docker-compose or use local mode." print_error "To install docker-compose: https://docs.docker.com/compose/install/" echo print_error "📚 For detailed installation instructions, see:" print_error " docs/getting-started/installation.md#-docker-installation-optional---for-full-stack" exit 1 fi # If uv is available, ensure uv.lock is up to date for Docker build if check_uv; then print_status "uv detected. Ensuring uv.lock is up to date for Docker build..." # Check if uv.lock exists and is current if [ ! -f "uv.lock" ] || [ "pyproject.toml" -nt "uv.lock" ]; then print_status "Updating uv.lock file..." uv sync --dev print_success "uv.lock updated successfully!" else print_status "uv.lock is already up to date." fi else print_warning "uv not found. Docker will use existing uv.lock file (if present)." if [ ! -f "uv.lock" ]; then print_warning "No uv.lock file found. Docker build may fail." print_warning "Consider installing uv to generate the lock file: curl -LsSf https://astral.sh/uv/install.sh | sh" fi fi # Check if .env file exists if [ ! -f .env ]; then print_warning ".env file not found. Creating from env.example..." cp env.example .env print_warning "Created .env file. You may want to edit it with your Splunk configuration." fi # Prompt for Splunk configuration with Docker mode enabled prompt_splunk_config true # Load environment variables from .env file for Docker Compose load_env_file # Ask user for Docker deployment mode echo print_status "Choose Docker deployment mode:" echo " 1) Production (default) - Optimized for performance, no hot reload" echo " 2) Development - Hot reload enabled, enhanced debugging" echo read -p "Enter your choice (1 or 2, default: 1): " docker_choice docker_choice=${docker_choice:-1} local docker_mode="" local service_name="mcp-server" case $docker_choice in 1) docker_mode="prod" print_status "Using Production mode (optimized performance)" ;; 2) docker_mode="dev" service_name="mcp-server-dev" print_status "Using Development mode (hot reload enabled)" ;; *) print_warning "Invalid choice. Using Production mode (default)." docker_mode="prod" ;; esac # Get the appropriate docker-compose command local compose_cmd compose_cmd=$(get_docker_compose_cmd "$docker_mode") # Show Splunk configuration info if should_run_splunk_docker; then print_local "Including Splunk Enterprise container (SPLUNK_HOST=${SPLUNK_HOST:-'not set'} indicates local Splunk)" print_local "Using profiles: dev + splunk for complete stack" else print_local "Using external Splunk instance: $SPLUNK_HOST" print_local "Using profile: dev only (no local Splunk)" fi print_status "Building Docker image..." eval "$compose_cmd build $service_name" if [ $? -eq 0 ]; then print_success "Docker image built successfully!" else print_error "Failed to build Docker image" exit 1 fi print_status "Starting services with docker-compose..." eval "$compose_cmd up -d" if [ $? -eq 0 ]; then print_success "Services started successfully!" else print_error "Failed to start services" exit 1 fi # Wait a moment for services to start sleep 5 print_status "Checking service status..." eval "$compose_cmd ps" # Wait for MCP Inspector to be ready print_status "Waiting for MCP Inspector to be ready..." local inspector_ready=false local inspector_attempts=0 local max_inspector_attempts=30 while [ $inspector_attempts -lt $max_inspector_attempts ] && [ "$inspector_ready" = false ]; do if command -v curl &> /dev/null; then if curl -s -f "http://localhost:6274" > /dev/null 2>&1; then inspector_ready=true print_success "MCP Inspector is ready and accessible at http://localhost:6274" break fi fi sleep 2 inspector_attempts=$((inspector_attempts + 1)) if [ $((inspector_attempts % 5)) -eq 0 ]; then print_local "Waiting for MCP Inspector... (attempt $inspector_attempts/$max_inspector_attempts)" fi done if [ "$inspector_ready" = false ]; then print_warning "MCP Inspector may not be fully ready yet. Check logs:" print_warning " $compose_cmd logs mcp-inspector" fi print_status "Checking MCP server logs..." eval "$compose_cmd logs $service_name --tail=20" echo print_success "🎉 Docker setup complete!" echo print_status "📋 Service URLs:" echo " 🔧 Traefik Dashboard: http://localhost:8080" if should_run_splunk_docker; then echo " 🌐 Splunk Web UI: http://localhost:9000 (admin/Chang3d!)" else echo " 🌐 External Splunk: $SPLUNK_HOST (configured in .env)" fi echo " 🔌 MCP Server: http://localhost:${MCP_SERVER_PORT:-8001}/mcp/" echo " 📊 MCP Inspector: http://localhost:6274" echo print_status "🔍 To check logs:" echo " $compose_cmd logs -f $service_name" echo " $compose_cmd logs -f mcp-inspector" echo print_status "🛑 To stop all services:" echo " $compose_cmd down" if should_run_splunk_docker; then echo " Note: Use '--remove-orphans' flag to ensure complete cleanup:" echo " $compose_cmd down --remove-orphans" fi if [ "$docker_mode" = "dev" ]; then echo print_status "🚀 Development Mode Features:" echo " • Hot reload enabled - changes sync automatically" echo " • Enhanced debugging and logging" echo " • Use: $compose_cmd logs -f $service_name" fi } # Function to find an available port find_available_port() { local start_port=$1 local max_attempts=10 local port=$start_port for ((i=0; i<max_attempts; i++)); do local port_available=true # Check if any process is actively listening on the port if command -v lsof &> /dev/null; then # Only check for LISTEN state, ignore CLOSED connections if lsof -i :$port 2>/dev/null | grep -q "LISTEN"; then port_available=false fi elif command -v netstat &> /dev/null; then # netstat is more reliable - only shows LISTEN state if netstat -an | grep ":$port " | grep -q "LISTEN"; then port_available=false fi fi # If port seems available, try a more direct test if [ "$port_available" = true ]; then # Try to bind to the port using Python (most reliable cross-platform) if command -v python3 &> /dev/null; then if python3 -c "import socket; s=socket.socket(); s.bind(('127.0.0.1', $port)); s.close()" 2>/dev/null; then echo $port return 0 else port_available=false fi elif command -v nc &> /dev/null; then # Fallback to nc if Python not available # Try to connect first - if we can't connect, port is likely free if ! nc -z -w 1 localhost $port 2>/dev/null; then echo $port return 0 else port_available=false fi else # Last resort: try /dev/tcp if ! (echo >/dev/tcp/localhost/$port) 2>/dev/null; then echo $port return 0 else port_available=false fi fi fi if [ "$port_available" = false ]; then print_local "Port $port is in use, trying next port..." >&2 fi port=$((port + 1)) done # If we can't find a port, return the original print_warning "Could not find an available port after $max_attempts attempts" >&2 echo $start_port return 1 } # Splunk configuration will be prompted after deployment mode is chosen # Main logic: Check Docker availability and choose deployment method print_status "Checking available deployment options..." # Check if Docker is running if docker info > /dev/null 2>&1; then print_success "Docker is available and running." DOCKER_AVAILABLE=true else print_warning "Docker is not available." DOCKER_AVAILABLE=false fi # Check if uv is available if check_uv; then print_success "uv package manager is available." UV_AVAILABLE=true else print_warning "uv package manager is not available." UV_AVAILABLE=false fi # Handle forced deployment modes if [ "$FORCE_MODE" = true ]; then case $DEPLOYMENT_MODE in "docker") if [ "$DOCKER_AVAILABLE" = true ]; then print_status "Forcing Docker deployment as requested..." run_docker_setup else print_error "Docker deployment requested but Docker is not available." print_error "Please start Docker or install Docker first." exit 1 fi ;; "local") if [ "$UV_AVAILABLE" = true ]; then print_status "Forcing local deployment as requested..." setup_local_env run_local_server else print_error "Local deployment requested but uv package manager is not available." print_error "Please install uv first: curl -LsSf https://astral.sh/uv/install.sh | sh" exit 1 fi ;; esac exit 0 fi # Handle stop mode if [ "$STOP_MODE" = true ]; then if [ "$DOCKER_AVAILABLE" = true ]; then stop_docker_services else print_error "Docker is not available. Cannot stop services." exit 1 fi exit 0 fi # Interactive mode - ask user preference if both options are available if [ "$DOCKER_AVAILABLE" = true ] && [ "$UV_AVAILABLE" = true ]; then echo print_status "Both Docker and local development options are available." echo "Choose deployment method:" echo " 1) Docker (full stack with Splunk, Traefik, MCP Inspector)" echo " 2) Local (FastMCP server only, lighter weight)" echo read -p "Enter your choice (1 or 2, default: 1): " choice choice=${choice:-1} case $choice in 1) run_docker_setup ;; 2) setup_local_env run_local_server ;; *) print_warning "Invalid choice. Using Docker deployment (default)." run_docker_setup ;; esac elif [ "$DOCKER_AVAILABLE" = true ]; then # Only Docker available, use Docker print_status "Only Docker is available. Using Docker deployment..." run_docker_setup elif [ "$UV_AVAILABLE" = true ]; then # Only uv available, use local mode print_status "Only local development is available. Setting up local mode..." setup_local_env run_local_server else # Neither Docker nor uv available print_error "Neither Docker nor uv package manager are available." print_error "Please install one of the following:" print_error "1. Docker: https://docs.docker.com/get-docker/" print_error "2. uv: curl -LsSf https://astral.sh/uv/install.sh | sh" echo print_error "📚 For detailed installation instructions, see:" print_error " docs/getting-started/installation.md" print_error "" print_error "🔧 You can also run our prerequisite checker to see what's missing:" print_error " ./scripts/check-prerequisites.sh" exit 1 fi

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/deslicer/mcp-for-splunk'

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