Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion SPECS/ARCHIVE/INDEX.md
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
# mcpbridge-wrapper Tasks Archive

**Last Updated:** 2026-03-04 (P1-T6 archived)
**Last Updated:** 2026-03-05 (P4-T1 archived)

## Archived Tasks

| Task ID | Folder | Archived | Verdict |
|---------|--------|----------|---------|
| P4-T1 | [P4-T1_Auto_restart_stale_broker_daemon_on_version_mismatch_after_upgrade/](P4-T1_Auto_restart_stale_broker_daemon_on_version_mismatch_after_upgrade/) | 2026-03-05 | PASS |
| P1-T6 | [P1-T6_Update_webui_setup_and_DocC_mirror_to_use_broker_in_multi_agent_examples/](P1-T6_Update_webui_setup_and_DocC_mirror_to_use_broker_in_multi_agent_examples/) | 2026-03-04 | PASS |
| P1-T5 | [P1-T5_Fix_missed_broker_spawn_references_in_troubleshooting/](P1-T5_Fix_missed_broker_spawn_references_in_troubleshooting/) | 2026-03-04 | PASS |
| P1-T9 | [P1-T9_Add_direct_links_for_all_command_steps_in_FLOW/](P1-T9_Add_direct_links_for_all_command_steps_in_FLOW/) | 2026-03-03 | PASS |
Expand Down Expand Up @@ -185,6 +186,7 @@

| File | Description |
|------|-------------|
| [REVIEW_p4_t1_broker_version_restart.md](_Historical/REVIEW_p4_t1_broker_version_restart.md) | Review report for P4-T1 |
| [REVIEW_p1_t6_webui_broker_examples.md](_Historical/REVIEW_p1_t6_webui_broker_examples.md) | Review report for P1-T6 |
| [REVIEW_p1_t5_troubleshooting_broker.md](_Historical/REVIEW_p1_t5_troubleshooting_broker.md) | Review report for P1-T5 |
| [REVIEW_p1_t9_flow_command_links.md](_Historical/REVIEW_p1_t9_flow_command_links.md) | Review report for P1-T9 |
Expand Down Expand Up @@ -315,6 +317,8 @@

| Date | Task ID | Action |
|------|---------|--------|
| 2026-03-05 | P4-T1 | Archived REVIEW_p4_t1_broker_version_restart report |
| 2026-03-05 | P4-T1 | Archived Auto_restart_stale_broker_daemon_on_version_mismatch_after_upgrade (PASS) |
| 2026-03-04 | P1-T6 | Archived REVIEW_p1_t6_webui_broker_examples report |
| 2026-03-04 | P1-T6 | Archived Update webui-setup.md and DocC mirror to use --broker in multi-agent examples (PASS) |
| 2026-03-04 | P1-T5 | Archived REVIEW_p1_t5_troubleshooting_broker report |
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
# P4-T1 PRD — Auto-restart stale broker daemon on version mismatch after upgrade

## Task Metadata

- **Task ID:** P4-T1
- **Phase:** Phase 4: Broker Lifecycle Management
- **Priority:** P0
- **Dependencies:** none
- **Source:** `SPECS/Workplan.md` open task entry

## Objective Summary

Prevent stale broker daemons from surviving wrapper upgrades and silently serving old behavior to new `--broker` clients. The wrapper must use a single version source of truth, persist daemon runtime version in broker state, detect mismatch before proxy reuse, and automatically restart stale daemons. The CLI must also expose explicit lifecycle control (`--broker-status`, `--broker-stop`) so users and scripts can inspect and remediate broker state without manual PID/socket file handling.

The implementation scope includes runtime behavior, CLI surface, install/uninstall operational safety, docs, and regression tests. Backwards compatibility requirement: legacy daemons without a version file must still be accepted as compatible unless a mismatch can be proven.

## Success Criteria

1. `__version__` is derived from package metadata (`importlib.metadata`) instead of hard-coded constants.
2. Broker daemon writes `broker.version` on startup and removes it during shutdown/cleanup paths.
3. Proxy detects mismatched daemon version and auto-restarts stale daemon before connecting.
4. Missing `broker.version` file is treated as compatible (no forced restart).
5. `--broker-status` reports PID/status/version info and mismatch warning.
6. `--broker-stop` gracefully stops daemon, waits for exit, and removes pid/socket/version state files.
7. Install and uninstall scripts stop any running broker daemon before replacing/removing wrapper files.
8. Broker-mode docs describe new lifecycle commands and version management flow.
9. Required quality gates pass: `pytest`, `ruff check src/`, `mypy src/mcpbridge_wrapper`, `pytest --cov=src/mcpbridge_wrapper --cov-report=term-missing` (>= 90%).

