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
2 changes: 1 addition & 1 deletion .agents/skills/flow-run/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
4 changes: 2 additions & 2 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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

Expand Down
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -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

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
# 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.

---
**Archived:** 2026-02-18
**Verdict:** PASS
Original file line number Diff line number Diff line change
@@ -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.
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-18 (REVIEW_FU-P13-T2-2)
**Last Updated:** 2026-02-18 (REVIEW_fu_bug_t7_1_pending_methods_cap)

## Archived Tasks

Expand Down Expand Up @@ -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

Expand Down Expand Up @@ -191,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

Expand Down Expand Up @@ -336,3 +338,5 @@
| 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) |
| 2026-02-18 | FU-BUG-T7-1 | Archived REVIEW_fu_bug_t7_1_pending_methods_cap report |
Original file line number Diff line number Diff line change
@@ -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.
5 changes: 5 additions & 0 deletions SPECS/COMMANDS/EXECUTE.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -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
Expand Down
4 changes: 2 additions & 2 deletions SPECS/INPROGRESS/next.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,15 @@

## Recently Archived

- 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)
- 2026-02-18 — P13-T5: Validate prompt reduction and multi-client stability (PARTIAL)

## Suggested Next Tasks

- 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)
- FU-P12-T1-2 — Add code comment clarifying stdin-only client capture in `on_request` (P3)
43 changes: 40 additions & 3 deletions SPECS/Workplan.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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

---

Expand Down Expand Up @@ -2228,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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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 |

Expand Down
34 changes: 33 additions & 1 deletion src/mcpbridge_wrapper/__main__.py
Original file line number Diff line number Diff line change
Expand Up @@ -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."""
Expand Down Expand Up @@ -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.

Expand Down Expand Up @@ -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:
Expand Down
Loading