Skip to content

Instantly share code, notes, and snippets.

@zudsniper
Last active July 17, 2025 02:58
Show Gist options
  • Save zudsniper/c18fe765ddc4155bead02f5f2ced8f0d to your computer and use it in GitHub Desktop.
Save zudsniper/c18fe765ddc4155bead02f5f2ced8f0d to your computer and use it in GitHub Desktop.
Comprehensive setup script for npx-for-claude - supports macOS and Linux with colorized output
#!/bin/bash
# npx-for-claude Setup Script
# Supports macOS and Linux (Ubuntu/Debian)
# Configures NVM, creates wrapper script, and sets up shell environment
# ANSI Color Codes
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
MAGENTA='\033[0;35m'
CYAN='\033[0;36m'
WHITE='\033[1;37m'
NC='\033[0m' # No Color
# Symbols
CHECK_MARK='\033[0;32m✓\033[0m'
CROSS_MARK='\033[0;31m✗\033[0m'
INFO_MARK='\033[0;34mℹ\033[0m'
WARNING_MARK='\033[1;33m⚠\033[0m'
# Function to print colored output
print_header() {
echo -e "\n${CYAN}════════════════════════════════════════════════════════════════════════════════${NC}"
echo -e "${WHITE}$1${NC}"
echo -e "${CYAN}════════════════════════════════════════════════════════════════════════════════${NC}\n"
}
print_step() {
echo -e "${BLUE}[STEP]${NC} $1"
}
print_success() {
echo -e "${CHECK_MARK} ${GREEN}$1${NC}"
}
print_error() {
echo -e "${CROSS_MARK} ${RED}$1${NC}"
}
print_warning() {
echo -e "${WARNING_MARK} ${YELLOW}$1${NC}"
}
print_info() {
echo -e "${INFO_MARK} ${CYAN}$1${NC}"
}
# Detect operating system
detect_os() {
if [[ "$OSTYPE" == "darwin"* ]]; then
OS="macos"
SHELL_CONFIG_DIR="$HOME"
SYSTEM_SHELL_CONFIG="/etc"
elif [[ "$OSTYPE" == "linux-gnu"* ]]; then
OS="linux"
SHELL_CONFIG_DIR="$HOME"
SYSTEM_SHELL_CONFIG="/etc"
else
print_error "Unsupported operating system: $OSTYPE"
exit 1
fi
}
# Detect shell
detect_shell() {
CURRENT_SHELL=$(basename "$SHELL")
case "$CURRENT_SHELL" in
"zsh")
SHELL_CONFIG_FILE="$SHELL_CONFIG_DIR/.zshenv"
SYSTEM_SHELL_CONFIG_FILE="$SYSTEM_SHELL_CONFIG/zshenv"
;;
"bash")
SHELL_CONFIG_FILE="$SHELL_CONFIG_DIR/.bashrc"
SYSTEM_SHELL_CONFIG_FILE="$SYSTEM_SHELL_CONFIG/bash.bashrc"
;;
*)
print_warning "Unsupported shell: $CURRENT_SHELL. Defaulting to bash configuration."
SHELL_CONFIG_FILE="$SHELL_CONFIG_DIR/.bashrc"
SYSTEM_SHELL_CONFIG_FILE="$SYSTEM_SHELL_CONFIG/bash.bashrc"
;;
esac
}
# Check if NVM is installed
check_nvm() {
if [ -s "$HOME/.nvm/nvm.sh" ]; then
print_success "NVM is already installed"
return 0
else
print_warning "NVM is not installed"
return 1
fi
}
# Install NVM
install_nvm() {
print_step "Installing NVM..."
# Download and install NVM
curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.7/install.sh | bash
if [ $? -eq 0 ]; then
print_success "NVM installed successfully"
# Source NVM to make it available in current session
export NVM_DIR="$HOME/.nvm"
[ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh"
[ -s "$NVM_DIR/bash_completion" ] && \. "$NVM_DIR/bash_completion"
return 0
else
print_error "Failed to install NVM"
return 1
fi
}
# Configure system-wide shell environment (for macOS)
configure_system_shell_macos() {
print_step "Configuring system-wide shell environment for macOS..."
# Create/update /etc/zshenv for macOS
if [ "$CURRENT_SHELL" == "zsh" ]; then
cat > /tmp/system_zshenv << 'EOF'
## /etc/zshenv
# MacOS puts this in /etc/zprofile originally, but it clobbers certain PATH-dependent utilities, namely NVM
if [ -x /usr/libexec/path_helper ]; then
eval `/usr/libexec/path_helper -s`
fi
EOF
# Check if we need sudo
if [ -w "$SYSTEM_SHELL_CONFIG_FILE" ]; then
cp /tmp/system_zshenv "$SYSTEM_SHELL_CONFIG_FILE"
else
print_info "Requires sudo to modify system shell configuration..."
sudo cp /tmp/system_zshenv "$SYSTEM_SHELL_CONFIG_FILE"
fi
rm /tmp/system_zshenv
print_success "System shell configuration updated"
fi
}
# Configure user shell environment
configure_user_shell() {
print_step "Configuring user shell environment..."
# Create backup of existing config
if [ -f "$SHELL_CONFIG_FILE" ]; then
cp "$SHELL_CONFIG_FILE" "$SHELL_CONFIG_FILE.backup.$(date +%Y%m%d_%H%M%S)"
print_info "Backup created: $SHELL_CONFIG_FILE.backup.$(date +%Y%m%d_%H%M%S)"
fi
# Create the shell configuration
if [ "$CURRENT_SHELL" == "zsh" ]; then
cat > "$SHELL_CONFIG_FILE" << 'EOF'
## ~/.zshenv
function run() {
## normal .zshenv content... ##
# This is the standard NVM initialization. It's what enables you to use NVM in general, and an alias in npx-for-claude in particular.
export NVM_DIR="$HOME/.nvm"
[ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh" # This loads nvm
[ -s "$NVM_DIR/bash_completion" ] && \. "$NVM_DIR/bash_completion" # This loads nvm bash_completion
## ... other shell settings for interactive and non-interactive uses ... ##
}
## This next bit shuts off unwanted STDOUT output when running a non-interactive shell, such as MCPs in Claude Desktop.
## Without this redirection, any messages hitting STDOUT will foul the MCP server loads.
## Ref: https://stackoverflow.com/a/44661168/4346960
OUTPUT=1
if [[ ! -o interactive ]]; then
OUTPUT=3
eval "exec $OUTPUT<>/dev/null"
fi
run >& $OUTPUT
if [[ ! -o interactive ]]; then
eval "exec $OUTPUT>&-"
fi
EOF
else
# Bash configuration
cat >> "$SHELL_CONFIG_FILE" << 'EOF'
# NVM configuration for npx-for-claude
export NVM_DIR="$HOME/.nvm"
[ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh" # This loads nvm
[ -s "$NVM_DIR/bash_completion" ] && \. "$NVM_DIR/bash_completion" # This loads nvm bash_completion
# Silence output for non-interactive shells (MCP servers)
if [[ $- != *i* ]]; then
exec 3>&1 4>&2 1>/dev/null 2>&1
fi
EOF
fi
print_success "User shell configuration updated"
}
# Install Node.js and create claude alias
setup_node_and_alias() {
print_step "Setting up Node.js and creating 'claude' alias..."
# Source NVM to make it available
export NVM_DIR="$HOME/.nvm"
[ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh"
# Install latest LTS Node.js
nvm install --lts
nvm use --lts
# Create claude alias pointing to the LTS version
CURRENT_NODE_VERSION=$(nvm current)
nvm alias claude "$CURRENT_NODE_VERSION"
print_success "Node.js $CURRENT_NODE_VERSION installed and 'claude' alias created"
# Set as default
nvm alias default claude
print_success "Set 'claude' as default Node.js version"
}
# Create scripts directory if it doesn't exist
create_scripts_dir() {
SCRIPTS_DIR="$HOME/scripts"
if [ ! -d "$SCRIPTS_DIR" ]; then
mkdir -p "$SCRIPTS_DIR"
print_success "Created scripts directory: $SCRIPTS_DIR"
fi
# Add scripts directory to PATH if not already there
if [[ ":$PATH:" != *":$SCRIPTS_DIR:"* ]]; then
echo "export PATH=\"\$PATH:$SCRIPTS_DIR\"" >> "$SHELL_CONFIG_FILE"
export PATH="$PATH:$SCRIPTS_DIR"
print_success "Added $SCRIPTS_DIR to PATH"
fi
}
# Create npx-for-claude wrapper script
create_npx_wrapper() {
print_step "Creating npx-for-claude wrapper script..."
WRAPPER_SCRIPT="$HOME/scripts/npx-for-claude"
cat > "$WRAPPER_SCRIPT" << 'EOF'
#!/usr/bin/env bash
# npx-for-claude wrapper script
# Ensures NVM is loaded and uses the 'claude' alias for Node.js
# Properly sets all environment variables for Node.js/npm ecosystem
# Initialize NVM
export NVM_DIR="$HOME/.nvm"
[ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh" > /dev/null 2>&1
# Use the claude alias and capture the node path
nvm use claude > /dev/null 2>&1
# Get the current Node.js version path
NODE_VERSION=$(nvm current)
NODE_PATH="$NVM_DIR/versions/node/$NODE_VERSION"
# Export all necessary environment variables
export PATH="$NODE_PATH/bin:$PATH"
export NODE_PATH="$NODE_PATH/lib/node_modules"
export npm_config_prefix="$NODE_PATH"
export npm_config_cache="$HOME/.npm"
export npm_config_globalconfig="$NODE_PATH/etc/npmrc"
export npm_config_userconfig="$HOME/.npmrc"
export npm_config_init_module="$HOME/.npm-init.js"
# Execute npx with all arguments
npx "$@"
EOF
# Make the script executable
chmod +x "$WRAPPER_SCRIPT"
print_success "npx-for-claude wrapper script created at: $WRAPPER_SCRIPT"
}
# Test the setup
test_setup() {
print_step "Testing the setup..."
# Source the shell configuration
source "$SHELL_CONFIG_FILE"
# Test npx-for-claude
if command -v npx-for-claude >/dev/null 2>&1; then
print_success "npx-for-claude command is available"
# Test version
VERSION_OUTPUT=$(npx-for-claude --version 2>/dev/null)
if [ $? -eq 0 ]; then
print_success "npx-for-claude --version works: $VERSION_OUTPUT"
else
print_error "npx-for-claude --version failed"
fi
# Test Node.js execution
NODE_VERSION=$(npx-for-claude node --version 2>/dev/null)
if [ $? -eq 0 ]; then
print_success "npx-for-claude node --version works: $NODE_VERSION"
else
print_error "npx-for-claude node --version failed"
fi
# Test simple JavaScript execution
JS_OUTPUT=$(npx-for-claude node -e "console.log('Hello from Claude!')" 2>/dev/null)
if [ $? -eq 0 ] && [ "$JS_OUTPUT" = "Hello from Claude!" ]; then
print_success "JavaScript execution test passed"
else
print_error "JavaScript execution test failed"
fi
else
print_error "npx-for-claude command not found in PATH"
fi
}
# Main setup function
main() {
print_header "npx-for-claude Setup Script"
print_info "This script will configure npx-for-claude for Claude Desktop MCP servers"
print_info "It supports macOS and Linux (Ubuntu/Debian) with bash and zsh shells"
echo -e "\n${YELLOW}Press Enter to continue or Ctrl+C to abort...${NC}"
read -r
# Detect environment
detect_os
detect_shell
print_info "Detected OS: $OS"
print_info "Detected Shell: $CURRENT_SHELL"
print_info "Shell Config: $SHELL_CONFIG_FILE"
# Check and install NVM if needed
if ! check_nvm; then
install_nvm
fi
# Configure system shell (macOS only)
if [ "$OS" == "macos" ]; then
configure_system_shell_macos
fi
# Configure user shell environment
configure_user_shell
# Create scripts directory
create_scripts_dir
# Setup Node.js and create claude alias
setup_node_and_alias
# Create npx-for-claude wrapper
create_npx_wrapper
# Test the setup
test_setup
print_header "Setup Complete!"
print_success "npx-for-claude has been successfully configured!"
print_info "You can now use 'npx-for-claude' instead of 'npx' in your Claude Desktop MCP configuration"
echo -e "\n${CYAN}Example MCP configuration:${NC}"
echo -e "${WHITE}{${NC}"
echo -e "${WHITE} \"mcpServers\": {${NC}"
echo -e "${WHITE} \"filesystem\": {${NC}"
echo -e "${WHITE} \"command\": \"npx-for-claude\",${NC}"
echo -e "${WHITE} \"args\": [${NC}"
echo -e "${WHITE} \"-y\",${NC}"
echo -e "${WHITE} \"@modelcontextprotocol/server-filesystem\",${NC}"
echo -e "${WHITE} \"/path/to/your/directory\"${NC}"
echo -e "${WHITE} ]${NC}"
echo -e "${WHITE} }${NC}"
echo -e "${WHITE} }${NC}"
echo -e "${WHITE}}${NC}"
print_warning "Please restart your terminal or run: source $SHELL_CONFIG_FILE"
}
# Run the main function
main "$@"
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment