From 544d2d29a45a309204b1b913d021e24278d92089 Mon Sep 17 00:00:00 2001 From: Egor Merkushev Date: Wed, 18 Feb 2026 23:04:27 +0300 Subject: [PATCH 01/12] Branch for FU-BUG-T7-1: cap pending methods map From 94a78e638d77855fe507a634be69b5951c98f495 Mon Sep 17 00:00:00 2001 From: Egor Merkushev Date: Wed, 18 Feb 2026 23:05:01 +0300 Subject: [PATCH 02/12] Select task FU-BUG-T7-1: Cap pending_methods map to guard against unbounded growth --- SPECS/INPROGRESS/next.md | 25 +++++++++++++------------ 1 file changed, 13 insertions(+), 12 deletions(-) diff --git a/SPECS/INPROGRESS/next.md b/SPECS/INPROGRESS/next.md index 74ac283a..6b6e9bf6 100644 --- a/SPECS/INPROGRESS/next.md +++ b/SPECS/INPROGRESS/next.md @@ -1,16 +1,17 @@ -# No Active Task +# Next Task: FU-BUG-T7-1 — Cap `pending_methods` map to guard against unbounded growth -## Recently Archived +**Priority:** P3 +**Phase:** Phase 13: Persistent Broker & Shared Xcode Session +**Effort:** 1-2 hours +**Dependencies:** BUG-T7 +**Status:** Selected -- 2026-02-18 — FU-P13-T2-2: Move PID file write to after successful upstream launch (PASS) -- 2026-02-18 — FU-P13-T2-1: Replace run_forever() polling loop with asyncio.Event-based wait (PASS) -- 2026-02-18 — FU-P13-T4-2: Implement or remove reconnect parameter in BrokerProxy (PASS) -- 2026-02-18 — FU-P13-T4-1: Fix asyncio.get_event_loop() deprecation in BrokerProxy (PASS) -- 2026-02-18 — P13-T6: Document broker mode configuration, migration, and rollback (PASS) -- 2026-02-18 — P13-T5: Validate prompt reduction and multi-client stability (PARTIAL) +## Description -## Suggested Next Tasks +Harden `pending_methods` tracking in `src/mcpbridge_wrapper/__main__.py` so it cannot +grow unbounded under abnormal traffic patterns (e.g. bridge crash or one-way +messages), while preserving BUG-T7 method-correlation behavior. -- P13-T5 follow-up — Complete interactive prompt verification in a desktop session (P1) -- FU-BUG-T7-1 — Cap `pending_methods` map to guard against unbounded growth (P3) -- FU-P12-T1-1 — Remove or document `MCPInitializeParams` in schemas (P3) +## Next Step + +Run the PLAN command to generate the implementation-ready PRD. From 1da016490186c7e1f551ca0f4360882f9f98cc3f Mon Sep 17 00:00:00 2001 From: Egor Merkushev Date: Wed, 18 Feb 2026 23:05:41 +0300 Subject: [PATCH 03/12] Plan task FU-BUG-T7-1: Cap pending_methods map to guard against unbounded growth --- ...g_methods_map_to_guard_unbounded_growth.md | 47 +++++++++++++++++++ 1 file changed, 47 insertions(+) create mode 100644 SPECS/INPROGRESS/FU-BUG-T7-1_Cap_pending_methods_map_to_guard_unbounded_growth.md diff --git a/SPECS/INPROGRESS/FU-BUG-T7-1_Cap_pending_methods_map_to_guard_unbounded_growth.md b/SPECS/INPROGRESS/FU-BUG-T7-1_Cap_pending_methods_map_to_guard_unbounded_growth.md new file mode 100644 index 00000000..3a3097cf --- /dev/null +++ b/SPECS/INPROGRESS/FU-BUG-T7-1_Cap_pending_methods_map_to_guard_unbounded_growth.md @@ -0,0 +1,47 @@ +# PRD: FU-BUG-T7-1 — Cap `pending_methods` map to guard against unbounded growth + +**Task ID:** FU-BUG-T7-1 +**Priority:** P3 +**Phase:** Phase 13: Persistent Broker & Shared Xcode Session +**Dependencies:** BUG-T7 +**Status:** Planned + +## Objective + +Prevent unbounded growth of `pending_methods` in `src/mcpbridge_wrapper/__main__.py` +while preserving BUG-T7 method-aware normalization behavior for in-flight JSON-RPC +responses. + +## Deliverables + +- Bounded `pending_methods` handling in `src/mcpbridge_wrapper/__main__.py`. +- Unit tests in `tests/unit/test_main.py` that verify capped growth behavior. +- Validation report at `SPECS/INPROGRESS/FU-BUG-T7-1_Validation_Report.md`. + +## Acceptance Criteria + +- `pending_methods` never exceeds the configured cap under sustained insert-only traffic. +- Existing request/response method correlation behavior remains unchanged. +- Regression tests for BUG-T7 behavior continue to pass. +- Full quality gates pass (`pytest`, `ruff check src/`, `mypy src/`, `pytest --cov` >= 90%). + +## Implementation Plan + +1. Add a module-level cap constant (default `1000`) for `pending_methods`. +2. Replace raw dict updates with helper logic that evicts the oldest pending entry + before inserting when at cap. +3. Keep response handling (`pop(request_id)`) unchanged so successful responses + still clear pending entries. +4. Add a focused test that simulates high-volume unmatched requests and asserts the + map size never exceeds cap. +5. Run required quality gates and write the validation report. + +## Risks and Mitigations + +- **Risk:** Evicting oldest entries can lose method context for delayed responses. + **Mitigation:** Eviction only occurs once cap is reached; capped memory growth is + the explicit priority for abnormal traffic conditions. + +- **Risk:** Test could become brittle if it depends on implementation details. + **Mitigation:** Test via observable behavior (map size and call count), not private + container type internals. From cfc4aac1f84d214c932020755951007e3c992795 Mon Sep 17 00:00:00 2001 From: Egor Merkushev Date: Wed, 18 Feb 2026 23:07:39 +0300 Subject: [PATCH 04/12] Implement FU-BUG-T7-1: cap pending_methods map with bounded eviction --- .../FU-BUG-T7-1_Validation_Report.md | 38 +++++++++ src/mcpbridge_wrapper/__main__.py | 34 +++++++- tests/unit/test_main.py | 82 +++++++++++++++++++ 3 files changed, 153 insertions(+), 1 deletion(-) create mode 100644 SPECS/INPROGRESS/FU-BUG-T7-1_Validation_Report.md diff --git a/SPECS/INPROGRESS/FU-BUG-T7-1_Validation_Report.md b/SPECS/INPROGRESS/FU-BUG-T7-1_Validation_Report.md new file mode 100644 index 00000000..47e1e08f --- /dev/null +++ b/SPECS/INPROGRESS/FU-BUG-T7-1_Validation_Report.md @@ -0,0 +1,38 @@ +# Validation Report — FU-BUG-T7-1 + +**Task:** FU-BUG-T7-1 — Cap `pending_methods` map to guard against unbounded growth +**Date:** 2026-02-18 +**Verdict:** PASS + +## Scope + +- Added bounded pending-method tracking in `src/mcpbridge_wrapper/__main__.py`. +- Added regression coverage in `tests/unit/test_main.py` for high-volume + request tracking with bounded eviction behavior. + +## Test Evidence + +- Focused regression tests: + - `pytest tests/unit/test_main.py -k "pending_method" -q` + - Result: **PASS** (`2 passed`) + +## Required Quality Gates + +- `pytest -q` + Result: **PASS** (`582 passed, 5 skipped`) +- `ruff check src/` + Result: **PASS** (`All checks passed!`) +- `mypy src/` + Result: **PASS** (`Success: no issues found in 18 source files`) +- `pytest --cov=src/mcpbridge_wrapper --cov-report=term-missing` + Result: **PASS** (`582 passed, 5 skipped`; total coverage **92.19%**) + +## Acceptance Criteria Status + +- [x] `pending_methods` does not grow beyond a capped size under abnormal traffic. +- [x] Existing BUG-T7 normalization behavior remains intact. + +## Notes + +- Existing third-party deprecation warnings (`websockets`/`uvicorn`) remain + unchanged and are unrelated to this task. diff --git a/src/mcpbridge_wrapper/__main__.py b/src/mcpbridge_wrapper/__main__.py index e75bd173..dbe3f500 100644 --- a/src/mcpbridge_wrapper/__main__.py +++ b/src/mcpbridge_wrapper/__main__.py @@ -18,6 +18,9 @@ _seen_tools_request = False _tools_response_timeout = False +# Guard rail for method-correlation tracking (FU-BUG-T7-1). +MAX_PENDING_METHODS = 1000 + def check_xcode_tools_enabled() -> None: """Print diagnostic message if Xcode Tools MCP is likely not enabled.""" @@ -217,6 +220,30 @@ def _parse_broker_args( return broker_connect, broker_spawn, remaining +def _track_pending_method( + pending_methods: Dict[str, str], + request_id: str, + method: str, + max_size: int, +) -> None: + """Track request method with bounded map size. + + Uses insertion order for eviction: when at capacity, drop the oldest pending + request before adding a new one. Re-seen request IDs are refreshed to the + newest position. + """ + if max_size <= 0: + return + + if request_id in pending_methods: + del pending_methods[request_id] + elif len(pending_methods) >= max_size and pending_methods: + oldest_request_id = next(iter(pending_methods)) + del pending_methods[oldest_request_id] + + pending_methods[request_id] = method + + def main() -> int: """Main entry point for the mcpbridge-wrapper command. @@ -372,7 +399,12 @@ def on_request(line: str) -> None: # Track method for ALL requests with an id (enables error normalization) if request_id is not None and method is not None: - pending_methods[request_id] = method + _track_pending_method( + pending_methods, + request_id=request_id, + method=method, + max_size=MAX_PENDING_METHODS, + ) # Extract MCP client identity from initialize handshake if method == "initialize" and metrics is not None: diff --git a/tests/unit/test_main.py b/tests/unit/test_main.py index 122e0e4d..a4421452 100644 --- a/tests/unit/test_main.py +++ b/tests/unit/test_main.py @@ -662,6 +662,88 @@ def capture_on_request(bridge, on_request=None): assert ("unknown", "unknown") in captured_calls +class TestPendingMethodTracking: + """Tests for bounded pending method tracking.""" + + def test_track_pending_method_caps_growth(self): + """Map size is capped and oldest request IDs are evicted first.""" + from mcpbridge_wrapper.__main__ import _track_pending_method + + pending_methods = {} + for i in range(8): + _track_pending_method( + pending_methods, + request_id=f"req-{i}", + method="resources/list", + max_size=3, + ) + + assert len(pending_methods) == 3 + assert list(pending_methods.keys()) == ["req-5", "req-6", "req-7"] + + @patch("mcpbridge_wrapper.__main__.process_response_line", side_effect=lambda s, method=None: s) + @patch("mcpbridge_wrapper.__main__.run_stdin_forwarder") + @patch("mcpbridge_wrapper.__main__.run_stdout_reader") + @patch("mcpbridge_wrapper.__main__.create_bridge") + @patch("mcpbridge_wrapper.__main__.cleanup_bridge") + @patch("mcpbridge_wrapper.__main__._extract_request_id", side_effect=["req-1", "req-4"]) + def test_main_evicted_pending_method_falls_back_to_none( + self, + mock_extract_request_id, + mock_cleanup, + mock_create, + mock_stdout_reader, + mock_stdin_forwarder, + mock_process_response_line, + ): + """Oldest pending request loses method context when cap is exceeded.""" + mock_bridge = MagicMock(spec=Popen) + mock_bridge.poll.return_value = None + mock_create.return_value = mock_bridge + mock_cleanup.return_value = 0 + + captured_on_request = {} + + def _capture_forwarder(_bridge, on_request=None): + captured_on_request["cb"] = on_request + return MagicMock() + + mock_stdin_forwarder.side_effect = _capture_forwarder + + class _TriggeringQueue: + def __init__(self, on_first_get): + self._on_first_get = on_first_get + self._count = 0 + + def get(self): + self._count += 1 + if self._count == 1: + self._on_first_get() + return '{"jsonrpc":"2.0","id":"req-1","result":{"content":[]}}\n' + if self._count == 2: + return '{"jsonrpc":"2.0","id":"req-4","result":{"content":[]}}\n' + return None + + with patch("mcpbridge_wrapper.__main__.MAX_PENDING_METHODS", 3): + + def _prime_pending_methods(): + assert "cb" in captured_on_request + for i in range(1, 5): + captured_on_request["cb"]( + f'{{"jsonrpc":"2.0","id":"req-{i}","method":"resources/list"}}' + ) + + mock_stdout_reader.return_value = (MagicMock(), _TriggeringQueue(_prime_pending_methods)) + + with patch("mcpbridge_wrapper.__main__.sys.argv", ["mcpbridge-wrapper"]): + result = main() + + assert result == 0 + # req-1 is evicted; req-4 is retained. + assert mock_process_response_line.call_args_list[0].kwargs["method"] is None + assert mock_process_response_line.call_args_list[1].kwargs["method"] == "resources/list" + + class TestParseErrorInfo: """Tests for _parse_error_info helper.""" From 1d83e7341e050f8d4b9d6a1da5e4c8aa5f9dccb2 Mon Sep 17 00:00:00 2001 From: Egor Merkushev Date: Wed, 18 Feb 2026 23:09:02 +0300 Subject: [PATCH 05/12] Archive task FU-BUG-T7-1: Cap pending_methods map to guard against unbounded growth (PASS) --- ...g_methods_map_to_guard_unbounded_growth.md | 4 +++ .../FU-BUG-T7-1_Validation_Report.md | 0 SPECS/ARCHIVE/INDEX.md | 4 ++- SPECS/INPROGRESS/next.md | 25 +++++++++---------- SPECS/Workplan.md | 7 +++--- 5 files changed, 23 insertions(+), 17 deletions(-) rename SPECS/{INPROGRESS => ARCHIVE/FU-BUG-T7-1_Cap_pending_methods_map_to_guard_unbounded_growth}/FU-BUG-T7-1_Cap_pending_methods_map_to_guard_unbounded_growth.md (97%) rename SPECS/{INPROGRESS => ARCHIVE/FU-BUG-T7-1_Cap_pending_methods_map_to_guard_unbounded_growth}/FU-BUG-T7-1_Validation_Report.md (100%) diff --git a/SPECS/INPROGRESS/FU-BUG-T7-1_Cap_pending_methods_map_to_guard_unbounded_growth.md b/SPECS/ARCHIVE/FU-BUG-T7-1_Cap_pending_methods_map_to_guard_unbounded_growth/FU-BUG-T7-1_Cap_pending_methods_map_to_guard_unbounded_growth.md similarity index 97% rename from SPECS/INPROGRESS/FU-BUG-T7-1_Cap_pending_methods_map_to_guard_unbounded_growth.md rename to SPECS/ARCHIVE/FU-BUG-T7-1_Cap_pending_methods_map_to_guard_unbounded_growth/FU-BUG-T7-1_Cap_pending_methods_map_to_guard_unbounded_growth.md index 3a3097cf..d7025ec5 100644 --- a/SPECS/INPROGRESS/FU-BUG-T7-1_Cap_pending_methods_map_to_guard_unbounded_growth.md +++ b/SPECS/ARCHIVE/FU-BUG-T7-1_Cap_pending_methods_map_to_guard_unbounded_growth/FU-BUG-T7-1_Cap_pending_methods_map_to_guard_unbounded_growth.md @@ -45,3 +45,7 @@ responses. - **Risk:** Test could become brittle if it depends on implementation details. **Mitigation:** Test via observable behavior (map size and call count), not private container type internals. + +--- +**Archived:** 2026-02-18 +**Verdict:** PASS diff --git a/SPECS/INPROGRESS/FU-BUG-T7-1_Validation_Report.md b/SPECS/ARCHIVE/FU-BUG-T7-1_Cap_pending_methods_map_to_guard_unbounded_growth/FU-BUG-T7-1_Validation_Report.md similarity index 100% rename from SPECS/INPROGRESS/FU-BUG-T7-1_Validation_Report.md rename to SPECS/ARCHIVE/FU-BUG-T7-1_Cap_pending_methods_map_to_guard_unbounded_growth/FU-BUG-T7-1_Validation_Report.md diff --git a/SPECS/ARCHIVE/INDEX.md b/SPECS/ARCHIVE/INDEX.md index f6e841e3..e2dc90e9 100644 --- a/SPECS/ARCHIVE/INDEX.md +++ b/SPECS/ARCHIVE/INDEX.md @@ -1,6 +1,6 @@ # mcpbridge-wrapper Tasks Archive -**Last Updated:** 2026-02-18 (REVIEW_FU-P13-T2-2) +**Last Updated:** 2026-02-18 (FU-BUG-T7-1) ## Archived Tasks @@ -114,6 +114,7 @@ | FU-P13-T4-2 | [FU-P13-T4-2_Implement_or_remove_reconnect_parameter_in_BrokerProxy/](FU-P13-T4-2_Implement_or_remove_reconnect_parameter_in_BrokerProxy/) | 2026-02-18 | PASS | | FU-P13-T2-1 | [FU-P13-T2-1_Replace_run_forever_polling_with_Event_wait/](FU-P13-T2-1_Replace_run_forever_polling_with_Event_wait/) | 2026-02-18 | PASS | | FU-P13-T2-2 | [FU-P13-T2-2_Move_PID_file_write_to_after_successful_upstream_launch/](FU-P13-T2-2_Move_PID_file_write_to_after_successful_upstream_launch/) | 2026-02-18 | PASS | +| FU-BUG-T7-1 | [FU-BUG-T7-1_Cap_pending_methods_map_to_guard_unbounded_growth/](FU-BUG-T7-1_Cap_pending_methods_map_to_guard_unbounded_growth/) | 2026-02-18 | PASS | ## Historical Artifacts @@ -336,3 +337,4 @@ | 2026-02-18 | FU-P13-T2-1 | Archived REVIEW_FU-P13-T2-1_event_wait_shutdown report | | 2026-02-18 | FU-P13-T2-2 | Archived Move_PID_file_write_to_after_successful_upstream_launch (PASS) | | 2026-02-18 | FU-P13-T2-2 | Archived REVIEW_FU-P13-T2-2_pid_write_order report | +| 2026-02-18 | FU-BUG-T7-1 | Archived Cap_pending_methods_map_to_guard_unbounded_growth (PASS) | diff --git a/SPECS/INPROGRESS/next.md b/SPECS/INPROGRESS/next.md index 6b6e9bf6..3c2d1fc2 100644 --- a/SPECS/INPROGRESS/next.md +++ b/SPECS/INPROGRESS/next.md @@ -1,17 +1,16 @@ -# Next Task: FU-BUG-T7-1 — Cap `pending_methods` map to guard against unbounded growth +# No Active Task -**Priority:** P3 -**Phase:** Phase 13: Persistent Broker & Shared Xcode Session -**Effort:** 1-2 hours -**Dependencies:** BUG-T7 -**Status:** Selected +## Recently Archived -## Description +- 2026-02-18 — FU-BUG-T7-1: Cap `pending_methods` map to guard against unbounded growth (PASS) +- 2026-02-18 — FU-P13-T2-2: Move PID file write to after successful upstream launch (PASS) +- 2026-02-18 — FU-P13-T2-1: Replace run_forever() polling loop with asyncio.Event-based wait (PASS) +- 2026-02-18 — FU-P13-T4-2: Implement or remove reconnect parameter in BrokerProxy (PASS) +- 2026-02-18 — FU-P13-T4-1: Fix asyncio.get_event_loop() deprecation in BrokerProxy (PASS) +- 2026-02-18 — P13-T6: Document broker mode configuration, migration, and rollback (PASS) -Harden `pending_methods` tracking in `src/mcpbridge_wrapper/__main__.py` so it cannot -grow unbounded under abnormal traffic patterns (e.g. bridge crash or one-way -messages), while preserving BUG-T7 method-correlation behavior. +## Suggested Next Tasks -## Next Step - -Run the PLAN command to generate the implementation-ready PRD. +- P13-T5 follow-up — Complete interactive prompt verification in a desktop session (P1) +- FU-P12-T1-1 — Remove or document `MCPInitializeParams` in schemas (P3) +- FU-P12-T1-2 — Add code comment clarifying stdin-only client capture in `on_request` (P3) diff --git a/SPECS/Workplan.md b/SPECS/Workplan.md index ed8787c8..731191bb 100644 --- a/SPECS/Workplan.md +++ b/SPECS/Workplan.md @@ -2188,7 +2188,8 @@ Phase 9 Follow-up Backlog --- -#### FU-BUG-T7-1: Cap `pending_methods` map to guard against unbounded growth +#### ✅ FU-BUG-T7-1: Cap `pending_methods` map to guard against unbounded growth +- **Status:** ✅ Completed (2026-02-18) - **Description:** The `pending_methods` dict in `__main__.py` (introduced in BUG-T7) maps request_id → method for all in-flight requests. In normal MCP traffic every request has exactly one response so the map stays small, but in abnormal conditions (bridge crash mid-flight, one-way messages) entries could accumulate. Add a bounded LRU eviction or periodic cleanup so the map cannot grow beyond a configurable maximum (e.g. 1000 entries). - **Priority:** P3 - **Dependencies:** BUG-T7 @@ -2197,8 +2198,8 @@ Phase 9 Follow-up Backlog - Updated `pending_methods` handling in `src/mcpbridge_wrapper/__main__.py` - Unit test that exercises high-volume in-flight requests without responses - **Acceptance Criteria:** - - [ ] `pending_methods` does not grow beyond a capped size under any traffic pattern - - [ ] Existing BUG-T7 normalization behavior is unaffected + - [x] `pending_methods` does not grow beyond a capped size under any traffic pattern + - [x] Existing BUG-T7 normalization behavior is unaffected --- From 926904c349d256c02d3303fc13cb23f927fcd167 Mon Sep 17 00:00:00 2001 From: Egor Merkushev Date: Wed, 18 Feb 2026 23:09:22 +0300 Subject: [PATCH 06/12] Review FU-BUG-T7-1: pending methods cap --- .../REVIEW_fu_bug_t7_1_pending_methods_cap.md | 37 +++++++++++++++++++ 1 file changed, 37 insertions(+) create mode 100644 SPECS/INPROGRESS/REVIEW_fu_bug_t7_1_pending_methods_cap.md diff --git a/SPECS/INPROGRESS/REVIEW_fu_bug_t7_1_pending_methods_cap.md b/SPECS/INPROGRESS/REVIEW_fu_bug_t7_1_pending_methods_cap.md new file mode 100644 index 00000000..57b90622 --- /dev/null +++ b/SPECS/INPROGRESS/REVIEW_fu_bug_t7_1_pending_methods_cap.md @@ -0,0 +1,37 @@ +## REVIEW REPORT — FU-BUG-T7-1 Pending Methods Cap + +**Scope:** origin/main..HEAD +**Files:** 7 + +### Summary Verdict +- [x] Approve +- [ ] Approve with comments +- [ ] Request changes +- [ ] Block + +### Critical Issues + +None. + +### Secondary Issues + +None. + +### Architectural Notes + +- The bounded insertion helper in `__main__.py` keeps method-correlation behavior + for active requests while preventing unbounded growth under abnormal traffic. +- Eviction policy is deterministic (oldest entry first), which keeps behavior + testable and easy to reason about. + +### Tests + +- `pytest -q` passed (`582 passed, 5 skipped`). +- `ruff check src/` passed. +- `mypy src/` passed. +- `pytest --cov=src/mcpbridge_wrapper --cov-report=term-missing` passed with + 92.19% coverage (>= 90% requirement). + +### Next Steps + +- No actionable follow-up items identified; FOLLOW-UP step should be skipped. From 84bbc6f98fa2f694116aa4430ab60d68aa509be7 Mon Sep 17 00:00:00 2001 From: Egor Merkushev Date: Wed, 18 Feb 2026 23:09:50 +0300 Subject: [PATCH 07/12] Archive REVIEW_fu_bug_t7_1_pending_methods_cap report --- SPECS/ARCHIVE/INDEX.md | 4 +++- .../_Historical}/REVIEW_fu_bug_t7_1_pending_methods_cap.md | 0 2 files changed, 3 insertions(+), 1 deletion(-) rename SPECS/{INPROGRESS => ARCHIVE/_Historical}/REVIEW_fu_bug_t7_1_pending_methods_cap.md (100%) diff --git a/SPECS/ARCHIVE/INDEX.md b/SPECS/ARCHIVE/INDEX.md index e2dc90e9..f0630794 100644 --- a/SPECS/ARCHIVE/INDEX.md +++ b/SPECS/ARCHIVE/INDEX.md @@ -1,6 +1,6 @@ # mcpbridge-wrapper Tasks Archive -**Last Updated:** 2026-02-18 (FU-BUG-T7-1) +**Last Updated:** 2026-02-18 (REVIEW_fu_bug_t7_1_pending_methods_cap) ## Archived Tasks @@ -192,6 +192,7 @@ | [REVIEW_FU-P13-T4-2_broker_proxy_reconnect.md](_Historical/REVIEW_FU-P13-T4-2_broker_proxy_reconnect.md) | Review report for FU-P13-T4-2 | | [REVIEW_FU-P13-T2-1_event_wait_shutdown.md](_Historical/REVIEW_FU-P13-T2-1_event_wait_shutdown.md) | Review report for FU-P13-T2-1 | | [REVIEW_FU-P13-T2-2_pid_write_order.md](_Historical/REVIEW_FU-P13-T2-2_pid_write_order.md) | Review report for FU-P13-T2-2 | +| [REVIEW_fu_bug_t7_1_pending_methods_cap.md](_Historical/REVIEW_fu_bug_t7_1_pending_methods_cap.md) | Review report for FU-BUG-T7-1 | ## Archive Log @@ -338,3 +339,4 @@ | 2026-02-18 | FU-P13-T2-2 | Archived Move_PID_file_write_to_after_successful_upstream_launch (PASS) | | 2026-02-18 | FU-P13-T2-2 | Archived REVIEW_FU-P13-T2-2_pid_write_order report | | 2026-02-18 | FU-BUG-T7-1 | Archived Cap_pending_methods_map_to_guard_unbounded_growth (PASS) | +| 2026-02-18 | FU-BUG-T7-1 | Archived REVIEW_fu_bug_t7_1_pending_methods_cap report | diff --git a/SPECS/INPROGRESS/REVIEW_fu_bug_t7_1_pending_methods_cap.md b/SPECS/ARCHIVE/_Historical/REVIEW_fu_bug_t7_1_pending_methods_cap.md similarity index 100% rename from SPECS/INPROGRESS/REVIEW_fu_bug_t7_1_pending_methods_cap.md rename to SPECS/ARCHIVE/_Historical/REVIEW_fu_bug_t7_1_pending_methods_cap.md From 3f921e72a3c157bf403eee176f637cb45b446e3d Mon Sep 17 00:00:00 2001 From: Egor Merkushev Date: Wed, 18 Feb 2026 23:12:02 +0300 Subject: [PATCH 08/12] Implement FU-BUG-T7-1: fix CI lint line-length regression --- tests/unit/test_main.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/tests/unit/test_main.py b/tests/unit/test_main.py index a4421452..50031828 100644 --- a/tests/unit/test_main.py +++ b/tests/unit/test_main.py @@ -733,7 +733,10 @@ def _prime_pending_methods(): f'{{"jsonrpc":"2.0","id":"req-{i}","method":"resources/list"}}' ) - mock_stdout_reader.return_value = (MagicMock(), _TriggeringQueue(_prime_pending_methods)) + mock_stdout_reader.return_value = ( + MagicMock(), + _TriggeringQueue(_prime_pending_methods), + ) with patch("mcpbridge_wrapper.__main__.sys.argv", ["mcpbridge-wrapper"]): result = main() From 82bbf1bc8638454a77595bc1e291914beff3e2b9 Mon Sep 17 00:00:00 2001 From: Egor Merkushev Date: Wed, 18 Feb 2026 23:13:48 +0300 Subject: [PATCH 09/12] Update waiting time --- .agents/skills/flow-run/SKILL.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.agents/skills/flow-run/SKILL.md b/.agents/skills/flow-run/SKILL.md index 36595a11..05a96271 100644 --- a/.agents/skills/flow-run/SKILL.md +++ b/.agents/skills/flow-run/SKILL.md @@ -79,7 +79,7 @@ Apply these rules throughout execution: - Body: summarise changes, list quality gate results, reference the validation report. 10. CI-REVIEW -- Wait at least 40 seconds after PR creation to allow GitHub Actions to start. +- Wait at least 50 seconds after PR creation to allow GitHub Actions to start. - Use `gh-pr-results-review` skill to inspect CI outcomes on the PR. - If all checks pass: report success and consider the run complete. - If checks fail: surface the actionable failure details from the skill output, fix the issues, push a new commit, then repeat CI-REVIEW. From 37bec50866c20dbc3ac68b07c09dc8404f084849 Mon Sep 17 00:00:00 2001 From: Egor Merkushev Date: Wed, 18 Feb 2026 23:19:07 +0300 Subject: [PATCH 10/12] Sync coverage docs and EXECUTE coverage-update step --- AGENTS.md | 4 ++-- README.md | 4 ++-- SPECS/COMMANDS/EXECUTE.md | 5 +++++ 3 files changed, 9 insertions(+), 4 deletions(-) diff --git a/AGENTS.md b/AGENTS.md index 3afa51b2..8db48488 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -40,7 +40,7 @@ A Python wrapper (`xcodemcpwrapper`) that intercepts responses from `xcrun mcpbr ### Metrics -- **Test Coverage:** 98.2% +- **Test Coverage:** 92.19% - **Performance:** <0.01ms overhead per transformation (0.0023ms avg) - **Memory:** <10MB footprint - **Lines of Code:** ~400 Python + 2000+ lines documentation @@ -104,7 +104,7 @@ A Python wrapper (`xcodemcpwrapper`) that intercepts responses from `xcrun mcpbr - **Python 3.7+** - Wrapper implementation (tested on 3.10.19) - **Xcode 26.3+** - Required for MCP bridge functionality - **MCP Protocol** - Model Context Protocol for AI tool integration -- **pytest** - Testing framework (98%+ coverage) +- **pytest** - Testing framework (~92% coverage) - **ruff** - Linting and formatting - **mypy** - Type checking diff --git a/README.md b/README.md index 06d68dd2..e7ac8d4a 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,7 @@ [![Python 3.7+](https://img.shields.io/badge/python-3.7+-blue.svg)](https://www.python.org/downloads/) [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT) -[![Coverage](https://img.shields.io/badge/coverage-98%25-brightgreen.svg)](./SPECS/ARCHIVE/P5-T14_Code_Coverage/) +[![Coverage](https://img.shields.io/badge/coverage-92.19%25-brightgreen.svg)](./SPECS/ARCHIVE/P5-T14_Code_Coverage/) [![MCP Registry](https://img.shields.io/badge/MCP%20Registry-io.github.SoundBlaster%2Fxcode--mcpbridge--wrapper-blue)](https://registry.modelcontextprotocol.io) A Python wrapper that makes Xcode 26.3's MCP bridge compatible with Cursor and other strict MCP-spec-compliant clients. @@ -534,7 +534,7 @@ make test && make lint && make typecheck - **Overhead:** <0.01ms per transformation - **Memory:** <10MB footprint -- **Coverage:** 98.2% test coverage +- **Coverage:** 92.19% test coverage ## License diff --git a/SPECS/COMMANDS/EXECUTE.md b/SPECS/COMMANDS/EXECUTE.md index 736d521e..34b2e10f 100644 --- a/SPECS/COMMANDS/EXECUTE.md +++ b/SPECS/COMMANDS/EXECUTE.md @@ -29,6 +29,10 @@ EXECUTE is a lightweight workflow wrapper for the Python-based mcpbridge-wrapper - Run `make lint` or `ruff check src/` for code quality - Run `make typecheck` or `mypy src/` for type checking (if configured) - Check test coverage: `pytest --cov=src/mcpbridge_wrapper --cov-report=term-missing` + - If reported coverage changed, update any published coverage references + (for example `/Users/egor/Development/GitHub/XcodeMCPWrapper/README.md` + badge/metrics and `/Users/egor/Development/GitHub/XcodeMCPWrapper/AGENTS.md`) + before finalizing commits. 4. **Refactor structure pass (if needed)** - Apply [`SPECS/COMMANDS/PRIMITIVES/REFACTORING.md`](./PRIMITIVES/REFACTORING.md) to split mixed changes into focused files before final commit. @@ -108,6 +112,7 @@ Before committing, ensure: - [ ] `pytest` passes (all tests) - [ ] `ruff check src/` passes (no linting errors) - [ ] Code coverage remains ≥90% for modified files +- [ ] Coverage references in docs/badges are updated if coverage changed - [ ] No regressions in existing tests ## Notes From 2f1afc0d82338e6ae93f7e0c1e1d735f3ba53271 Mon Sep 17 00:00:00 2001 From: Egor Merkushev Date: Wed, 18 Feb 2026 23:50:15 +0300 Subject: [PATCH 11/12] Add workplan follow-ups for multi-client and in-flight dashboard KPIs --- SPECS/Workplan.md | 36 ++++++++++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/SPECS/Workplan.md b/SPECS/Workplan.md index 731191bb..f296024a 100644 --- a/SPECS/Workplan.md +++ b/SPECS/Workplan.md @@ -2229,6 +2229,42 @@ Phase 9 Follow-up Backlog --- +#### FU-P12-T1-3: Show multi-client widgets in Web UI instead of single overwritten active client +- **Description:** The dashboard currently displays one `ACTIVE CLIENT` value that is overwritten by the most recent `initialize` handshake. Add multi-client visibility so the UI can show one widget/card per detected client (e.g., Codex, Zed, Cursor) with useful metadata (last seen and/or call counts), rather than a single global value. +- **Priority:** P2 +- **Dependencies:** P12-T1 +- **Parallelizable:** no +- **Outputs/Artifacts:** + - Updated `src/mcpbridge_wrapper/webui/shared_metrics.py` and/or `src/mcpbridge_wrapper/webui/metrics.py` to expose per-client summaries + - Updated `src/mcpbridge_wrapper/webui/server.py` API response schema (or new endpoint) for multi-client dashboard data + - Updated `src/mcpbridge_wrapper/webui/static/index.html` and `src/mcpbridge_wrapper/webui/static/dashboard.js` to render one widget per client + - Updated Web UI tests covering multi-client display behavior +- **Acceptance Criteria:** + - [ ] Dashboard shows multiple clients simultaneously when more than one client connects + - [ ] Existing single-client behavior remains correct when only one client is present + - [ ] Client widgets update in real time with the same refresh cadence as other KPIs + - [ ] `pytest` suite remains green + +--- + +#### FU-P12-T1-4: Make `IN FLIGHT` KPI reflect real in-flight requests in shared-metrics mode +- **Description:** In shared SQLite metrics mode, `/api/metrics` currently returns `in_flight: 0` unconditionally, so the `IN FLIGHT` widget is not informative. Add process-safe in-flight tracking so this KPI reports the true number of outstanding requests across active wrapper processes. +- **Priority:** P2 +- **Dependencies:** P12-T1 +- **Parallelizable:** no +- **Outputs/Artifacts:** + - Updated `src/mcpbridge_wrapper/webui/shared_metrics.py` with shared in-flight tracking strategy + - Updated `src/mcpbridge_wrapper/__main__.py` request/response hooks to record and clear in-flight entries consistently + - Updated `src/mcpbridge_wrapper/webui/server.py` metrics payload (if schema adjustments are needed) + - Updated tests for shared-metrics in-flight behavior +- **Acceptance Criteria:** + - [ ] `IN FLIGHT` KPI is greater than zero while requests are in progress and returns to zero after responses + - [ ] Works correctly with multiple concurrent clients/processes using the shared metrics database + - [ ] No regressions in existing dashboard metrics endpoints + - [ ] `pytest` suite remains green + +--- + #### FU-P12-T3-1: Document unused `error_message` parameter in `MetricsCollector.record_response` - **Description:** `MetricsCollector.record_response()` accepts `error_message: Optional[str]` for API symmetry with `SharedMetricsStore`, but never stores or uses it. Add a docstring note clarifying this parameter is accepted for interface compatibility but not persisted in the in-memory collector. - **Priority:** P3 From 79b08bbb0fc0504ac605d600eba3f35007b2c0c1 Mon Sep 17 00:00:00 2001 From: Egor Merkushev Date: Wed, 18 Feb 2026 23:54:32 +0300 Subject: [PATCH 12/12] Sync DocC coverage value with README --- Sources/XcodeMCPWrapper/Documentation.docc/XcodeMCPWrapper.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Sources/XcodeMCPWrapper/Documentation.docc/XcodeMCPWrapper.md b/Sources/XcodeMCPWrapper/Documentation.docc/XcodeMCPWrapper.md index 3d57f978..5e95e84b 100644 --- a/Sources/XcodeMCPWrapper/Documentation.docc/XcodeMCPWrapper.md +++ b/Sources/XcodeMCPWrapper/Documentation.docc/XcodeMCPWrapper.md @@ -305,7 +305,7 @@ Open http://localhost:8080 in your browser to view the dashboard. | Metric | Value | |--------|-------| -| Test Coverage | 98.2% | +| Test Coverage | 92.19% | | Performance Overhead | <0.01ms per transformation | | Memory Footprint | <10MB |