Skip to content

Commit f81f95a

Browse files
authored
Merge pull request #137 from SoundBlaster/feature/P4-T1-auto-restart-stale-broker
P4-T1: Auto-restart stale broker daemon on version mismatch after upgrade
2 parents 2cdc606 + 319fe62 commit f81f95a

19 files changed

Lines changed: 1067 additions & 57 deletions

File tree

SPECS/ARCHIVE/INDEX.md

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,12 @@
11
# mcpbridge-wrapper Tasks Archive
22

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

55
## Archived Tasks
66

77
| Task ID | Folder | Archived | Verdict |
88
|---------|--------|----------|---------|
9+
| 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 |
910
| 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 |
1011
| P1-T5 | [P1-T5_Fix_missed_broker_spawn_references_in_troubleshooting/](P1-T5_Fix_missed_broker_spawn_references_in_troubleshooting/) | 2026-03-04 | PASS |
1112
| 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 |
@@ -185,6 +186,7 @@
185186

186187
| File | Description |
187188
|------|-------------|
189+
| [REVIEW_p4_t1_broker_version_restart.md](_Historical/REVIEW_p4_t1_broker_version_restart.md) | Review report for P4-T1 |
188190
| [REVIEW_p1_t6_webui_broker_examples.md](_Historical/REVIEW_p1_t6_webui_broker_examples.md) | Review report for P1-T6 |
189191
| [REVIEW_p1_t5_troubleshooting_broker.md](_Historical/REVIEW_p1_t5_troubleshooting_broker.md) | Review report for P1-T5 |
190192
| [REVIEW_p1_t9_flow_command_links.md](_Historical/REVIEW_p1_t9_flow_command_links.md) | Review report for P1-T9 |
@@ -315,6 +317,8 @@
315317

