Skip to main content
Glama
setup.sh8.64 kB
#!/usr/bin/env bash set -euo pipefail # Improved setup script for MCP Server # Usage: ./scripts/setup.sh [--target "$HOME/MCPServer"] [--user username] TARGET_DIR=${TARGET_DIR:-$HOME/MCPServer} INSTALL_USER="${INSTALL_USER:-}" # parse args while [[ $# -gt 0 ]]; do case "$1" in --target) TARGET_DIR="$2"; shift 2 ;; --user) INSTALL_USER="$2"; shift 2 ;; *) echo "Unknown arg: $1"; exit 1 ;; esac done if [ "$TARGET_DIR" = "$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" ]; then echo "Refusing to install into source repository path: $TARGET_DIR" >&2 echo "Please specify --target \"$HOME/MCPServer\" or another directory under your home." >&2 exit 1 fi echo "Setting up MCP server in: $TARGET_DIR" # Ensure source dir SRC_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" echo "Source directory: $SRC_DIR" mkdir -p "$TARGET_DIR/bin" "$TARGET_DIR/logs" "$TARGET_DIR/run" "$TARGET_DIR/config" # If an install user was supplied, try to set ownership (best-effort) if [ -n "$INSTALL_USER" ]; then if id "$INSTALL_USER" >/dev/null 2>&1; then chown -R "$INSTALL_USER":"$INSTALL_USER" "$TARGET_DIR" || true else echo "Warning: install user '$INSTALL_USER' not found; continuing as current user" fi fi # Create venv VENV_DIR="$TARGET_DIR/venv" if [ ! -d "$VENV_DIR" ]; then echo "Creating venv at $VENV_DIR" if command -v python3 >/dev/null 2>&1; then python3 -m venv "$VENV_DIR" else python -m venv "$VENV_DIR" fi fi # Install requirements into venv using venv python if [ -f "$SRC_DIR/requirements.txt" ]; then echo "Installing pip requirements into venv..." "$VENV_DIR/bin/python" -m pip install --upgrade pip setuptools wheel "$VENV_DIR/bin/python" -m pip install -r "$SRC_DIR/requirements.txt" else echo "No requirements.txt found in source; skipping pip install." fi # Copy application code (exclude docs and probes by default) echo "Copying application files to $TARGET_DIR/config (excluding docs/probes)..." rsync -a --delete --exclude='.git' --exclude='*.md' --exclude='tools/mcp_probe.py' \ --exclude='__pycache__' --exclude='logs' --exclude='.vscode' \ "$SRC_DIR/" "$TARGET_DIR/config/" # Ensure .env exists: prefer existing .env in repo, else copy .env.example if [ -f "$SRC_DIR/.env" ]; then cp -f "$SRC_DIR/.env" "$TARGET_DIR/config/.env" elif [ -f "$SRC_DIR/.env.example" ]; then echo "Copying .env.example to $TARGET_DIR/config/.env (please edit credentials)" cp -f "$SRC_DIR/.env.example" "$TARGET_DIR/config/.env" fi echo "Generating runtime wrappers in $TARGET_DIR/bin" cat > "$TARGET_DIR/bin/start.sh" <<'EOF' #!/usr/bin/env bash set -euo pipefail DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" VENV="$DIR/venv" # shellcheck source=/dev/null source "$VENV/bin/activate" export PYTHONUNBUFFERED=1 # Load env if present if [ -f "$DIR/config/.env" ]; then # Normalize CRLF to LF to avoid $'\r' errors if command -v dos2unix >/dev/null 2>&1; then dos2unix "$DIR/config/.env" >/dev/null 2>&1 || true else if grep -q $'\r' "$DIR/config/.env" >/dev/null 2>&1; then sed -i 's/\r$//' "$DIR/config/.env" || true fi fi set -o allexport # shellcheck disable=SC1091 source "$DIR/config/.env" set +o allexport fi # Logs and pid LOG="$DIR/logs/mcp-server.log" PIDFILE="$DIR/run/mcp-server.pid" PGIDFILE="$DIR/run/mcp-server.pgid" cmd="$VENV/bin/python -u $DIR/config/server.py" if [ "${1:-}" = "foreground" ]; then exec $cmd fi # Prevent duplicate starts if [ -f "$PIDFILE" ]; then EXISTING_PID=$(cat "$PIDFILE" 2>/dev/null || echo "") if [ -n "$EXISTING_PID" ] && kill -0 "$EXISTING_PID" 2>/dev/null; then echo "Server already running (PID $EXISTING_PID)." exit 0 else echo "Removing stale pidfile $PIDFILE" >&2 rm -f "$PIDFILE" || true rm -f "$PGIDFILE" || true fi fi echo "Starting MCP server..." # Start in its own session so we can stop the group later setsid nohup $cmd >> "$LOG" 2>&1 & PID=$! echo "$PID" > "$PIDFILE" sleep 0.2 if command -v ps >/dev/null 2>&1; then PGID=$(ps -o pgid= "$PID" | tr -d ' ' || true) else PGID="" fi if [ -n "$PGID" ]; then echo "$PGID" > "$PGIDFILE" fi sleep 0.5 if kill -0 "$PID" >/dev/null 2>&1; then echo "Started: PID $PID, PGID ${PGID:-unknown}, logs: $LOG" else echo "Failed to start server; check $LOG for errors." >&2 tail -n 50 "$LOG" >&2 || true exit 1 fi EOF chmod +x "$TARGET_DIR/bin/start.sh" cat > "$TARGET_DIR/bin/stop.sh" <<'EOF' #!/usr/bin/env bash set -euo pipefail DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" PIDFILE="$DIR/run/mcp-server.pid" PGIDFILE="$DIR/run/mcp-server.pgid" if [ ! -f "$PIDFILE" ]; then echo "No PID file found; server may not be running." exit 0 fi PID="$(cat "$PIDFILE")" if [ -f "/proc/$PID/cmdline" ]; then CMDLINE=$(tr '\0' ' ' < /proc/$PID/cmdline) if ! echo "$CMDLINE" | grep -E "(python|uvicorn|server.py)" >/dev/null 2>&1; then echo "PID $PID does not look like our server process: $CMDLINE" >&2 echo "Removing stale pidfile." >&2 rm -f "$PIDFILE" || true rm -f "$PGIDFILE" || true exit 0 fi fi # If we have a PGID, prefer stopping the whole group if [ -f "$PGIDFILE" ]; then PGID=$(cat "$PGIDFILE" 2>/dev/null || echo "") if [ -n "$PGID" ]; then echo "Stopping process group $PGID..." kill -TERM -"$PGID" 2>/dev/null || true for i in {1..8}; do if ! kill -0 -"$PGID" >/dev/null 2>&1; then break fi sleep 1 done if kill -0 -"$PGID" >/dev/null 2>&1; then echo "Process group $PGID did not stop; force killing..." kill -KILL -"$PGID" 2>/dev/null || true fi rm -f "$PGIDFILE" || true fi fi if kill -0 "$PID" >/dev/null 2>&1; then echo "Stopping PID $PID..." kill -TERM "$PID" 2>/dev/null || true for i in {1..6}; do if ! kill -0 "$PID" >/dev/null 2>&1; then break fi sleep 1 done if kill -0 "$PID" >/dev/null 2>&1; then echo "PID $PID did not exit, force killing..." kill -KILL "$PID" 2>/dev/null || true fi else echo "Process $PID not running; cleaning up pidfile." >&2 fi rm -f "$PIDFILE" || true echo "Stopped" EOF chmod +x "$TARGET_DIR/bin/stop.sh" cat > "$TARGET_DIR/bin/health_check.sh" <<'EOF' #!/usr/bin/env bash set -euo pipefail DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" if [ -f "$DIR/config/.env" ]; then if command -v dos2unix >/dev/null 2>&1; then dos2unix "$DIR/config/.env" >/dev/null 2>&1 || true else if grep -q $'\r' "$DIR/config/.env" >/dev/null 2>&1; then sed -i 's/\r$//' "$DIR/config/.env" || true fi fi set -o allexport # shellcheck disable=SC1091 source "$DIR/config/.env" set +o allexport fi HOST=${MCP_HOST:-127.0.0.1} PORT=${MCP_PORT:-3000} URL="http://$HOST:$PORT/health" if command -v curl >/dev/null 2>&1; then HTTP=$(curl -s -o /dev/null -w "%{http_code}" --max-time 5 "$URL" || true) if [ "$HTTP" = "200" ]; then echo "OK" exit 0 else echo "UNHEALTHY (HTTP $HTTP)" exit 1 fi else PIDFILE="$DIR/run/mcp-server.pid" if [ -f "$PIDFILE" ] && kill -0 "$(cat "$PIDFILE")" 2>/dev/null; then echo "OK (process running)" exit 0 fi echo "UNHEALTHY (no curl and no running process)" exit 1 fi EOF chmod +x "$TARGET_DIR/bin/health_check.sh" cat > "$TARGET_DIR/bin/uninstall.sh" <<'EOF' #!/usr/bin/env bash set -euo pipefail DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" echo "Stopping and removing installation at $DIR" if [ -f "$DIR/run/mcp-server.pid" ]; then "$DIR/bin/stop.sh" || true fi if [ "${FORCE:-}" = "yes" ]; then echo "FORCE=yes set; removing installation and logs without prompting" rm -rf "$DIR" || true echo "Installation directory removed" else echo "You are about to remove: $DIR" read -p "Type the exact path to confirm deletion: " -r CONFIRM_PATH if [ "$CONFIRM_PATH" != "$DIR" ]; then echo "Confirmation did not match. Aborting without changes." exit 1 fi rm -rf "$DIR" echo "Installation directory removed" fi echo "Uninstalled." EOF chmod +x "$TARGET_DIR/bin/uninstall.sh" echo "Setup complete. Runtime wrappers are in $TARGET_DIR/bin" echo "To start: $TARGET_DIR/bin/start.sh | To stop: $TARGET_DIR/bin/stop.sh | Health: $TARGET_DIR/bin/health_check.sh"

Latest Blog Posts

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/dnchandra/mcp_linux'

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