Skip to content

Commit 8800a35

Browse files
mojwangclaude
andcommitted
fix: improve MCP server setup reliability and bash compatibility
- Replace eval with indirect variable expansion for better security - Add bash 4+ version check with clear error messages - Fix API key loading to properly detect existing keys - Improve MCP server build detection to skip unnecessary rebuilds - Better error handling when MCP servers already exist in Claude Code - Enhanced Homebrew output to show what's actually being updated This fixes the issue where setup.sh would prompt for API keys on every run even when they were already configured. Also improves performance by skipping unnecessary MCP server builds. 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
1 parent 46bf056 commit 8800a35

4 files changed

Lines changed: 526 additions & 50 deletions

File tree

lib/common.sh

Lines changed: 43 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,14 @@
77
# Note: Strict mode is not set here to allow sourcing from various scripts
88
# Individual scripts should set their own error handling as needed
99

10+
# Check bash version requirement (bash 4+ required for associative arrays)
11+
if [[ "${BASH_VERSION%%.*}" -lt 4 ]]; then
12+
echo "Error: This script requires bash 4.0 or higher (found $BASH_VERSION)" >&2
13+
echo "Please run with Homebrew bash: brew install bash" >&2
14+
echo "Ensure /opt/homebrew/bin is in your PATH" >&2
15+
exit 1
16+
fi
17+
1018
# Prevent multiple sourcing of this file to avoid readonly variable errors
1119
if [[ -n "${COMMON_LIB_LOADED:-}" ]]; then
1220
return 0
@@ -506,13 +514,30 @@ export -f kill_all_test_jobs
506514
# ============================================================================
507515

