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-07
**Last Updated:** 2026-03-10

## Archived Tasks

| Task ID | Folder | Archived | Verdict |
|---------|--------|----------|---------|
| P2-T8 | [P2-T8_Gate_broker_tools_list_on_warmed_tool_catalog/](P2-T8_Gate_broker_tools_list_on_warmed_tool_catalog/) | 2026-03-10 | PASS |
| P8-T1 | [P8-T1_Release_version_0.4.2_to_PyPI_and_MCP_Registry/](P8-T1_Release_version_0.4.2_to_PyPI_and_MCP_Registry/) | 2026-03-07 | PASS |
| P7-T5 | [P7-T5_Document_broker_UX/](P7-T5_Document_broker_UX/) | 2026-03-07 | PASS |
| P7-T4 | [P7-T4_Add_direct_local-status_fallback_for_TUI_when_dashboard_API_is_unavailable/](P7-T4_Add_direct_local-status_fallback_for_TUI_when_dashboard_API_is_unavailable/) | 2026-03-07 | PASS |
Expand Down Expand Up @@ -205,6 +206,7 @@

| File | Description |
|------|-------------|
| [REVIEW_p2_t8_tools_catalog_gate.md](_Historical/REVIEW_p2_t8_tools_catalog_gate.md) | Review report for P2-T8 |
| [REVIEW_broker_ux_docs.md](_Historical/REVIEW_broker_ux_docs.md) | Review report for P7-T5 |
| [REVIEW_tui_local_status_fallback.md](P7-T4_Add_direct_local-status_fallback_for_TUI_when_dashboard_API_is_unavailable/REVIEW_tui_local_status_fallback.md) | Review report for P7-T4 |
| [REVIEW_broker_owned_listener_guidance.md](_Historical/REVIEW_broker_owned_listener_guidance.md) | Review report for FU-P7-T3-2 |
Expand Down Expand Up @@ -636,3 +638,5 @@
| 2026-03-07 | P7-T2 | Archived REVIEW_broker_doctor_diagnostics report |
| 2026-03-07 | P7-T3 | Archived Auto-recover_or_guide_on_dashboard_port_ownership_conflicts (PASS) |
| 2026-03-07 | P7-T3 | Archived REVIEW_dashboard_port_ownership_conflicts report |
| 2026-03-10 | P2-T8 | Archived Gate_broker_tools_list_on_warmed_tool_catalog (PASS) |
| 2026-03-10 | P2-T8 | Archived REVIEW_p2_t8_tools_catalog_gate report |
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
# P2-T8: Gate broker tools/list on warmed tool catalog

**Status:** In Progress
**Priority:** P0
**Dependencies:** BUG-T9, P4-T2
**Branch:** `codex/p2-t8-broker-tools-catalog-gate`

---

## Problem Statement

Strict MCP clients such as Cursor and Zed cache the first successful `tools/list`
response they receive from a server. In broker mode, `mcpbridge-wrapper` currently
lets external client `tools/list` requests pass as soon as the upstream
`initialize` round-trip finishes.

That is too early.

The broker still has a second startup phase after `initialize`:

1. Send `notifications/initialized` to `xcrun mcpbridge`
2. Probe `tools/list` internally
3. Cache the resulting tool catalog for later clients

If an external client sends `tools/list` during that warm-up gap, it can observe an
empty or invalid tools list and cache that broken success locally. The user then
sees a green MCP indicator but fewer than the full 20 Xcode tools until they toggle
the server multiple times.

---

## Root Cause

In `UnixSocketServer._process_client_line`, the only readiness gate for all
request/response traffic is `daemon.upstream_initialized`. That event becomes set
immediately after the broker receives the upstream `initialize` response.

At that moment, however:

- the broker's internal `tools/list` probe may still be in flight
- `_tools_list_cache` may still be `None`
- the upstream may still return an empty or malformed tools payload during cold-start

Because `tools/list` follows the same gate as every other method, the first client
tool-discovery request can race ahead of the broker's own cache warm-up.

---

## Fix

Introduce a second broker readiness concept dedicated to tool discovery:

- add a `tools_catalog_ready` event on the daemon
- set it only after the broker receives a non-empty, structurally valid
`tools/list` probe result
- clear it on reconnect or when the internal probe returns an empty/invalid catalog
- make external client `tools/list` wait on `tools_catalog_ready` instead of only
`upstream_initialized`

This preserves existing behavior for non-`tools/list` methods while making the
first tool-discovery handshake safe for strict clients.

---

## Deliverables

