Skip to content
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
# PRD: FU-P13-T4-1 — Fix asyncio.get_event_loop() deprecation in BrokerProxy

**Created:** 2026-02-18
**Priority:** P2
**Branch:** `feature/FU-P13-T4-1-fix-asyncio-loop-deprecation`
**Status:** PLAN

---

## 1. Problem Statement

`src/mcpbridge_wrapper/broker/proxy.py` still uses `asyncio.get_event_loop()` in async code paths. Python 3.10+ deprecates this usage in favor of `asyncio.get_running_loop()` when inside a running event loop.

This follow-up removes deprecated calls in proxy timeout and stdio wrapping helpers without changing broker behavior.

---

## 2. Scope

### In Scope
- Replace `asyncio.get_event_loop()` with `asyncio.get_running_loop()` in:
- `BrokerProxy._spawn_broker_if_needed`
- `BrokerProxy._connect_with_timeout`
- `BrokerProxy._make_stdin_reader`
- `BrokerProxy._make_stdout_writer`
- Keep semantics unchanged (timeouts, retries, forwarding).
- Run tests and quality gates.

### Out of Scope
- Reconnect behavior changes (`FU-P13-T4-2`).
- Broker daemon lifecycle changes.
- Any CLI/flag behavior changes.

---

## 3. Deliverables

1. `src/mcpbridge_wrapper/broker/proxy.py`
- Replace all deprecated loop access calls with running-loop access.
- Cache loop once per method where appropriate.

2. `tests/unit/test_broker_proxy.py` (if needed)
- Keep/extend tests only if behavior needs explicit coverage updates.

3. `SPECS/INPROGRESS/FU-P13-T4-1_Validation_Report.md`
- Record command outputs and acceptance evidence.

---

## 4. Acceptance Criteria

- [ ] All `asyncio.get_event_loop()` calls in `src/mcpbridge_wrapper/broker/proxy.py` are replaced with `asyncio.get_running_loop()`.
- [ ] Broker proxy tests pass.
- [ ] Full quality gates pass:
- `pytest`
- `ruff check src/`
- `mypy src/`
- `pytest --cov` (coverage >= 90%)

---

## 5. Dependencies

- P13-T4 ✅

---

## 6. Risks and Mitigations

- **Risk:** `get_running_loop()` raises `RuntimeError` if called outside a running loop.
- **Mitigation:** Only use it inside async methods already awaited from running loop contexts.

---

## 7. Validation Plan

1. Verify no `get_event_loop` usages remain in proxy module.
2. Run targeted test module: `pytest tests/unit/test_broker_proxy.py`.
3. Run full required gates and capture results in validation report.

---
**Archived:** 2026-02-18
**Verdict:** PASS
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
# Validation Report: FU-P13-T4-1

**Task:** FU-P13-T4-1 — Fix asyncio.get_event_loop() deprecation in BrokerProxy
**Date:** 2026-02-18
**Branch:** `feature/FU-P13-T4-1-fix-asyncio-loop-deprecation`

## Scope validated

- Updated `src/mcpbridge_wrapper/broker/proxy.py` to replace deprecated
`asyncio.get_event_loop()` calls with `asyncio.get_running_loop()` in:
- `_spawn_broker_if_needed`
- `_connect_with_timeout`
- `_make_stdin_reader`
- `_make_stdout_writer`
- Preserved broker proxy behavior (timeout/retry, stdio bridge wiring).

## Quality gates

### 1) Targeted task test

Command:

```bash
pytest tests/unit/test_broker_proxy.py
```

Result: **PASS** (`15 passed`)

### 2) Full test suite

Command:

```bash
pytest
```

Result: **PASS** (`577 passed, 5 skipped`)

### 3) Lint

Command:

```bash
ruff check src/
```

Result: **PASS** (`All checks passed!`)

### 4) Type checks

Command:

```bash
mypy src/
```

Result: **PASS** (`Success: no issues found in 18 source files`)

### 5) Coverage

Command:

```bash
pytest --cov
```

Result: **PASS** (`Total coverage: 92.32%`, threshold: `>= 90%`)

## Acceptance criteria evidence