508516
# MCP server configurations - base paths
517+
# Check both possible locations for official servers (src/ subfolder or direct)
518+
get_mcp_server_base_path() {
519+
local server_name="$1"
520+
521+
# Official servers - check both locations
522+
if [[ "$server_name" =~ ^(filesystem|memory|sequentialthinking|git|fetch)$ ]]; then
523+
if [[ -d "$HOME/repos/mcp-servers/official/src/$server_name" ]]; then
524+
echo "$HOME/repos/mcp-servers/official/src/$server_name"
525+
elif [[ -d "$HOME/repos/mcp-servers/official/$server_name" ]]; then
526+
echo "$HOME/repos/mcp-servers/official/$server_name"
527+
fi
528+
# Community servers
529+
elif [[ -d "$HOME/repos/mcp-servers/community/$server_name" ]]; then
530+
echo "$HOME/repos/mcp-servers/community/$server_name"
531+
fi
532+
}
533+
509534
declare -A MCP_SERVER_BASE_PATHS=(
510-
# Official servers
511-
["filesystem"]="$HOME/repos/mcp-servers/official/filesystem"
512-
["memory"]="$HOME/repos/mcp-servers/official/memory"
513-
["sequentialthinking"]="$HOME/repos/mcp-servers/official/sequentialthinking"
514-
["git"]="$HOME/repos/mcp-servers/official/git"
515-
["fetch"]="$HOME/repos/mcp-servers/official/fetch"
535+
# Official servers - will be dynamically determined
536+
["filesystem"]="$(get_mcp_server_base_path filesystem)"
537+
["memory"]="$(get_mcp_server_base_path memory)"
538+
["sequentialthinking"]="$(get_mcp_server_base_path sequentialthinking)"
539+
["git"]="$(get_mcp_server_base_path git)"
540+
["fetch"]="$(get_mcp_server_base_path fetch)"
516541
# Community servers
517542
["context7"]="$HOME/repos/mcp-servers/community/context7"
518543
["playwright"]="$HOME/repos/mcp-servers/community/playwright"
@@ -574,7 +599,14 @@ find_mcp_server_executable() {
574599
return 0
575600
fi
576601

577-
local base_path="${MCP_SERVER_BASE_PATHS[$server_name]}"
602+
# Get the base path dynamically
603+
local base_path=$(get_mcp_server_base_path "$server_name")
604+
605+
# If base path doesn't exist, return error
606+
if [[ -z "$base_path" ]] || [[ ! -d "$base_path" ]]; then
607+
return 1
608+
fi
609+
578610
local executables="${MCP_SERVER_EXECUTABLES[$server_name]}"
579611

580612
# Python servers just use the base directory
@@ -737,10 +769,11 @@ is_mcp_server_installed() {
737769
return 0
738770
fi
739771

740-
local base_path="${MCP_SERVER_BASE_PATHS[$server_name]}"
772+
# Get the base path dynamically
773+
local base_path=$(get_mcp_server_base_path "$server_name")
741774

742775
# Check if base path exists
743-
if [[ ! -d "$base_path" ]]; then
776+
if [[ -z "$base_path" ]] || [[ ! -d "$base_path" ]]; then
744777
return 1
745778
fi
746779

@@ -755,6 +788,7 @@ is_mcp_server_installed() {
755788
}
756789

757790
# Export MCP functions
791+
export -f get_mcp_server_base_path
758792
export -f find_mcp_server_executable
759793
export -f generate_mcp_server_config
760794
export -f is_mcp_server_installed

scripts/setup-claude-code-mcp.sh

Lines changed: 87 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -46,48 +46,68 @@ add_claude_code_server() {
4646

4747
print_info "Adding $server_name to Claude Code (scope: $scope)..."
4848

49+
# Capture the command output and exit code
50+
local cmd_output
51+
local cmd_exit_code
52+
4953
case "$server_type" in
5054
"node")
5155
if [[ "$server_name" == "filesystem" ]]; then
52-
claude mcp add "$server_name" -s "$scope" node "$server_path" "$HOME"
56+
cmd_output=$(claude mcp add "$server_name" -s "$scope" node "$server_path" "$HOME" 2>&1)
57+
cmd_exit_code=$?
5358
elif [[ -n "$api_key_var" ]]; then
54-
claude mcp add "$server_name" -s "$scope" node "$server_path" --env "${api_key_var}=${!api_key_var}"
59+
cmd_output=$(claude mcp add "$server_name" -s "$scope" node "$server_path" --env "${api_key_var}=${!api_key_var}" 2>&1)
60+
cmd_exit_code=$?
5561
else
56-
claude mcp add "$server_name" -s "$scope" node "$server_path"
62+
cmd_output=$(claude mcp add "$server_name" -s "$scope" node "$server_path" 2>&1)
63+
cmd_exit_code=$?
5764
fi
5865
;;
5966
"python-uv")
60-
claude mcp add "$server_name" -s "$scope" uv --directory "$server_path" run "mcp-server-$server_name"
67+
# Claude Code requires the command to be passed correctly for Python servers
68+
cmd_output=$(claude mcp add "$server_name" -s "$scope" -- sh -c "cd '$server_path' && uv run mcp-server-$server_name" 2>&1)
69+
cmd_exit_code=$?
6170
;;
6271
"python-uvx")
6372
if [[ "$server_name" == "semgrep" ]]; then
64-
claude mcp add "$server_name" -s "$scope" uvx --with mcp==1.11.0 semgrep-mcp
73+
cmd_output=$(claude mcp add "$server_name" -s "$scope" -- uvx --with mcp==1.11.0 semgrep-mcp 2>&1)
74+
cmd_exit_code=$?
6575
fi
6676
;;
6777
"npx")
6878
local npx_package="${MCP_SERVER_NPX_PACKAGES[$server_name]}"
6979
if [[ "$server_name" == "figma" ]]; then
7080
if [[ -n "$api_key_var" ]]; then
71-
claude mcp add "$server_name" -s "$scope" --env "${api_key_var}=${!api_key_var}" -- npx -y "$npx_package" --stdio
81+
cmd_output=$(claude mcp add "$server_name" -s "$scope" --env "${api_key_var}=${!api_key_var}" -- npx -y "$npx_package" --stdio 2>&1)
82+
cmd_exit_code=$?
7283
else
73-
claude mcp add "$server_name" -s "$scope" -- npx -y "$npx_package" --stdio
84+
cmd_output=$(claude mcp add "$server_name" -s "$scope" -- npx -y "$npx_package" --stdio 2>&1)
85+
cmd_exit_code=$?
7486
fi
7587
else
7688
if [[ -n "$api_key_var" ]]; then
77-
claude mcp add "$server_name" -s "$scope" --env "${api_key_var}=${!api_key_var}" -- npx -y "$npx_package"
89+
cmd_output=$(claude mcp add "$server_name" -s "$scope" --env "${api_key_var}=${!api_key_var}" -- npx -y "$npx_package" 2>&1)
90+
cmd_exit_code=$?
7891
else
79-
claude mcp add "$server_name" -s "$scope" -- npx -y "$npx_package"
92+
cmd_output=$(claude mcp add "$server_name" -s "$scope" -- npx -y "$npx_package" 2>&1)
93+
cmd_exit_code=$?
8094
fi
8195
fi
8296
;;
8397
esac
8498

85-
if [[ $? -eq 0 ]]; then
99+
if [[ $cmd_exit_code -eq 0 ]]; then
86100
print_success "Added $server_name"
87101
return 0
88102
else
89-
print_error "Failed to add $server_name"
90-
return 1
103+
# Check if the error is because the server already exists
104+
if echo "$cmd_output" | grep -q "already exists"; then
105+
print_info "Server $server_name already exists in $scope config - skipping"
106+
return 0 # Not really an error, server is already configured
107+
else
108+
print_error "Failed to add $server_name: $cmd_output"
109+
return 1
110+
fi
91111
fi
92112
}
93113