| File | Change |
|------|--------|
| `src/mcpbridge_wrapper/broker/daemon.py` | Add explicit tools-catalog readiness event and reject empty/invalid warm-up results |
| `src/mcpbridge_wrapper/broker/transport.py` | Hold client `tools/list` until broker tool catalog is ready |
| `tests/unit/test_broker_daemon.py` | Cover ready/non-ready broker tool catalog transitions |
| `tests/unit/test_broker_transport.py` | Cover client `tools/list` wait/error/cache paths |
| `tests/integration/test_broker_multi_client.py` | Keep integration coverage aligned with the stronger broker contract |

---

## Acceptance Criteria

- [ ] Broker does not forward external `tools/list` while the internal tools cache is still cold
- [ ] Empty or invalid internal `tools/list` probe results do not open the client-facing readiness gate
- [ ] Client `tools/list` returns either a warmed catalog or a clear TTL error, never a premature empty success
- [ ] Existing non-`tools/list` broker traffic still flows after `upstream_initialized`
- [ ] `pytest` passes
- [ ] `ruff check src/` passes
- [ ] `mypy src/` passes
- [ ] `pytest --cov` remains at or above 90%


---
**Archived:** 2026-03-10
**Verdict:** PASS
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
# Validation Report: P2-T8 — Gate broker tools/list on warmed tool catalog

**Date:** 2026-03-10
**Verdict:** PASS

---

## Acceptance Criteria

| # | Criterion | Status |
|---|-----------|--------|
| 1 | Broker does not forward external `tools/list` while the internal tools cache is still cold | ✅ PASS |
| 2 | Empty or invalid internal `tools/list` probe results do not open the client-facing readiness gate | ✅ PASS |
| 3 | Client `tools/list` returns either a warmed catalog or a clear TTL error, never a premature empty success | ✅ PASS |
| 4 | Existing non-`tools/list` broker traffic still flows after `upstream_initialized` | ✅ PASS |
| 5 | `pytest` passes | ✅ PASS |
| 6 | `ruff check src/` passes | ✅ PASS |
| 7 | `mypy src/` passes | ✅ PASS |
| 8 | `pytest --cov` remains at or above 90% | ✅ PASS |

---

## Evidence

### Functional behavior

- Added a dedicated `tools_catalog_ready` event in the broker daemon so tool discovery
is gated separately from the upstream `initialize` round-trip.
- The broker now treats only non-empty, structurally valid internal `tools/list`
probe results as a ready catalog; empty or invalid results keep the gate closed,
clear the cache, and schedule another broker-internal warm-up probe instead of
requiring a reconnect or manual restart.
- External client `tools/list` now waits on the warmed catalog gate and returns a
deterministic `-32001` TTL error if the broker never reaches a safe ready state.
- Non-`tools/list` methods still wait only on `upstream_initialized`, preserving the
existing broker contract for normal request forwarding.

### Regression coverage

- `tests/unit/test_broker_daemon.py`
- verifies catalog readiness opens only for a valid non-empty probe result
- verifies empty probe results keep the catalog gate closed
- verifies an empty first probe retries until a valid tool catalog becomes available
- verifies reconnect clears both cache and readiness state
- `tests/unit/test_broker_transport.py`
- verifies `tools/list` times out with a catalog-specific readiness error
- verifies non-`tools/list` requests still wait on upstream initialization only
- verifies `tools/list` resumes from the warmed cache instead of racing upstream
- `tests/integration/test_broker_multi_client.py`
- keeps concurrent multi-client coverage aligned with the stronger broker warm-up
contract by exercising a normal forwarded tool call path instead of the special
cached `tools/list` path

### Validation environment hardening

- Added `pythonpath = ["src"]` to `pyproject.toml` so pytest in a clean worktree
imports the local checkout instead of an unrelated editable install from another
repository path. This makes FLOW validation deterministic and fixes `pytest --cov`
reporting in multi-worktree setups.

### Command results

- `pytest` → **901 passed, 5 skipped, 2 warnings**
- `ruff check src/` → **All checks passed**
- `mypy src/` → **Success: no issues found in 20 source files**
- `pytest --cov` → **901 passed, 5 skipped, 2 warnings; coverage 91.58%**

---

## Changed Files

- `pyproject.toml`
- `src/mcpbridge_wrapper/broker/daemon.py`
- `src/mcpbridge_wrapper/broker/transport.py`
- `tests/integration/test_broker_multi_client.py`
- `tests/unit/test_broker_daemon.py`
- `tests/unit/test_broker_transport.py`
41 changes: 41 additions & 0 deletions SPECS/ARCHIVE/_Historical/REVIEW_p2_t8_tools_catalog_gate.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
## REVIEW REPORT — P2-T8 Tools Catalog Gate

**Scope:** `origin/main..HEAD`
**Files:** 12

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

### Critical Issues

- None.

### Secondary Issues

- None.

### Architectural Notes

