Skip to content

Commit fdaf11a

Browse files
committed
fix(idempotency): anchor MCP detection greps at "^<name>:"
`claude mcp list` prints one MCP per line as "<name>: <command>...". Plain substring greps (e.g. `grep -q "notion"`) also match the package/URL in another MCP's command string — so every registered MCP's command contains its own name, and many contain other MCPs' names too (e.g. @notionhq/..., fidgetcoding-morgen-mcp, @playwright/mcp). That means a substring check false-positives whenever a user has a different MCP registered under a custom name but using the same package. Switch every `claude mcp list | grep` check in step-4 (fidgetflo), step-5 (11 productivity MCPs), and uninstall.sh to `grep -qE '^<name>:'` so we only match the exact MCP we registered. Uses the same anchor pattern step-7 already uses for GitHub. Also adds `local` qualifiers to two function-scoped variables in step-4's configure_mcp / init_config that were leaking into global scope.
1 parent 0fed24e commit fdaf11a

3 files changed

Lines changed: 107 additions & 77 deletions

File tree

‎step-4/step-4-install.sh‎

Lines changed: 20 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -111,20 +111,22 @@ install_fidgetflo() {
111111
configure_mcp() {
112112
info "Adding fidgetflo as MCP server to Claude Code..."
113113

114-
# Check if already configured
115-
if claude mcp list 2>/dev/null | grep -q "fidgetflo" 2>/dev/null; then
114+
# Check if already configured. `claude mcp list` prints "<name>: <cmd>...",
115+
# so anchor at "^fidgetflo:" — the command (`npx -y fidgetflo`) also contains
116+
# the word "fidgetflo", which would false-positive on a plain substring grep.
117+
if claude mcp list 2>/dev/null | grep -qE '^fidgetflo:' 2>/dev/null; then
116118
success "fidgetflo MCP server already configured"
117119
return
118120
fi
119121

120122
claude mcp add fidgetflo -- npx -y fidgetflo 2>/dev/null
121123

122-
if claude mcp list 2>/dev/null | grep -q "fidgetflo" 2>/dev/null; then
124+
if claude mcp list 2>/dev/null | grep -qE '^fidgetflo:' 2>/dev/null; then
123125
success "fidgetflo MCP server added to Claude Code"
124126
else
125127
# Try alternative approach — write directly to config
126128
warn "MCP add command may not have worked. Trying direct config..."
127-
CLAUDE_MCP_CONFIG="$HOME/.claude/claude_mcp_config.json"
129+
local CLAUDE_MCP_CONFIG="$HOME/.claude/claude_mcp_config.json"
128130
if [ -f "$CLAUDE_MCP_CONFIG" ]; then
129131
if ! grep -q "fidgetflo" "$CLAUDE_MCP_CONFIG" 2>/dev/null; then
130132
jq '.mcpServers["fidgetflo"] = {"command": "npx", "args": ["-y", "fidgetflo"]}' "$CLAUDE_MCP_CONFIG" > "${CLAUDE_MCP_CONFIG}.tmp" \
@@ -195,7 +197,7 @@ init_config() {
195197

196198
# fidgetflo init may write a verbose statusLine to project-level .claude/settings.json.
197199
# Remove it so our clean global statusline (Final Step) isn't overridden.
198-
PROJECT_SETTINGS=".claude/settings.json"
200+
local PROJECT_SETTINGS=".claude/settings.json"
199201
if [ -f "$PROJECT_SETTINGS" ] && command -v jq &>/dev/null; then
200202
if jq -e '.statusLine' "$PROJECT_SETTINGS" &>/dev/null; then
201203
jq 'del(.statusLine)' "$PROJECT_SETTINGS" > "${PROJECT_SETTINGS}.tmp" \
@@ -242,14 +244,14 @@ configure_model_defaults() {
242244
npx fidgetflo config set --key "model.routing.minModel" --value "opus" 2>/dev/null || true
243245

244246
# Disable automatic model routing (CLI can't pass boolean false, so patch config directly)
245-
CONFIG_FILE=".claude-flow/config.json"
247+
local CONFIG_FILE=".claude-flow/config.json"
246248
if [ -f "$CONFIG_FILE" ] && command -v jq &>/dev/null; then
247249
jq '.scopes.project["model.default"] = "opus" | .scopes.system["model.default"] = "opus" | .scopes.project["model.routing.enabled"] = false | .scopes.system["model.routing.enabled"] = false' "$CONFIG_FILE" > "${CONFIG_FILE}.tmp" \
248250
&& mv "${CONFIG_FILE}.tmp" "$CONFIG_FILE"
249251
fi
250252

251253
# Also patch config.yaml if it exists (init creates this file)
252-
YAML_CONFIG=".claude-flow/config.yaml"
254+
local YAML_CONFIG=".claude-flow/config.yaml"
253255
if [ -f "$YAML_CONFIG" ]; then
254256
if grep -q "default:" "$YAML_CONFIG" 2>/dev/null; then
255257
sed -i.bak 's/default:.*/default: opus/' "$YAML_CONFIG" && rm -f "${YAML_CONFIG}.bak"
@@ -267,6 +269,9 @@ configure_model_defaults() {
267269
install_swarm_skills() {
268270
info "Installing swarm skills and statusline..."
269271

272+
local FSWARM_DIR FHIVE_DIR FMINI_DIR FMINI1_DIR FMINI2_DIR FMINI3_DIR FMINIMAX_DIR
273+
local FSWARM1_DIR FSWARM2_DIR FSWARM3_DIR FSWARMMAX_DIR W4W_DIR STATUSLINE_DIR SETTINGS_FILE EXISTING
274+
270275
# --- /fswarm skill ---
271276
FSWARM_DIR="$HOME/.claude/skills/fswarm"
272277
mkdir -p "$FSWARM_DIR"
@@ -1065,8 +1070,9 @@ run_self_test() {
10651070
echo -e "${BLUE}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}"
10661071
echo ""
10671072

1068-
TEST_PASS=0
1069-
TEST_FAIL=0
1073+
local TEST_PASS=0
1074+
local TEST_FAIL=0
1075+
local CONFIG_JSON MODEL_CONFIG MEMORY_OK
10701076

10711077
# fidgetflo CLI available
10721078
if npx fidgetflo --version &>/dev/null 2>&1; then
@@ -1077,8 +1083,10 @@ run_self_test() {
10771083
TEST_FAIL=$((TEST_FAIL + 1))
10781084
fi
10791085

1080-
# MCP server configured
1081-
if claude mcp list 2>/dev/null | grep -q "fidgetflo" 2>/dev/null; then
1086+
# MCP server configured — anchor on "^fidgetflo:" so a substring in some
1087+
# other MCP's command string (the package name "fidgetflo" appears in
1088+
# `npx -y fidgetflo`) can't false-positive this test.
1089+
if claude mcp list 2>/dev/null | grep -qE '^fidgetflo:' 2>/dev/null; then
10821090
success "TEST: fidgetflo MCP server configured"
10831091
TEST_PASS=$((TEST_PASS + 1))
10841092
else
@@ -1214,7 +1222,7 @@ run_self_test() {
12141222

12151223
# Model set to Opus (check config.json first, then config.yaml, then CLI)
12161224
CONFIG_JSON=".claude-flow/config.json"
1217-
if [ -f "$CONFIG_JSON" ] && jq -e '.scopes.project["model.default"] == "opus" or .scopes.system["model.default"] == "opus"' "$CONFIG_JSON" &>/dev/null; then
1225+
if [ -f "$CONFIG_JSON" ] && command -v jq &>/dev/null && jq -e '.scopes.project["model.default"] == "opus" or .scopes.system["model.default"] == "opus"' "$CONFIG_JSON" &>/dev/null; then
12181226
success "TEST: Model locked to Opus"
12191227
TEST_PASS=$((TEST_PASS + 1))
12201228
elif grep -q "default: opus" ".claude-flow/config.yaml" 2>/dev/null; then

0 commit comments

Comments
 (0)