@@ -114,14 +134,20 @@ print_usage() {
114134
echo " --no-api-keys Skip servers that require API keys"
115135
echo " --servers LIST Only configure specified servers (comma-separated)"
116136
echo " --remove Remove all MCP servers before adding"
137+
echo " --force Force reconnect all servers (ignores update status)"
117138
echo " --help, -h Show this help message"
118139
echo ""
140+
echo "Behavior:"
141+
echo " By default, only reconnects servers that were actually updated."
142+
echo " Use --force to reconnect all servers regardless of update status."
143+
echo ""
119144
echo "Available servers:"
120145
echo " Official: filesystem, memory, git, fetch, sequentialthinking"
121146
echo " Community: context7, playwright, figma, semgrep, exa"
122147
echo ""
123148
echo "Examples:"
124-
echo " $0 # Add all servers to user scope"
149+
echo " $0 # Smart reconnect (only if updated)"
150+
echo " $0 --force # Force reconnect all servers"
125151
echo " $0 --scope project # Add to project scope (.mcp.json)"
126152
echo " $0 --no-api-keys # Skip servers needing API keys"
127153
echo " $0 --servers filesystem,memory,git # Only add specific servers"
@@ -133,8 +159,13 @@ main() {
133159
local SCOPE="user"
134160
local INCLUDE_API_KEYS=true
135161
local REMOVE_FIRST=false
162+
local FORCE_RECONNECT=false
136163
local SERVERS_TO_INCLUDE=()
137164

165+
# Check for update status file to determine if reconnection is needed
166+
local UPDATE_STATUS_FILE="/tmp/mcp-update-status"
167+
local UPDATED_SERVERS=()
168+
138169
# Parse command line options
139170
while [[ $# -gt 0 ]]; do
140171
case $1 in
@@ -158,6 +189,11 @@ main() {
158189
REMOVE_FIRST=true
159190
shift
160191
;;
192+
--force)
193+
FORCE_RECONNECT=true
194+
REMOVE_FIRST=true # Force implies remove first
195+
shift
196+
;;
161197
--help|-h)
162198
print_usage
163199
exit 0
@@ -182,15 +218,44 @@ main() {
182218
source "$HOME/.config/zsh/51-api-keys.zsh" 2>/dev/null || true
183219
fi
184220

185-
# Remove existing servers if requested
186-
if [[ "$REMOVE_FIRST" == "true" ]]; then
221+
# Check for updated servers if not forcing
222+
if [[ "$FORCE_RECONNECT" == "false" ]] && [[ -f "$UPDATE_STATUS_FILE" ]]; then
223+
# Read updated servers from file
224+
while IFS= read -r server; do
225+
[[ -n "$server" ]] && UPDATED_SERVERS+=("$server")
226+
done < "$UPDATE_STATUS_FILE"
227+
228+
if [[ ${#UPDATED_SERVERS[@]} -eq 0 ]]; then
229+
print_info "No MCP servers were updated, skipping reconnection"
230+
echo ""
231+
print_info "Use --force to force reconnection of all servers"
232+
exit 0
233+
else
234+
print_info "Servers that were updated: ${UPDATED_SERVERS[*]}"
235+
echo ""
236+
fi
237+
fi
238+
239+
# Remove existing servers if requested or if forcing
240+
if [[ "$REMOVE_FIRST" == "true" ]] || [[ "$FORCE_RECONNECT" == "true" ]]; then
187241
remove_all_servers "$SCOPE"
242+
elif [[ ${#UPDATED_SERVERS[@]} -gt 0 ]]; then
243+
# Only remove the servers that were updated
244+
print_info "Removing updated servers for reconnection..."
245+
for server in "${UPDATED_SERVERS[@]}"; do
246+
print_info "Removing $server..."
247+
claude mcp remove "$server" -s "$SCOPE" 2>/dev/null || true
248+
done
188249
fi
189250

190251
# Determine which servers to configure
191252
if [[ ${#SERVERS_TO_INCLUDE[@]} -gt 0 ]]; then
192253
# Use specified servers
193254
servers=("${SERVERS_TO_INCLUDE[@]}")
255+
elif [[ "$FORCE_RECONNECT" == "false" ]] && [[ ${#UPDATED_SERVERS[@]} -gt 0 ]]; then
256+
# Only reconnect updated servers
257+
servers=("${UPDATED_SERVERS[@]}")
258+
print_info "Reconnecting only updated servers: ${servers[*]}"
194259
else
195260
# Use all available servers
196261
servers=(
@@ -210,18 +275,22 @@ main() {
210275
local success_count=0
211276
for server in "${servers[@]}"; do
212277
# Skip API key servers if not including them
213-
if [[ "$INCLUDE_API_KEYS" == "false" ]] && [[ -n "${MCP_SERVER_API_KEYS[$server]}" ]]; then
278+
if [[ "$INCLUDE_API_KEYS" == "false" ]] && [[ -n "${MCP_SERVER_API_KEYS[$server]:-}" ]]; then
214279
print_info "Skipping $server (requires API key)"
215280
continue
216281
fi
217282

218283
if add_claude_code_server "$server" "$SCOPE"; then
219-
((success_count++))
284+
success_count=$((success_count + 1))
220285
fi
221286
done
222287

223288
echo ""
224-
print_success "Successfully configured $success_count MCP servers for Claude Code"
289+
if [[ $success_count -gt 0 ]]; then
290+
print_success "Successfully configured $success_count MCP servers for Claude Code"
291+
else
292+
print_info "All MCP servers were already configured for Claude Code"
293+
fi
225294

226295
# Show current configuration
227296
echo ""

0 commit comments

Comments
 (0)