- Separating `tools_catalog_ready` from `upstream_initialized` closes the broker's
startup race without delaying unrelated non-`tools/list` traffic.
- Review of the first P2-T8 revision uncovered one high-risk hole: an empty first
broker `tools/list` probe could leave the catalog gate closed indefinitely until
reconnect or restart. The final branch resolves that by retrying the broker-internal
warm-up probe until a valid non-empty catalog arrives or the daemon transitions.
- Adding `pythonpath = ["src"]` to `pytest` config is a valid repo-level hardening
step because this project is actively developed from multiple local worktrees and
otherwise can import an unrelated editable install.

### Tests

- `pytest` — PASS (`901 passed, 5 skipped, 2 warnings`)
- `ruff check src/` — PASS
- `mypy src/` — PASS
- `pytest --cov` — PASS (`91.58%`)

### Next Steps

- No actionable review findings. FOLLOW-UP is skipped.
33 changes: 14 additions & 19 deletions SPECS/INPROGRESS/next.md
Original file line number Diff line number Diff line change
@@ -1,28 +1,23 @@
# Next Task: (none pending)
# Next Task: None

All current workplan tasks are complete.
## Selected Task

- No active task selected.

## Description

All tasks currently tracked in `SPECS/Workplan.md` are archived or completed. Select a new
task only after a follow-up or new workplan entry is created.

## Outputs

- None.

## Recently Archived

- `2026-03-10` — `P2-T8` archived with verdict `PASS`
- `2026-03-07` — `P8-T1` archived with verdict `PASS`
- `2026-03-07` — `P7-T5` archived with verdict `PASS`
- `2026-03-07` — `P7-T4` archived with verdict `PASS`
- `2026-03-07` — `FU-P7-T3-2` archived with verdict `PASS`
- `2026-03-07` — `FU-P7-T3-1` archived with verdict `PASS`

## Next Step

All tasks in the current workplan cycle have been completed. Add new tasks to
`SPECS/Workplan.md` to begin the next cycle.

## Post-Merge Action Required

After the P8-T1 PR merges to `main`, push the release tag to trigger publishing:

```bash
git checkout main && git pull origin main
git tag v0.4.2
git push origin v0.4.2
```

Then verify GitHub Actions `publish-mcp.yml` completes successfully.
17 changes: 17 additions & 0 deletions SPECS/Workplan.md
Original file line number Diff line number Diff line change
Expand Up @@ -274,6 +274,23 @@ Add new tasks using the canonical template in [TASK_TEMPLATE.md](TASK_TEMPLATE.m
- [x] Broker mode guidance remains clear with `--broker` (proxy) and `--broker-daemon` (host)
- [x] Required quality gates pass (`pytest`, `ruff check src/`, `mypy src/`, `pytest --cov` with coverage >=90%)

#### ✅ P2-T8: Gate broker tools/list on warmed tool catalog
- **Status:** ✅ Completed (2026-03-10)
- **Description:** Cursor and Zed can cache the first successful `tools/list` response they receive from `mcpbridge-wrapper`. Today the broker releases client `tools/list` requests immediately after upstream `initialize`, even if the broker has not yet completed its own `notifications/initialized` + `tools/list` warm-up and populated a stable tool cache. During cold-start or Xcode approval timing, that lets strict clients see an empty or invalid tool list and forces users to toggle the server several times before all 20 Xcode tools appear. Fix the broker so external `tools/list` waits for a warmed non-empty catalog instead of racing the warm-up path.
- **Priority:** P0
- **Dependencies:** BUG-T9, P4-T2
- **Parallelizable:** no
- **Outputs/Artifacts:**
- `src/mcpbridge_wrapper/broker/daemon.py` — explicit tool-catalog readiness gate and empty-catalog handling
- `src/mcpbridge_wrapper/broker/transport.py` — hold client `tools/list` until broker cache is ready
- `tests/unit/test_broker_daemon.py`, `tests/unit/test_broker_transport.py` — broker warm-up regression coverage
- `tests/integration/test_broker_multi_client.py` — integration coverage updated for the new broker contract
- **Acceptance Criteria:**
- [x] Broker does not forward client `tools/list` while its internal tool catalog is still cold
- [x] Empty or invalid broker `tools/list` probe results do not open the client-facing readiness gate
- [x] Cursor/Zed-style first `tools/list` requests receive either a warmed catalog or a clear TTL error, never a prematurely cached empty success
- [x] Required quality gates pass (`pytest`, `ruff check src/`, `mypy src/`, `pytest --cov` with coverage >=90%)

### Phase 3: Web UI Controls

#### ✅ P3-T11: Add Stop broker/service control button to Web UI
Expand Down
1 change: 1 addition & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@ include-package-data = true

[tool.pytest.ini_options]
testpaths = ["tests"]
pythonpath = ["src"]
python_files = ["test_*.py", "*_test.py"]
python_classes = ["Test*"]
python_functions = ["test_*"]
Expand Down
Loading