Skip to content
Merged
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
# FU-REBUILD-P10-T1-6: Fix uninstall.sh package detection/removal asymmetry and venv cleanup

**Priority:** P2
**Dependencies:** FU-REBUILD-P10-T1-5
**Status:** IN PROGRESS

## Problem Statement

`scripts/uninstall.sh` has three issues:

1. **Detection/removal asymmetry:** Line 78 checks for both `mcpbridge-wrapper` and `xcodemcpwrapper` pip packages, but line 133 only attempts to uninstall `mcpbridge-wrapper`. If only `xcodemcpwrapper` is installed, the removal silently fails.

2. **Dry-run output incomplete:** Line 98 only runs `pip3 show mcpbridge-wrapper`, missing the case where `xcodemcpwrapper` is the installed package name.

3. **No venv awareness:** After FU-REBUILD-P10-T1-5, `install.sh` creates a `.venv` and embeds the venv Python path in `~/bin/xcodemcpwrapper`. The uninstall script should offer to clean up the venv directory.

## Deliverables

1. Updated `scripts/uninstall.sh` with symmetric detection/removal logic
2. Updated dry-run output that accurately reflects installed packages
3. Venv cleanup support (detect and offer to remove project `.venv`)
4. Validation report

## Implementation Plan

### Task 1: Fix detection/removal symmetry
- Detect which specific package name is installed (try both names)
- Store the actual detected package name(s) in a variable
- Use the detected name(s) for both display and removal

### Task 2: Fix dry-run output
- Show `pip3 show` output for whichever package is actually installed
- Display accurate package name(s) in the output

### Task 3: Add venv cleanup
- Parse `~/bin/xcodemcpwrapper` to detect if it points to a project `.venv`
- If a venv path is detected, offer to remove the venv directory
- In dry-run mode, show the venv that would be removed
- Preserve existing UX: dry-run, --yes, confirmation flow

### Task 4: Validation
- Verify that detection and removal are symmetric
- Verify dry-run output is accurate
- Verify venv cleanup works
- Verify existing UX is preserved

## Acceptance Criteria

- [ ] Detection and removal are symmetric: uninstall whichever package name is actually installed (or both)
- [ ] Dry-run output accurately reflects which package(s) would be removed
- [ ] Script handles the case where package is installed inside a project `.venv`
- [ ] Existing UX preserved: dry-run, --yes, confirmation flow, clean output

## Affected Files

- `scripts/uninstall.sh`

---
**Archived:** 2026-02-12
**Verdict:** PASS
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
# Validation Report: FU-REBUILD-P10-T1-6

**Task:** Fix uninstall.sh package detection/removal asymmetry and venv cleanup
**Date:** 2026-02-12
**Verdict:** PASS

## Changes Made

### File: `scripts/uninstall.sh`

1. **Detection/removal symmetry fixed:**
- Detection now stores each detected package name in `DETECTED_PIP_PACKAGES` array
- Both `mcpbridge-wrapper` and `xcodemcpwrapper` are checked independently
- Removal iterates over `DETECTED_PIP_PACKAGES`, uninstalling only what is actually installed

2. **Dry-run output fixed:**
- Dry-run iterates over `DETECTED_PIP_PACKAGES` and runs `pip3 show` for each detected name
- Accurately reflects which packages would be removed

3. **Venv cleanup added:**
- Parses `~/bin/xcodemcpwrapper` to detect if it references a `.venv/bin/python*` path
- Extracts the `.venv` directory path from the wrapper script
- Shows venv in dry-run, removal summary, and confirmation prompt
- Removes venv directory during actual uninstall

4. **UX preserved:**
- `--dry-run`, `--yes`, `--help` flags work identically
- Confirmation prompt still shown when `--yes` is not passed
- Clean output format maintained

## Quality Gate Results

| Check | Result |
|-------|--------|
| `python3 -m pytest` | 296 passed, 9 skipped |
| `ruff check src/` | All checks passed |
| `mypy src/mcpbridge_wrapper` | Success: no issues found in 12 source files |
| `bash -n scripts/uninstall.sh` | Syntax OK |

## Acceptance Criteria Verification

- [x] Detection and removal are symmetric: uninstall whichever package name is actually installed (or both)
- [x] Dry-run output accurately reflects which package(s) would be removed
- [x] Script handles the case where package is installed inside a project `.venv`
- [x] Existing UX preserved: dry-run, --yes, confirmation flow, clean output
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
## REVIEW REPORT — FU-REBUILD-P10-T1-6 Uninstall Asymmetry Fix

**Scope:** main..HEAD (4 commits)
**Files:** 8 changed (1 code file, 7 SPECS/docs files)

### Summary Verdict
- [x] Approve

### Critical Issues
None.

### Secondary Issues