316318
| Date | Task ID | Action |
317319
|------|---------|--------|
320+
| 2026-03-05 | P4-T1 | Archived REVIEW_p4_t1_broker_version_restart report |
321+
| 2026-03-05 | P4-T1 | Archived Auto_restart_stale_broker_daemon_on_version_mismatch_after_upgrade (PASS) |
318322
| 2026-03-04 | P1-T6 | Archived REVIEW_p1_t6_webui_broker_examples report |
319323
| 2026-03-04 | P1-T6 | Archived Update webui-setup.md and DocC mirror to use --broker in multi-agent examples (PASS) |
320324
| 2026-03-04 | P1-T5 | Archived REVIEW_p1_t5_troubleshooting_broker report |
Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
# P4-T1 PRD — Auto-restart stale broker daemon on version mismatch after upgrade
2+
3+
## Task Metadata
4+
5+
- **Task ID:** P4-T1
6+
- **Phase:** Phase 4: Broker Lifecycle Management
7+
- **Priority:** P0
8+
- **Dependencies:** none
9+
- **Source:** `SPECS/Workplan.md` open task entry
10+
11+
## Objective Summary
12+
13+
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.
14+
15+
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.
16+
17+
## Success Criteria
18+
19+
1. `__version__` is derived from package metadata (`importlib.metadata`) instead of hard-coded constants.
20+
2. Broker daemon writes `broker.version` on startup and removes it during shutdown/cleanup paths.
21+
3. Proxy detects mismatched daemon version and auto-restarts stale daemon before connecting.
22+
4. Missing `broker.version` file is treated as compatible (no forced restart).
23+
5. `--broker-status` reports PID/status/version info and mismatch warning.
24+
6. `--broker-stop` gracefully stops daemon, waits for exit, and removes pid/socket/version state files.
25+
7. Install and uninstall scripts stop any running broker daemon before replacing/removing wrapper files.
26+
8. Broker-mode docs describe new lifecycle commands and version management flow.
27+
9. Required quality gates pass: `pytest`, `ruff check src/`, `mypy src/mcpbridge_wrapper`, `pytest --cov=src/mcpbridge_wrapper --cov-report=term-missing` (>= 90%).
28+
29+
## Acceptance Tests
30+
31+
- Unit tests for `BrokerConfig.version_file` path derivation.
32+
- Unit tests for daemon version-file write/remove and status payload version field.
33+
- Unit tests for proxy version mismatch detection and restart behavior.
34+
- Unit tests for `_parse_broker_args` handling of `--broker-status` and `--broker-stop`.
35+
- CLI behavior validation:
36+
- `python -m mcpbridge_wrapper --broker-status`
37+
- `python -m mcpbridge_wrapper --broker-stop`
38+
- Script-level behavior validation for install/uninstall broker-stop logic (covered by shell-path checks and existing script tests if present).
39+
40+
## Test-First Plan
41+
42+
1. Add/extend tests for version file state in broker config and daemon lifecycle.
43+
2. Add/extend proxy tests for mismatch detection, backward-compatible no-version behavior, and stale daemon restart.
44+
3. Add/extend main/CLI tests for new broker control flags and argument parsing.
45+
4. Implement runtime logic minimally to satisfy new failing tests.
46+
5. Update docs and scripts after runtime tests pass.
47+
6. Run full quality gates and capture outputs in validation report.
48+
49+
## Implementation Plan (Hierarchical TODO)
50+
51+
### Phase A — Version Source and Broker State
52+
53+
- **Inputs:** Existing `__version__` declaration, `BrokerConfig`, daemon lifecycle logic.
54+
- **Outputs:** Metadata-derived version string; `version_file` property; daemon writes/cleans version file; status includes version.
55+
- **Verification:** New daemon/config unit tests pass and stale-lock cleanup removes version artifacts.
56+
57+
### Phase B — Proxy and CLI Lifecycle Controls
58+
59+
- **Inputs:** Proxy spawn/reuse flow, broker arg parsing and command dispatch in `__main__.py`.
60+
- **Outputs:** Version mismatch detection + stale daemon restart; `--broker-status`; `--broker-stop` cleanup behavior.
61+
- **Verification:** New proxy/main tests pass and manual status/stop commands return expected output/exit semantics.
62+
63+
### Phase C — Operational Scripts, Docs, and Validation
64+
65+
- **Inputs:** `scripts/install.sh`, `scripts/uninstall.sh`, `docs/broker-mode.md`, quality gate tooling.
66+
- **Outputs:** Install/uninstall stop running daemon before file replacement/removal; docs updated for status/stop/version behavior; validation report.
67+
- **Verification:** Required quality gates pass; acceptance criteria checklist fully satisfied.
68+
69+
## Decision Points and Constraints
70+
71+
- Use defensive behavior when metadata/version files are unavailable: prefer safe fallback over hard failure.
72+
- Keep broker-stop idempotent (safe when daemon already dead or files are stale/corrupt).
73+
- Avoid introducing behavior that depends on non-portable process APIs beyond existing macOS/Linux constraints.
74+
- Keep implementation scoped to P4-T1 artifacts; do not refactor unrelated broker subsystems.
75+
76+
## Notes (Post-Completion Docs/Artifacts)
77+
78+
- Create `SPECS/INPROGRESS/P4-T1_Validation_Report.md` with command evidence and verdict.
79+
- Archive PRD and validation report to `SPECS/ARCHIVE/P4-T1_Auto_restart_stale_broker_daemon_on_version_mismatch_after_upgrade/`.
80+
- Mark `P4-T1` complete in `SPECS/Workplan.md` and update `SPECS/ARCHIVE/INDEX.md`.
81+
- Create and archive `REVIEW_p4_t1_broker_version_restart.md` after review.
82+
83+
---
84+
**Archived:** 2026-03-05
85+
**Verdict:** PASS
Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
# Validation Report — P4-T1
2+
3+
**Task:** P4-T1 — Auto-restart stale broker daemon on version mismatch after upgrade
4+
**Date:** 2026-03-05
5+
**Executor:** Codex (`flow-run`)
6+
**Verdict:** PASS
7+
8+
## Summary
9+
10+
Implemented and validated version-aware broker lifecycle handling across runtime code, CLI, scripts, documentation, and tests.
11+
12+
Key outcomes:
13+
- `__version__` now resolves from package metadata with safe fallback.
14+
- Broker daemon writes/cleans `broker.version` and reports version in status payload.
15+
- Proxy detects daemon version mismatch and restarts stale daemon before reuse.
16+
- Added `--broker-status` and `--broker-stop` commands.
17+
- Install/uninstall scripts now stop running broker daemons before file operations.
18+
- Broker mode docs updated for new status/stop/version management workflow.
19+
- Coverage restored above threshold after adding targeted tests for new paths.
20+
21+
## Acceptance Criteria Check
22+
23+
- [x] `__version__` derived from `importlib.metadata` (single source: `pyproject.toml`)
24+
- [x] Daemon writes `~/.mcpbridge_wrapper/broker.version` on start and cleans on stop
25+
- [x] Proxy auto-restarts daemon when version file mismatches current `__version__`
26+
- [x] No version file (old daemon) treated as compatible
27+
- [x] `--broker-status` prints daemon PID, version, mismatch warning
28+
- [x] `--broker-stop` sends SIGTERM, waits, and cleans up state files
29+
- [x] `scripts/install.sh` stops running broker daemon before writing new wrapper
30+
- [x] `scripts/uninstall.sh` stops running broker daemon before removing files
31+
- [x] `docs/broker-mode.md` documents `--broker-stop`, `--broker-status`, and version management
32+
- [x] All quality gates pass (`pytest`, `ruff`, `mypy`, coverage >= 90%)
33+
34+
## Quality Gate Evidence
35+
36+
```bash
37+
pytest
38+
```
39+
40+
Result: `766 passed, 5 skipped, 2 warnings`.
41+
42+
```bash
43+
ruff check src/
44+
```
45+
46+
Result: `All checks passed!`.
47+
48+
```bash
49+
mypy src/mcpbridge_wrapper
50+
```
51+
52+
Result: `Success: no issues found in 18 source files`.
53+
54+
```bash
55+
pytest --cov=src/mcpbridge_wrapper --cov-report=term-missing
56+
```
57+
58+
Result: `766 passed, 5 skipped, 2 warnings`; total coverage `90.71%` (threshold `90%`).
59+
60+
## Additional Test Coverage Added
61+
62+
- `tests/unit/test_main.py`:
63+
- New coverage for `--broker-status` and `--broker-stop` runtime branches.
64+
- `tests/unit/test_broker_proxy.py`:
65+
- New coverage for version-file read errors and `_stop_stale_daemon` cleanup paths.
66+
- `tests/unit/test_init.py`:
67+
- Metadata-derived version and fallback behavior.
68+
69+
## Notes
70+
71+
- Existing deprecation warnings from websocket dependencies remain unchanged and are non-blocking for this task.
72+
- Ready for ARCHIVE step.
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
## REVIEW REPORT — P4-T1 Broker version restart
2+
3+
**Scope:** origin/main..HEAD
4+
**Files:** 18
5+
6+
### Summary Verdict
7+
- [x] Approve
8+
- [ ] Approve with comments
9+
- [ ] Request changes
10+
- [ ] Block
11+
12+
### Critical Issues
13+
- None.
14+
15+
### Secondary Issues
16+
- None.
17+
18+
### Architectural Notes
19+
- Version source-of-truth now resolves from package metadata and is consistently reused across daemon/proxy status paths.
20+
- Version mismatch handling is defensive and backward-compatible: no `broker.version` file does not force restart.
21+
- Lifecycle command additions (`--broker-status`, `--broker-stop`) improve operability without changing normal stdio bridge behavior.
22+
23+
### Tests
24+
- Quality gates are documented in `SPECS/ARCHIVE/P4-T1_Auto_restart_stale_broker_daemon_on_version_mismatch_after_upgrade/P4-T1_Validation_Report.md`:
25+
- `pytest``766 passed, 5 skipped`
26+
- `ruff check src/` → pass
27+
- `mypy src/mcpbridge_wrapper` → pass
28+
- `pytest --cov=src/mcpbridge_wrapper --cov-report=term-missing``90.71%`
29+
30+
### Next Steps
31+
- No actionable review findings.
32+
- FOLLOW-UP step is explicitly skipped.