- [x] All `asyncio.get_event_loop()` calls in `src/mcpbridge_wrapper/broker/proxy.py` are replaced with `asyncio.get_running_loop()`
- Evidence: `rg -n "get_event_loop" src/mcpbridge_wrapper/broker/proxy.py` returned no matches.
- [x] Broker proxy tests pass
- Evidence: `pytest tests/unit/test_broker_proxy.py` -> `15 passed`.
- [x] Full quality gates pass (`pytest`, `ruff check src/`, `mypy src/`, `pytest --cov >= 90%`)
- Evidence: all commands above passed; coverage `92.32%`.

## Notes

- Existing warnings from `websockets` deprecations in Web UI tests 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 (P13-T6)
**Last Updated:** 2026-02-18 (FU-P13-T4-1)

## Archived Tasks

Expand Down Expand Up @@ -110,6 +110,7 @@
| P13-T4 | [P13-T4_Add_stdio_proxy_mode/](P13-T4_Add_stdio_proxy_mode/) | 2026-02-18 | PASS |
| P13-T5 | [P13-T5_Validate_prompt_reduction_and_multi_client_stability/](P13-T5_Validate_prompt_reduction_and_multi_client_stability/) | 2026-02-18 | PARTIAL |
| P13-T6 | [P13-T6_Document_broker_mode_configuration_migration_and_rollback/](P13-T6_Document_broker_mode_configuration_migration_and_rollback/) | 2026-02-18 | PASS |
| FU-P13-T4-1 | [FU-P13-T4-1_Fix_asyncio_get_event_loop_deprecation_in_BrokerProxy/](FU-P13-T4-1_Fix_asyncio_get_event_loop_deprecation_in_BrokerProxy/) | 2026-02-18 | PASS |

## Historical Artifacts

Expand Down Expand Up @@ -183,6 +184,7 @@
| [REVIEW_P13-T4_stdio_proxy_mode.md](_Historical/REVIEW_P13-T4_stdio_proxy_mode.md) | Review report for P13-T4 |
| [REVIEW_P13-T5_prompt_reduction_multi_client_stability.md](_Historical/REVIEW_P13-T5_prompt_reduction_multi_client_stability.md) | Review report for P13-T5 |
| [REVIEW_P13-T6_broker_mode_configuration_migration.md](_Historical/REVIEW_P13-T6_broker_mode_configuration_migration.md) | Review report for P13-T6 |
| [REVIEW_FU-P13-T4-1_broker_proxy_loop.md](_Historical/REVIEW_FU-P13-T4-1_broker_proxy_loop.md) | Review report for FU-P13-T4-1 |

## Archive Log

Expand Down Expand Up @@ -320,3 +322,5 @@
| 2026-02-18 | P13-T5 | Archived REVIEW_P13-T5_prompt_reduction_multi_client_stability report |
| 2026-02-18 | P13-T6 | Archived Document_broker_mode_configuration_migration_and_rollback (PASS) |
| 2026-02-18 | P13-T6 | Archived REVIEW_P13-T6_broker_mode_configuration_migration report |
| 2026-02-18 | FU-P13-T4-1 | Archived Fix_asyncio_get_event_loop_deprecation_in_BrokerProxy (PASS) |
| 2026-02-18 | FU-P13-T4-1 | Archived REVIEW_FU-P13-T4-1_broker_proxy_loop report |
34 changes: 34 additions & 0 deletions SPECS/ARCHIVE/_Historical/REVIEW_FU-P13-T4-1_broker_proxy_loop.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
## REVIEW REPORT — FU-P13-T4-1 broker proxy loop deprecation fix

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

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

### Critical Issues

None.

### Secondary Issues

None.

### Architectural Notes

- The change is narrowly scoped and behavior-preserving: timeout windows and stream bridge logic are unchanged while loop acquisition now matches modern asyncio guidance.
- No new concurrency or lifecycle risks were introduced in broker proxy paths.

### Tests

- `pytest` passed (`577 passed, 5 skipped`)
- `ruff check src/` passed
- `mypy src/` passed
- `pytest --cov` passed (`92.32%`, threshold `>= 90%`)

### Next Steps