## Acceptance Tests

- Unit tests for `BrokerConfig.version_file` path derivation.
- Unit tests for daemon version-file write/remove and status payload version field.
- Unit tests for proxy version mismatch detection and restart behavior.
- Unit tests for `_parse_broker_args` handling of `--broker-status` and `--broker-stop`.
- CLI behavior validation:
- `python -m mcpbridge_wrapper --broker-status`
- `python -m mcpbridge_wrapper --broker-stop`
- Script-level behavior validation for install/uninstall broker-stop logic (covered by shell-path checks and existing script tests if present).

## Test-First Plan

1. Add/extend tests for version file state in broker config and daemon lifecycle.
2. Add/extend proxy tests for mismatch detection, backward-compatible no-version behavior, and stale daemon restart.
3. Add/extend main/CLI tests for new broker control flags and argument parsing.
4. Implement runtime logic minimally to satisfy new failing tests.
5. Update docs and scripts after runtime tests pass.
6. Run full quality gates and capture outputs in validation report.

## Implementation Plan (Hierarchical TODO)

### Phase A — Version Source and Broker State

- **Inputs:** Existing `__version__` declaration, `BrokerConfig`, daemon lifecycle logic.
- **Outputs:** Metadata-derived version string; `version_file` property; daemon writes/cleans version file; status includes version.
- **Verification:** New daemon/config unit tests pass and stale-lock cleanup removes version artifacts.

### Phase B — Proxy and CLI Lifecycle Controls

- **Inputs:** Proxy spawn/reuse flow, broker arg parsing and command dispatch in `__main__.py`.
- **Outputs:** Version mismatch detection + stale daemon restart; `--broker-status`; `--broker-stop` cleanup behavior.
- **Verification:** New proxy/main tests pass and manual status/stop commands return expected output/exit semantics.

### Phase C — Operational Scripts, Docs, and Validation

- **Inputs:** `scripts/install.sh`, `scripts/uninstall.sh`, `docs/broker-mode.md`, quality gate tooling.
- **Outputs:** Install/uninstall stop running daemon before file replacement/removal; docs updated for status/stop/version behavior; validation report.
- **Verification:** Required quality gates pass; acceptance criteria checklist fully satisfied.

## Decision Points and Constraints

- Use defensive behavior when metadata/version files are unavailable: prefer safe fallback over hard failure.
- Keep broker-stop idempotent (safe when daemon already dead or files are stale/corrupt).
- Avoid introducing behavior that depends on non-portable process APIs beyond existing macOS/Linux constraints.
- Keep implementation scoped to P4-T1 artifacts; do not refactor unrelated broker subsystems.

## Notes (Post-Completion Docs/Artifacts)

- Create `SPECS/INPROGRESS/P4-T1_Validation_Report.md` with command evidence and verdict.
- Archive PRD and validation report to `SPECS/ARCHIVE/P4-T1_Auto_restart_stale_broker_daemon_on_version_mismatch_after_upgrade/`.
- Mark `P4-T1` complete in `SPECS/Workplan.md` and update `SPECS/ARCHIVE/INDEX.md`.
- Create and archive `REVIEW_p4_t1_broker_version_restart.md` after review.

---
**Archived:** 2026-03-05
**Verdict:** PASS
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
# Validation Report — P4-T1

**Task:** P4-T1 — Auto-restart stale broker daemon on version mismatch after upgrade
**Date:** 2026-03-05
**Executor:** Codex (`flow-run`)
**Verdict:** PASS

## Summary

Implemented and validated version-aware broker lifecycle handling across runtime code, CLI, scripts, documentation, and tests.

Key outcomes:
- `__version__` now resolves from package metadata with safe fallback.
- Broker daemon writes/cleans `broker.version` and reports version in status payload.
- Proxy detects daemon version mismatch and restarts stale daemon before reuse.
- Added `--broker-status` and `--broker-stop` commands.
- Install/uninstall scripts now stop running broker daemons before file operations.
- Broker mode docs updated for new status/stop/version management workflow.
- Coverage restored above threshold after adding targeted tests for new paths.