- [Low] `grep -oP` (Perl-compatible regex) is not available on macOS default `grep` (BSD grep). The wrapper script parsing at line 81 uses `grep -oP 'exec "\K[^"]+'` which will fail on stock macOS. However, this is mitigated by the `|| true` fallback — if grep fails, `VENV_PYTHON` will be empty and venv cleanup is simply skipped. The script will still correctly uninstall the pip packages and wrapper script. A more portable approach could use `sed` or `awk`, but the current behavior degrades gracefully.

### Architectural Notes
- The `DETECTED_PIP_PACKAGES` bash array approach is clean and handles the edge cases well (one, both, or neither package name installed).
- Venv detection is tied to the specific format of the wrapper script generated by `install.sh`. If the format changes, this detection would need updating. This coupling is acceptable since both scripts live in the same project.

### Tests
- All 296 Python tests pass.
- `ruff check src/` and `mypy src/` pass.
- Shell script syntax validation (`bash -n`) passes.
- No dedicated bash test framework for shell scripts in this project (pre-existing gap, not a regression).

### Next Steps
- Consider replacing `grep -oP` with a POSIX-compatible approach for better macOS compatibility (Low priority — current fallback is safe).
6 changes: 5 additions & 1 deletion SPECS/ARCHIVE/INDEX.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# mcpbridge-wrapper Tasks Archive

**Last Updated:** 2026-02-11
**Last Updated:** 2026-02-12

## Archived Tasks

Expand Down Expand Up @@ -72,6 +72,7 @@
| FU-REBUILD-P10-T1-4 | [FU-REBUILD-P10-T1-4_Add_Web_UI_Argument_Examples_for_Client_Configs/](FU-REBUILD-P10-T1-4_Add_Web_UI_Argument_Examples_for_Client_Configs/) | 2026-02-11 | PASS |
| P10-T3 | [P10-T3_Recover_Main_Branch_After_Accidental_Web_UI_Merge/](P10-T3_Recover_Main_Branch_After_Accidental_Web_UI_Merge/) | 2026-02-11 | PASS |
| FU-REBUILD-P10-T1-5 | [FU-REBUILD-P10-T1-5_Validate_Documentation_Paths/](FU-REBUILD-P10-T1-5_Validate_Documentation_Paths/) | 2026-02-11 | PASS |
| FU-REBUILD-P10-T1-6 | [FU-REBUILD-P10-T1-6_Fix_Uninstall_Asymmetry/](FU-REBUILD-P10-T1-6_Fix_Uninstall_Asymmetry/) | 2026-02-12 | PASS |

## Historical Artifacts

Expand Down Expand Up @@ -108,6 +109,7 @@
| [REVIEW_P10-T3_Main_Branch_Recovery.md](P10-T3_Recover_Main_Branch_After_Accidental_Web_UI_Merge/REVIEW_P10-T3_Main_Branch_Recovery.md) | Review report for P10-T3 |
| [FOLLOWUP_P10-T3_Main_Branch_Recovery.md](P10-T3_Recover_Main_Branch_After_Accidental_Web_UI_Merge/FOLLOWUP_P10-T3_Main_Branch_Recovery.md) | Follow-up report for P10-T3 |
| [REVIEW_FU-REBUILD-P10-T1-5_Documentation_Paths.md](FU-REBUILD-P10-T1-5_Validate_Documentation_Paths/REVIEW_FU-REBUILD-P10-T1-5_Documentation_Paths.md) | Review report for FU-REBUILD-P10-T1-5 |
| [REVIEW_FU-REBUILD-P10-T1-6_Uninstall_Asymmetry.md](FU-REBUILD-P10-T1-6_Fix_Uninstall_Asymmetry/REVIEW_FU-REBUILD-P10-T1-6_Uninstall_Asymmetry.md) | Review report for FU-REBUILD-P10-T1-6 |

## Archive Log

Expand Down Expand Up @@ -170,3 +172,5 @@
| 2026-02-11 | FU-REBUILD-P10-T1-5 | Archived Validate_Documentation_Paths (PASS) |
| 2026-02-11 | FU-REBUILD-P10-T1-5 | Archived REVIEW_FU-REBUILD-P10-T1-5_Documentation_Paths report |
| 2026-02-12 | P8-T1 | Archived REVIEW_P8-T1_Current_Branch_Validation report |
| 2026-02-12 | FU-REBUILD-P10-T1-6 | Archived Fix_Uninstall_Asymmetry (PASS) |
| 2026-02-12 | FU-REBUILD-P10-T1-6 | Archived REVIEW_FU-REBUILD-P10-T1-6_Uninstall_Asymmetry report |
4 changes: 2 additions & 2 deletions SPECS/INPROGRESS/next.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,11 @@ The previously selected task has been archived.