- No follow-up tasks required from this review.
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-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)
- 2026-02-18 — P13-T4: Add stdio proxy mode for compatibility with existing MCP clients (PASS)
- 2026-02-18 — P13-T3: Implement multi-client transport and JSON-RPC multiplexing (PASS)
- 2026-02-17 — P13-T2: Implement persistent broker daemon with single upstream Xcode bridge (PASS)
- 2026-02-16 — P13-T1: Design persistent broker architecture and protocol contract (PASS)

## Suggested Next Tasks

- FU-P13-T4-1 — Replace deprecated `asyncio.get_event_loop()` calls in `broker/proxy.py` (P2)
- FU-P13-T4-2 — Implement or remove `reconnect` parameter in `BrokerProxy` (P2)
- 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)
7 changes: 4 additions & 3 deletions SPECS/Workplan.md
Original file line number Diff line number Diff line change
Expand Up @@ -2066,16 +2066,17 @@ Phase 9 Follow-up Backlog

---

#### FU-P13-T4-1: Fix asyncio.get_event_loop() deprecation in BrokerProxy
#### ✅ FU-P13-T4-1: Fix asyncio.get_event_loop() deprecation in BrokerProxy
- **Status:** ✅ Completed (2026-02-18)
- **Description:** Replace `asyncio.get_event_loop()` calls with `asyncio.get_running_loop()` in `BrokerProxy._spawn_broker_if_needed` and `BrokerProxy._connect_with_timeout` to comply with Python 3.10+ asyncio API.
- **Priority:** P2
- **Dependencies:** P13-T4
- **Parallelizable:** yes
- **Outputs/Artifacts:**
- Updated `src/mcpbridge_wrapper/broker/proxy.py`
- **Acceptance Criteria:**
- [ ] All `asyncio.get_event_loop()` calls in proxy.py replaced with `asyncio.get_running_loop()`
- [ ] Tests still pass
- [x] All `asyncio.get_event_loop()` calls in proxy.py replaced with `asyncio.get_running_loop()`
- [x] Tests still pass

---

Expand Down
14 changes: 8 additions & 6 deletions src/mcpbridge_wrapper/broker/proxy.py
Original file line number Diff line number Diff line change
Expand Up @@ -145,8 +145,9 @@ async def _spawn_broker_if_needed(self) -> None:
)

# Poll for socket appearance
deadline = asyncio.get_event_loop().time() + self._connect_timeout
while asyncio.get_event_loop().time() < deadline:
loop = asyncio.get_running_loop()
deadline = loop.time() + self._connect_timeout
while loop.time() < deadline:
if socket_path.exists():
logger.debug("Broker socket appeared.")
return
Expand All @@ -161,10 +162,11 @@ async def _connect_with_timeout(
) -> tuple[asyncio.StreamReader, asyncio.StreamWriter]:
"""Connect to the broker Unix socket, retrying until timeout."""
socket_path = str(self._config.socket_path)
deadline = asyncio.get_event_loop().time() + self._connect_timeout
loop = asyncio.get_running_loop()
deadline = loop.time() + self._connect_timeout
last_exc: Exception = FileNotFoundError(f"Socket not found: {socket_path}")

while asyncio.get_event_loop().time() < deadline:
while loop.time() < deadline:
try:
reader, writer = await asyncio.open_unix_connection(socket_path)
logger.debug("Connected to broker at %s", socket_path)
Expand Down Expand Up @@ -240,7 +242,7 @@ async def _forward_stream(
@staticmethod
async def _make_stdin_reader() -> asyncio.StreamReader:
"""Wrap sys.stdin.buffer as an asyncio StreamReader."""
loop = asyncio.get_event_loop()
loop = asyncio.get_running_loop()
reader = asyncio.StreamReader()
protocol = asyncio.StreamReaderProtocol(reader)
await loop.connect_read_pipe(lambda: protocol, sys.stdin.buffer)
Expand All @@ -249,7 +251,7 @@ async def _make_stdin_reader() -> asyncio.StreamReader:
@staticmethod
async def _make_stdout_writer() -> asyncio.StreamWriter:
"""Wrap sys.stdout.buffer as an asyncio StreamWriter."""
loop = asyncio.get_event_loop()
loop = asyncio.get_running_loop()
transport, protocol = await loop.connect_write_pipe(asyncio.BaseProtocol, sys.stdout.buffer)
writer = asyncio.StreamWriter(transport, protocol, None, loop)
return writer