## Acceptance Criteria Check

- [x] `__version__` derived from `importlib.metadata` (single source: `pyproject.toml`)
- [x] Daemon writes `~/.mcpbridge_wrapper/broker.version` on start and cleans on stop
- [x] Proxy auto-restarts daemon when version file mismatches current `__version__`
- [x] No version file (old daemon) treated as compatible
- [x] `--broker-status` prints daemon PID, version, mismatch warning
- [x] `--broker-stop` sends SIGTERM, waits, and cleans up state files
- [x] `scripts/install.sh` stops running broker daemon before writing new wrapper
- [x] `scripts/uninstall.sh` stops running broker daemon before removing files
- [x] `docs/broker-mode.md` documents `--broker-stop`, `--broker-status`, and version management
- [x] All quality gates pass (`pytest`, `ruff`, `mypy`, coverage >= 90%)

## Quality Gate Evidence

```bash
pytest
```

Result: `766 passed, 5 skipped, 2 warnings`.

```bash
ruff check src/
```

Result: `All checks passed!`.

```bash
mypy src/mcpbridge_wrapper
```

Result: `Success: no issues found in 18 source files`.

```bash
pytest --cov=src/mcpbridge_wrapper --cov-report=term-missing
```

Result: `766 passed, 5 skipped, 2 warnings`; total coverage `90.71%` (threshold `90%`).

## Additional Test Coverage Added

- `tests/unit/test_main.py`:
- New coverage for `--broker-status` and `--broker-stop` runtime branches.
- `tests/unit/test_broker_proxy.py`:
- New coverage for version-file read errors and `_stop_stale_daemon` cleanup paths.
- `tests/unit/test_init.py`:
- Metadata-derived version and fallback behavior.

## Notes

- Existing deprecation warnings from websocket dependencies remain unchanged and are non-blocking for this task.
- Ready for ARCHIVE step.
32 changes: 32 additions & 0 deletions SPECS/ARCHIVE/_Historical/REVIEW_p4_t1_broker_version_restart.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
## REVIEW REPORT — P4-T1 Broker version restart

**Scope:** origin/main..HEAD
**Files:** 18

### Summary Verdict
- [x] Approve
- [ ] Approve with comments
- [ ] Request changes
- [ ] Block

### Critical Issues
- None.

### Secondary Issues
- None.

### Architectural Notes
- Version source-of-truth now resolves from package metadata and is consistently reused across daemon/proxy status paths.
- Version mismatch handling is defensive and backward-compatible: no `broker.version` file does not force restart.
- Lifecycle command additions (`--broker-status`, `--broker-stop`) improve operability without changing normal stdio bridge behavior.

### Tests
- Quality gates are documented in `SPECS/ARCHIVE/P4-T1_Auto_restart_stale_broker_daemon_on_version_mismatch_after_upgrade/P4-T1_Validation_Report.md`:
- `pytest` → `766 passed, 5 skipped`
- `ruff check src/` → pass
- `mypy src/mcpbridge_wrapper` → pass
- `pytest --cov=src/mcpbridge_wrapper --cov-report=term-missing` → `90.71%`

### Next Steps
- No actionable review findings.
- FOLLOW-UP step is explicitly skipped.
4 changes: 2 additions & 2 deletions SPECS/INPROGRESS/next.md
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
# No Active Task

**Status:** Idle — P1-T6 archived. Select the next task from `SPECS/Workplan.md`.
**Status:** Idle — P4-T1 archived. Select the next task from `SPECS/Workplan.md`.

## Recently Archived

- **P4-T1** — Auto-restart stale broker daemon on version mismatch after upgrade (2026-03-05, PASS)
- **P1-T6** — Update webui-setup.md and DocC mirror to use --broker in multi-agent examples (2026-03-04, PASS)
- **P1-T5** — Fix missed --broker-spawn references in troubleshooting.md "MCP tools are green" section (2026-03-04, PASS)
- **P1-T9** — Add direct links for all command steps in FLOW.md (2026-03-03, PASS)
- **P3-T11** — Add Stop broker/service control button to Web UI (2026-03-01, PASS)
- **P1-T8** — Update /config examples for broker setup first (2026-03-01, PASS)

## Suggested Next Tasks

