Last active
July 17, 2025 02:58
-
-
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
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/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