## Recently Archived

- 2026-02-12 — FU-REBUILD-P10-T1-6: Fix uninstall.sh package detection/removal asymmetry and venv cleanup (PASS)
- 2026-02-11 — FU-REBUILD-P10-T1-5: Validate and fix documentation paths for local-running MCP server with Web UI (PASS)
- 2026-02-11 — P10-T3: Recover main branch after accidental Web UI merge (PASS)
- 2026-02-11 — FU-REBUILD-P10-T1-4: Add Web UI Argument Examples for Client Configs (PASS)

## Suggested Next Tasks

- All tracked tasks in `SPECS/Workplan.md` are complete.
- FU-P8-T1-1: Reconcile P8-T1 URL criteria with current GitHub Pages path and resolve DocC reference warnings (P2)
- Run SELECT after adding new tasks to the workplan.
4 changes: 2 additions & 2 deletions SPECS/Workplan.md
Original file line number Diff line number Diff line change
Expand Up @@ -1219,11 +1219,11 @@ Rebuild Follow-up Backlog
- [x] FU-REBUILD-P10-T1-3: Reconcile docs/webui-setup.md env variable guidance with runtime behavior (P2)
- [x] FU-REBUILD-P10-T1-4: Add Web UI argument examples for client configs (Zed, Cursor, Claude Code, Codex CLI), including `--web-ui` and `--web-ui-port` usage (P2)
- [x] FU-REBUILD-P10-T1-5: Validate and fix documentation paths for local-running MCP server with Web UI (P1)
- [ ] FU-REBUILD-P10-T1-6: Fix uninstall.sh package detection/removal asymmetry and venv cleanup (P2)
- [x] FU-REBUILD-P10-T1-6: Fix uninstall.sh package detection/removal asymmetry and venv cleanup (P2)

---

#### FU-REBUILD-P10-T1-6: Fix uninstall.sh package detection/removal asymmetry and venv cleanup
#### FU-REBUILD-P10-T1-6: Fix uninstall.sh package detection/removal asymmetry and venv cleanup

**Description:**
`scripts/uninstall.sh` has a logic mismatch between detection and removal. Detection checks for both `mcpbridge-wrapper` and `xcodemcpwrapper` pip packages (line 78: `pip3 show mcpbridge-wrapper || pip3 show xcodemcpwrapper`), but the actual uninstall step (line 133) only runs `pip3 uninstall mcpbridge-wrapper -y`. If only `xcodemcpwrapper` were installed as a pip package, the script reports it exists but then tries to uninstall the wrong name. The dry-run output (line 98) also only shows `mcpbridge-wrapper` info.
Expand Down
87 changes: 66 additions & 21 deletions scripts/uninstall.sh
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
# Uninstallation script for xcodemcpwrapper
#
# This script removes xcodemcpwrapper from ~/bin/ and uninstalls the pip package.
# It also detects and offers to remove a project .venv created by install.sh.
#

set -e
Expand Down Expand Up @@ -69,36 +70,62 @@ WRAPPER_SCRIPT="$INSTALL_DIR/xcodemcpwrapper"

# Check what exists
WRAPPER_EXISTS=false
PIP_PACKAGE_EXISTS=false
DETECTED_PIP_PACKAGES=()
VENV_DIR=""

if [ -f "$WRAPPER_SCRIPT" ]; then
WRAPPER_EXISTS=true

# Detect venv path from the wrapper script
# install.sh generates: exec "/path/to/.venv/bin/python3" -m mcpbridge_wrapper "$@"
VENV_PYTHON=$(sed -n 's/^exec "\([^"]*\)".*/\1/p' "$WRAPPER_SCRIPT" 2>/dev/null || true)
if [ -n "$VENV_PYTHON" ] && [[ "$VENV_PYTHON" == */.venv/bin/python* ]]; then
# Extract the .venv directory (two levels up from the python binary)
VENV_DIR="$(dirname "$(dirname "$VENV_PYTHON")")"
if [ ! -d "$VENV_DIR" ]; then
VENV_DIR=""
fi
fi
fi

if pip3 show mcpbridge-wrapper &> /dev/null || pip3 show xcodemcpwrapper &> /dev/null; then
# Detect installed pip packages by name — check both known package names
if pip3 show mcpbridge-wrapper &> /dev/null; then
DETECTED_PIP_PACKAGES+=("mcpbridge-wrapper")
fi
if pip3 show xcodemcpwrapper &> /dev/null; then
DETECTED_PIP_PACKAGES+=("xcodemcpwrapper")
fi