SPECS/INPROGRESS/next.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,14 @@
11
# No Active Task
22

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

55
## Recently Archived
66

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

1313
## Suggested Next Tasks
1414

SPECS/Workplan.md

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -265,3 +265,32 @@ Add new tasks using the canonical template in [TASK_TEMPLATE.md](TASK_TEMPLATE.m
265265
- [x] A proxy session forwarding `initialize``notifications/initialized``tools/list` returns 20 tools without the proxy exiting early
266266
- [x] All existing broker tests pass (715 passed, 5 skipped)
267267
- [x] MCP clients in broker mode (e.g. Zed with `--broker-spawn`) show the correct tool count
268+
269+
### Phase 4: Broker Lifecycle Management
270+
271+
#### ✅ P4-T1: Auto-restart stale broker daemon on version mismatch after upgrade
272+
- **Status:** ✅ Completed (2026-03-05)
273+
- **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.
274+
- **Priority:** P0
275+
- **Dependencies:** none
276+
- **Parallelizable:** yes
277+
- **Outputs/Artifacts:**
278+
- `src/mcpbridge_wrapper/__init__.py``importlib.metadata`-based `__version__`
279+
- `src/mcpbridge_wrapper/broker/types.py``version_file` property on `BrokerConfig`
280+
- `src/mcpbridge_wrapper/broker/daemon.py` — version file write/cleanup/status
281+
- `src/mcpbridge_wrapper/broker/proxy.py``_check_version_mismatch()`, `_stop_stale_daemon()`
282+
- `src/mcpbridge_wrapper/__main__.py``--broker-stop`, `--broker-status` CLI commands
283+
- `scripts/install.sh` — stop running broker on install
284+
- `scripts/uninstall.sh` — stop running broker on uninstall
285+
- `docs/broker-mode.md` — CLI commands and version management section
286+
- **Acceptance Criteria:**
287+
- [x] `__version__` derived from `importlib.metadata` (single source: `pyproject.toml`)
288+
- [x] Daemon writes `~/.mcpbridge_wrapper/broker.version` on start and cleans on stop
289+
- [x] Proxy auto-restarts daemon when version file mismatches current `__version__`
290+
- [x] No version file (old daemon) is treated as compatible (backwards-compatible)
291+
- [x] `--broker-status` prints daemon PID, version, mismatch warning
292+
- [x] `--broker-stop` sends SIGTERM, waits, and cleans up state files
293+
- [x] `scripts/install.sh` stops running broker daemon before writing new wrapper
294+
- [x] `scripts/uninstall.sh` stops running broker daemon before removing files
295+
- [x] `docs/broker-mode.md` documents `--broker-stop`, `--broker-status`, and version management
296+
- [x] All quality gates pass (`pytest`, `ruff`, `mypy`, coverage >= 90%)

docs/broker-mode.md

Lines changed: 21 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -71,22 +71,11 @@ uvx --from 'mcpbridge-wrapper[webui]' mcpbridge-wrapper \
7171
### Status
7272

7373
```bash
74-
PID_FILE="$HOME/.mcpbridge_wrapper/broker.pid"
75-
SOCK="$HOME/.mcpbridge_wrapper/broker.sock"
76-
77-
if [ -f "$PID_FILE" ] && kill -0 "$(cat "$PID_FILE")" 2>/dev/null; then
78-
echo "broker: running (pid $(cat "$PID_FILE"))"
79-
else
80-
echo "broker: stopped"
81-
fi
82-
83-
if [ -S "$SOCK" ]; then
84-
echo "socket: present ($SOCK)"
85-
else
86-
echo "socket: missing ($SOCK)"
87-
fi
74+
mcpbridge-wrapper --broker-status
8875
```
8976

77+
Prints proxy version, daemon PID, daemon version, file paths, and warns on version mismatch.
78+
9079
### Logs
9180

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

9887
```bash
99-
PID_FILE="$HOME/.mcpbridge_wrapper/broker.pid"
100-
SOCK="$HOME/.mcpbridge_wrapper/broker.sock"
101-
102-
if [ -f "$PID_FILE" ]; then
103-
kill "$(cat "$PID_FILE")" 2>/dev/null || true
104-
fi
105-
rm -f "$PID_FILE" "$SOCK"
88+
mcpbridge-wrapper --broker-stop
10689
```
10790

91+
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.
92+
93+
## Version management
94+
95+
When upgrading `mcpbridge-wrapper` (via `pip install`, `uvx`, or `./scripts/install.sh`):
96+
97+
1. The **install script** automatically stops any running broker daemon.
98+
2. On next `--broker` launch, the proxy compares its version against the daemon's
99+
version file (`~/.mcpbridge_wrapper/broker.version`). If versions differ, the
100+
stale daemon is stopped and a fresh one is spawned automatically.
101+
3. Use `--broker-status` to verify the running daemon matches the installed version.
102+
103+
If an older daemon was started before the upgrade and you want to force an immediate
104+
restart, run `mcpbridge-wrapper --broker-stop` followed by any `--broker` command.
105+
108106
## Client configuration examples
109107

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

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

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

202197
4. Verify direct mode behavior by running one tool call and confirming no broker files are recreated.

scripts/install.sh

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,22 @@ else
9999
python3 -m pip install -e . --quiet
100100
fi
101101

102+
# Stop any running broker daemon so the new version takes over on next launch
103+
BROKER_PID_FILE="$HOME/.mcpbridge_wrapper/broker.pid"
104+
if [ -f "$BROKER_PID_FILE" ]; then
105+
BROKER_PID=$(cat "$BROKER_PID_FILE" 2>/dev/null)
106+
if [ -n "$BROKER_PID" ] && kill -0 "$BROKER_PID" 2>/dev/null; then
107+
echo "Stopping running broker daemon (PID $BROKER_PID)..."
108+
kill "$BROKER_PID" 2>/dev/null || true
109+
sleep 1
110+
if kill -0 "$BROKER_PID" 2>/dev/null; then
111+
sleep 2
112+
fi
113+
fi
114+
rm -f "$BROKER_PID_FILE" "$HOME/.mcpbridge_wrapper/broker.sock" "$HOME/.mcpbridge_wrapper/broker.version"
115+
echo -e "${GREEN}✓ Old broker daemon stopped${NC}"
116+
fi
117+
102118
# Create wrapper script in ~/bin that uses the venv Python
103119
cat > "$INSTALL_DIR/xcodemcpwrapper" << WRAPPER
104120
#!/bin/bash

scripts/uninstall.sh

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -159,6 +159,22 @@ fi
159159
echo "Uninstalling..."
160160
echo ""
161161

162+
# Stop any running broker daemon before removing files
163+
BROKER_PID_FILE="$HOME/.mcpbridge_wrapper/broker.pid"
164+
if [ -f "$BROKER_PID_FILE" ]; then
165+
BROKER_PID=$(cat "$BROKER_PID_FILE" 2>/dev/null)
166+
if [ -n "$BROKER_PID" ] && kill -0 "$BROKER_PID" 2>/dev/null; then
167+
echo "Stopping running broker daemon (PID $BROKER_PID)..."
168+
kill "$BROKER_PID" 2>/dev/null || true
169+
sleep 1
170+
if kill -0 "$BROKER_PID" 2>/dev/null; then
171+
sleep 2
172+
fi
173+
fi
174+
rm -f "$BROKER_PID_FILE" "$HOME/.mcpbridge_wrapper/broker.sock" "$HOME/.mcpbridge_wrapper/broker.version"
175+
echo -e "${GREEN}✓ Broker daemon stopped${NC}"
176+
fi
177+
162178
# Remove pip package(s)
163179
if [ "$PIP_PACKAGE_EXISTS" = true ]; then
164180
for pkg in "${DETECTED_PIP_PACKAGES[@]}"; do

0 commit comments

Comments
 (0)