Expand Down
29 changes: 29 additions & 0 deletions SPECS/Workplan.md
Original file line number Diff line number Diff line change
Expand Up @@ -265,3 +265,32 @@ Add new tasks using the canonical template in [TASK_TEMPLATE.md](TASK_TEMPLATE.m
- [x] A proxy session forwarding `initialize` → `notifications/initialized` → `tools/list` returns 20 tools without the proxy exiting early
- [x] All existing broker tests pass (715 passed, 5 skipped)
- [x] MCP clients in broker mode (e.g. Zed with `--broker-spawn`) show the correct tool count

### Phase 4: Broker Lifecycle Management

#### ✅ P4-T1: Auto-restart stale broker daemon on version mismatch after upgrade
- **Status:** ✅ Completed (2026-03-05)
- **Description:** When users upgrade mcpbridge-wrapper, the old broker daemon keeps running with the old binary. New `--broker` clients silently connect to the stale daemon instead of using updated code. Fix by: (1) fixing version source of truth (`__init__.py` uses `importlib.metadata` from `pyproject.toml`), (2) daemon writes `broker.version` file on startup, (3) proxy checks version before connecting and auto-restarts mismatched daemons, (4) adding `--broker-stop` and `--broker-status` CLI commands, (5) install/uninstall scripts stop running daemons, (6) updating broker-mode docs.
- **Priority:** P0
- **Dependencies:** none
- **Parallelizable:** yes
- **Outputs/Artifacts:**
- `src/mcpbridge_wrapper/__init__.py` — `importlib.metadata`-based `__version__`
- `src/mcpbridge_wrapper/broker/types.py` — `version_file` property on `BrokerConfig`
- `src/mcpbridge_wrapper/broker/daemon.py` — version file write/cleanup/status
- `src/mcpbridge_wrapper/broker/proxy.py` — `_check_version_mismatch()`, `_stop_stale_daemon()`
- `src/mcpbridge_wrapper/__main__.py` — `--broker-stop`, `--broker-status` CLI commands
- `scripts/install.sh` — stop running broker on install
- `scripts/uninstall.sh` — stop running broker on uninstall
- `docs/broker-mode.md` — CLI commands and version management section
- **Acceptance Criteria:**
- [x] `__version__` derived from `importlib.metadata` (single source: `pyproject.toml`)
- [x] Daemon writes `~/.mcpbridge_wrapper/broker.version` on start and cleans on stop
- [x] Proxy auto-restarts daemon when version file mismatches current `__version__`
- [x] No version file (old daemon) is treated as compatible (backwards-compatible)
- [x] `--broker-status` prints daemon PID, version, mismatch warning
- [x] `--broker-stop` sends SIGTERM, waits, and cleans up state files
- [x] `scripts/install.sh` stops running broker daemon before writing new wrapper
- [x] `scripts/uninstall.sh` stops running broker daemon before removing files
- [x] `docs/broker-mode.md` documents `--broker-stop`, `--broker-status`, and version management
- [x] All quality gates pass (`pytest`, `ruff`, `mypy`, coverage >= 90%)
47 changes: 21 additions & 26 deletions docs/broker-mode.md
Original file line number Diff line number Diff line change
Expand Up @@ -71,22 +71,11 @@ uvx --from 'mcpbridge-wrapper[webui]' mcpbridge-wrapper \
### Status

```bash
PID_FILE="$HOME/.mcpbridge_wrapper/broker.pid"
SOCK="$HOME/.mcpbridge_wrapper/broker.sock"

if [ -f "$PID_FILE" ] && kill -0 "$(cat "$PID_FILE")" 2>/dev/null; then
echo "broker: running (pid $(cat "$PID_FILE"))"
else
echo "broker: stopped"
fi

if [ -S "$SOCK" ]; then
echo "socket: present ($SOCK)"
else
echo "socket: missing ($SOCK)"
fi
mcpbridge-wrapper --broker-status
```

Prints proxy version, daemon PID, daemon version, file paths, and warns on version mismatch.

### Logs

```bash
Expand All @@ -96,15 +85,24 @@ tail -f "$HOME/.mcpbridge_wrapper/broker.log"
### Stop

```bash
PID_FILE="$HOME/.mcpbridge_wrapper/broker.pid"
SOCK="$HOME/.mcpbridge_wrapper/broker.sock"

if [ -f "$PID_FILE" ]; then
kill "$(cat "$PID_FILE")" 2>/dev/null || true
fi
rm -f "$PID_FILE" "$SOCK"
mcpbridge-wrapper --broker-stop
```

Sends SIGTERM to the running daemon and waits up to 3 seconds for a clean exit. If the daemon exits, PID/socket/version files are removed. If it does not exit in time, the command returns an error and preserves state files for manual recovery.

## Version management

When upgrading `mcpbridge-wrapper` (via `pip install`, `uvx`, or `./scripts/install.sh`):

1. The **install script** automatically stops any running broker daemon.
2. On next `--broker` launch, the proxy compares its version against the daemon's
version file (`~/.mcpbridge_wrapper/broker.version`). If versions differ, the
stale daemon is stopped and a fresh one is spawned automatically.
3. Use `--broker-status` to verify the running daemon matches the installed version.

If an older daemon was started before the upgrade and you want to force an immediate
restart, run `mcpbridge-wrapper --broker-stop` followed by any `--broker` command.

## Client configuration examples

### Unified single-config examples (recommended)
Expand Down Expand Up @@ -190,13 +188,10 @@ If you prefer explicit host lifecycle management, start one `--broker-daemon --w

1. Remove `--broker` from MCP config args.
2. Restart the MCP client.
3. Stop any running broker process and remove stale files:
3. Stop any running broker process:

```bash
PID_FILE="$HOME/.mcpbridge_wrapper/broker.pid"
SOCK="$HOME/.mcpbridge_wrapper/broker.sock"
if [ -f "$PID_FILE" ]; then kill "$(cat "$PID_FILE")" 2>/dev/null || true; fi
rm -f "$PID_FILE" "$SOCK"
mcpbridge-wrapper --broker-stop
```

4. Verify direct mode behavior by running one tool call and confirming no broker files are recreated.
Expand Down
16 changes: 16 additions & 0 deletions scripts/install.sh
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,22 @@ else
python3 -m pip install -e . --quiet
fi

# Stop any running broker daemon so the new version takes over on next launch
BROKER_PID_FILE="$HOME/.mcpbridge_wrapper/broker.pid"
if [ -f "$BROKER_PID_FILE" ]; then
BROKER_PID=$(cat "$BROKER_PID_FILE" 2>/dev/null)
if [ -n "$BROKER_PID" ] && kill -0 "$BROKER_PID" 2>/dev/null; then
echo "Stopping running broker daemon (PID $BROKER_PID)..."
kill "$BROKER_PID" 2>/dev/null || true
sleep 1
if kill -0 "$BROKER_PID" 2>/dev/null; then
sleep 2
fi
fi
rm -f "$BROKER_PID_FILE" "$HOME/.mcpbridge_wrapper/broker.sock" "$HOME/.mcpbridge_wrapper/broker.version"
echo -e "${GREEN}✓ Old broker daemon stopped${NC}"
fi

# Create wrapper script in ~/bin that uses the venv Python
cat > "$INSTALL_DIR/xcodemcpwrapper" << WRAPPER
#!/bin/bash
Expand Down
16 changes: 16 additions & 0 deletions scripts/uninstall.sh
Original file line number Diff line number Diff line change
Expand Up @@ -159,6 +159,22 @@ fi
echo "Uninstalling..."
echo ""

# Stop any running broker daemon before removing files
BROKER_PID_FILE="$HOME/.mcpbridge_wrapper/broker.pid"
if [ -f "$BROKER_PID_FILE" ]; then
BROKER_PID=$(cat "$BROKER_PID_FILE" 2>/dev/null)
if [ -n "$BROKER_PID" ] && kill -0 "$BROKER_PID" 2>/dev/null; then
echo "Stopping running broker daemon (PID $BROKER_PID)..."
kill "$BROKER_PID" 2>/dev/null || true
sleep 1
if kill -0 "$BROKER_PID" 2>/dev/null; then
sleep 2
fi
fi
rm -f "$BROKER_PID_FILE" "$HOME/.mcpbridge_wrapper/broker.sock" "$HOME/.mcpbridge_wrapper/broker.version"
echo -e "${GREEN}✓ Broker daemon stopped${NC}"
fi

# Remove pip package(s)
if [ "$PIP_PACKAGE_EXISTS" = true ]; then
for pkg in "${DETECTED_PIP_PACKAGES[@]}"; do
Expand Down
Loading