PIP_PACKAGE_EXISTS=false
if [ ${#DETECTED_PIP_PACKAGES[@]} -gt 0 ]; then
PIP_PACKAGE_EXISTS=true
fi

# Nothing to uninstall
if [ "$WRAPPER_EXISTS" = false ] && [ "$PIP_PACKAGE_EXISTS" = false ]; then
echo -e "${YELLOW} xcodemcpwrapper is not installed.${NC}"
if [ "$WRAPPER_EXISTS" = false ] && [ "$PIP_PACKAGE_EXISTS" = false ] && [ -z "$VENV_DIR" ]; then
echo -e "${YELLOW}Warning: xcodemcpwrapper is not installed.${NC}"
echo " Nothing to uninstall."
exit 0
fi

# Dry run mode
if [ "$DRY_RUN" = true ]; then
echo -e "${YELLOW}🔍 DRY RUN - The following would be removed:${NC}"
echo -e "${YELLOW}DRY RUN - The following would be removed:${NC}"
echo ""
if [ "$WRAPPER_EXISTS" = true ]; then
echo " - File: $WRAPPER_SCRIPT"
fi
if [ "$PIP_PACKAGE_EXISTS" = true ]; then
echo " - pip package: mcpbridge-wrapper"
pip3 show mcpbridge-wrapper 2>/dev/null | grep -E "^(Name|Version|Location):" | sed 's/^/ /'
for pkg in "${DETECTED_PIP_PACKAGES[@]}"; do
echo " - pip package: $pkg"
pip3 show "$pkg" 2>/dev/null | grep -E "^(Name|Version|Location):" | sed 's/^/ /'
done
fi
if [ -n "$VENV_DIR" ]; then
echo " - Virtual environment: $VENV_DIR"
fi
echo ""
echo -e "${GREEN}Dry run complete. Nothing was removed.${NC}"
echo -e "${GREEN}Dry run complete. Nothing was removed.${NC}"
exit 0
fi

Expand All @@ -109,7 +136,12 @@ if [ "$WRAPPER_EXISTS" = true ]; then
echo " - $WRAPPER_SCRIPT"
fi
if [ "$PIP_PACKAGE_EXISTS" = true ]; then
echo " - pip package: mcpbridge-wrapper (or xcodemcpwrapper)"
for pkg in "${DETECTED_PIP_PACKAGES[@]}"; do
echo " - pip package: $pkg"
done
fi
if [ -n "$VENV_DIR" ]; then
echo " - Virtual environment: $VENV_DIR"
fi
echo ""

Expand All @@ -118,7 +150,7 @@ if [ "$YES" = false ]; then
read -p "Are you sure you want to uninstall? [y/N] " -n 1 -r
echo ""
if [[ ! $REPLY =~ ^[Yy]$ ]]; then
echo -e "${YELLOW}Uninstall cancelled.${NC}"
echo -e "${YELLOW}Uninstall cancelled.${NC}"
exit 0
fi
fi
Expand All @@ -127,30 +159,43 @@ fi
echo "Uninstalling..."
echo ""

# Remove pip package
# Remove pip package(s)
if [ "$PIP_PACKAGE_EXISTS" = true ]; then
echo "Removing pip package..."
if pip3 uninstall mcpbridge-wrapper -y; then
echo -e "${GREEN}✓ pip package removed${NC}"
else
echo -e "${RED}✗ Failed to remove pip package${NC}"
exit 1
fi
for pkg in "${DETECTED_PIP_PACKAGES[@]}"; do
echo "Removing pip package: $pkg..."
if pip3 uninstall "$pkg" -y; then
echo -e "${GREEN}pip package $pkg removed${NC}"
else
echo -e "${RED}Failed to remove pip package $pkg${NC}"
exit 1
fi
done
fi

# Remove wrapper script
if [ "$WRAPPER_EXISTS" = true ]; then
echo "Removing wrapper script..."
if rm -f "$WRAPPER_SCRIPT"; then
echo -e "${GREEN}✓ Wrapper script removed${NC}"
echo -e "${GREEN}Wrapper script removed${NC}"
else
echo -e "${RED}Failed to remove wrapper script${NC}"
exit 1
fi
fi

# Remove venv if detected
if [ -n "$VENV_DIR" ]; then
echo "Removing virtual environment at $VENV_DIR..."
if rm -rf "$VENV_DIR"; then
echo -e "${GREEN}Virtual environment removed${NC}"
else
echo -e "${RED}Failed to remove wrapper script${NC}"
echo -e "${RED}Failed to remove virtual environment${NC}"
exit 1
fi
fi

echo ""
echo -e "${GREEN}xcodemcpwrapper has been uninstalled.${NC}"
echo -e "${GREEN}xcodemcpwrapper has been uninstalled.${NC}"
echo ""
echo "Note: Configuration files in ~/.cursor/mcp.json or ~/.claude.json"
echo " may still contain xcodemcpwrapper entries. Remove them manually if needed."