From 630f943268082f7fdb5088cee908f634fd046bc1 Mon Sep 17 00:00:00 2001 From: Egor Merkushev Date: Sun, 1 Mar 2026 00:47:47 +0300 Subject: [PATCH 1/2] Archive workplan 0.4.0 and reset tracker --- SPECS/ARCHIVE/INDEX.md | 4 +- SPECS/ARCHIVE/_Historical/Workplan_0.4.0.md | 3210 +++++++++++++++++++ SPECS/Workplan.md | 3210 +------------------ 3 files changed, 3219 insertions(+), 3205 deletions(-) create mode 100644 SPECS/ARCHIVE/_Historical/Workplan_0.4.0.md diff --git a/SPECS/ARCHIVE/INDEX.md b/SPECS/ARCHIVE/INDEX.md index ea12e8c4..de63574e 100644 --- a/SPECS/ARCHIVE/INDEX.md +++ b/SPECS/ARCHIVE/INDEX.md @@ -1,6 +1,6 @@ # mcpbridge-wrapper Tasks Archive -**Last Updated:** 2026-02-28 (REVIEW_p15_t1_next_release_readiness.md) +**Last Updated:** 2026-03-01 (Workplan_0.4.0.md) ## Archived Tasks @@ -167,6 +167,7 @@ | File | Description | |------|-------------| +| [Workplan_0.4.0.md](_Historical/Workplan_0.4.0.md) | Archived workplan snapshot for release 0.4.0 | | [REVIEW_p15_t1_next_release_readiness.md](_Historical/REVIEW_p15_t1_next_release_readiness.md) | Review report for P15-T1 | | [REVIEW_fu_p13_t19_broker_webui_observability.md](_Historical/REVIEW_fu_p13_t19_broker_webui_observability.md) | Review report for FU-P13-T19 | | [REVIEW_fu_p13_t18_unified_config_docs.md](_Historical/REVIEW_fu_p13_t18_unified_config_docs.md) | Review report for FU-P13-T18 | @@ -283,6 +284,7 @@ | Date | Task ID | Action | |------|---------|--------| +| 2026-03-01 | WORKPLAN | Archived Workplan_0.4.0.md to _Historical and reset SPECS/Workplan.md | | 2026-02-28 | P15-T1 | Archived REVIEW_p15_t1_next_release_readiness report | | 2026-02-28 | P15-T1 | Archived Validate_project_readiness_for_the_next_release (PASS) | | 2026-02-28 | FU-P13-T19 | Archived REVIEW_fu_p13_t19_broker_webui_observability report | diff --git a/SPECS/ARCHIVE/_Historical/Workplan_0.4.0.md b/SPECS/ARCHIVE/_Historical/Workplan_0.4.0.md new file mode 100644 index 00000000..8961db96 --- /dev/null +++ b/SPECS/ARCHIVE/_Historical/Workplan_0.4.0.md @@ -0,0 +1,3210 @@ +# Workplan: mcpbridge-wrapper + +## 1. Overview + +### 1.1 Goal +Create a Python-based protocol compatibility wrapper that intercepts MCP responses from Xcode 26.3's `xcrun mcpbridge` and transforms non-compliant responses into MCP-spec-compliant format by injecting the required `structuredContent` field. + +### 1.2 Key Assumptions (from PRD §1.4) +- Xcode 26.3+ is installed with MCP bridge enabled +- Python 3.7+ is available (standard macOS installation) +- Target users are developers using Cursor or other strict MCP-spec-compliant clients +- Xcode must be running with a project open for tools to function + +### 1.3 Constraints (from PRD §1.4) +- Wrapper must be a single executable Python script for easy distribution +- Response latency overhead must be < 5ms per request (NFR1) +- Memory footprint must be < 10MB (NFR2) +- Must handle concurrent bidirectional I/O (FR10) + +### 1.4 Non-Goals +- Do NOT modify Xcode's mcpbridge binary or internal behavior +- Do NOT implement caching or response buffering beyond line-level +- Do NOT create a GUI or interactive configuration tool +- Do NOT support Python versions below 3.7 +- Do NOT implement the 20 Xcode MCP tools (only wrap the existing bridge) + +--- + +## 2. Phases + +### Phase 1: Foundation & Scaffolding +**Intent:** Establish project structure, Python packaging, and development tooling to support implementation and testing. + +### Phase 2: Core Bridge Implementation +**Intent:** Implement the subprocess wrapper around `xcrun mcpbridge` with bidirectional stdin/stdout piping and async I/O handling. + +### Phase 3: Response Transformation Engine +**Intent:** Implement JSON parsing, MCP compliance detection, and the `structuredContent` injection logic per PRD §3.1 Functional Requirements. + +### Phase 4: Edge Case Handling +**Intent:** Ensure robust handling of all failure scenarios and edge cases documented in PRD §5.1-5.2. + +### Phase 5: Testing & Verification +**Intent:** Validate all functional requirements, non-functional requirements, and success criteria through comprehensive unit and integration tests. + +### Phase 6: Packaging & Distribution +**Intent:** Create installable artifacts and configuration templates for Cursor, Claude Code, and Codex CLI. + +### Phase 7: Documentation +**Intent:** Produce user-facing documentation for installation, configuration, and troubleshooting. + +### Phase 10: Web UI Control & Audit Dashboard +**Intent:** Create a web-based dashboard for real-time monitoring, control, and audit logging of the XcodeMCPWrapper. + +### Phase 11: Web UI UX Improvements +**Intent:** Enhance the dashboard with better debugging tools, session awareness, theming, and keyboard-driven workflows. + +### Phase 12: Data Collection Enhancements +**Intent:** Enrich collected telemetry with client identity, parameter patterns, and structured error classification for deeper operational insight. + +### Phase 13: Persistent Broker & Shared Xcode Session +**Intent:** Introduce a long-lived broker connection to Xcode MCP so multiple short-lived clients can reuse one upstream session and reduce repeated Xcode permission prompts. + +### Phase 14: Release 0.4.0 Readiness +**Intent:** Close release-blocking findings from the 0.4.0 readiness review across broker runtime safety, package metadata, versioning, and build compliance. + +### Phase 15: Next Release Readiness Validation +**Intent:** Validate release readiness for the next version with full quality-gate, packaging, and metadata checks before publishing. + +--- + +## 3. Tasks + +### Phase 1: Foundation & Scaffolding + +#### ✅ P1-T1: Create Project Directory Structure +- **Description:** Create `src/mcpbridge_wrapper/`, `tests/unit/`, `tests/integration/`, and `scripts/` directories +- **Priority:** P0 +- **Dependencies:** none +- **Parallelizable:** no +- **Outputs/Artifacts:** + - Directory tree structure + - `src/mcpbridge_wrapper/__init__.py` (empty) +- **Acceptance Criteria:** All directories exist and are importable as Python packages + +#### ✅ P1-T2: Initialize Python Project with pyproject.toml +- **Description:** Create `pyproject.toml` with project metadata, Python 3.7+ requirement, and build system configuration +- **Priority:** P0 +- **Dependencies:** P1-T1 +- **Parallelizable:** no +- **Outputs/Artifacts:** + - `pyproject.toml` with [project], [build-system], and [project.scripts] sections +- **Acceptance Criteria:** `pip install -e .` succeeds and installs the package + +#### ✅ P1-T3: Configure Linting and Formatting Tools +- **Description:** Add ruff configuration for linting/formatting and mypy for type checking in pyproject.toml +- **Priority:** P1 +- **Dependencies:** P1-T2 +- **Parallelizable:** yes +- **Outputs/Artifacts:** + - Linting rules in `pyproject.toml` + - `.pre-commit-config.yaml` (optional) +- **Acceptance Criteria:** `ruff check src/` runs without configuration errors + +#### ✅ P1-T4: Set up pytest Configuration +- **Description:** Configure pytest with coverage reporting in pyproject.toml +- **Priority:** P0 +- **Dependencies:** P1-T2 +- **Parallelizable:** yes +- **Outputs/Artifacts:** + - `[tool.pytest.ini_options]` and `[tool.coverage.run]` in pyproject.toml +- **Acceptance Criteria:** `pytest --version` reads config without errors; `pytest` runs (even with 0 tests) + +#### ✅ P1-T5: Create Makefile with Common Tasks +- **Description:** Add Makefile targets for test, lint, format, typecheck, and install +- **Priority:** P2 +- **Dependencies:** P1-T3, P1-T4 +- **Parallelizable:** yes +- **Outputs/Artifacts:** + - `Makefile` with targets: test, lint, format, typecheck, install, clean +- **Acceptance Criteria:** `make test` runs pytest; `make lint` runs ruff check + +#### ✅ P1-T6: Add Python .gitignore +- **Description:** Create .gitignore with standard Python patterns (venv, __pycache__, *.pyc, etc.) +- **Priority:** P1 +- **Dependencies:** P1-T1 +- **Parallelizable:** yes +- **Outputs/Artifacts:** + - `.gitignore` file +- **Acceptance Criteria:** `git status` does not show Python cache files or virtual environment directories + +--- + +### Phase 2: Core Bridge Implementation + +#### ✅ P2-T1: Implement Subprocess Bridge to xcrun mcpbridge +- **Description:** Create subprocess.Popen wrapper that launches `xcrun mcpbridge` with stdin/stdout pipes per PRD §3.1 FR1-FR2 +- **Priority:** P0 +- **Dependencies:** P1-T1 +- **Parallelizable:** no +- **Outputs/Artifacts:** + - `src/mcpbridge_wrapper/bridge.py` with `create_bridge()` function +- **Acceptance Criteria:** Function returns a Popen object with readable stdout and writable stdin; process starts without errors when Xcode is running + +#### ✅ P2-T2: Implement Stdin Forwarding Loop +- **Description:** Forward all stdin lines from wrapper process to mcpbridge stdin unmodified per PRD §3.1 FR2 +- **Priority:** P0 +- **Dependencies:** P2-T1 +- **Parallelizable:** no +- **Outputs/Artifacts:** + - `forward_stdin()` function in `bridge.py` +- **Acceptance Criteria:** Raw bytes from sys.stdin appear identically on bridge.stdin; manual test with echo confirms passthrough + +#### ✅ P2-T3: Implement Stdout Capture with Line Buffering +- **Description:** Read stdout from bridge line-by-line with bufsize=1 (line buffering) per PRD §3.1 FR9 +- **Priority:** P0 +- **Dependencies:** P2-T1 +- **Parallelizable:** no +- **Outputs/Artifacts:** + - `read_stdout()` generator function in `bridge.py` +- **Acceptance Criteria:** Each yielded item is a complete line (ends with newline); no partial line buffering issues + +#### ✅ P2-T4: Add Daemon Thread for Async Stdout Reading +- **Description:** Spawn daemon thread that runs stdout reader to prevent blocking main thread per PRD §3.1 FR10 +- **Priority:** P0 +- **Dependencies:** P2-T3 +- **Parallelizable:** no +- **Outputs/Artifacts:** + - Thread spawning logic in `bridge.py` + - Queue for thread-safe line passing +- **Acceptance Criteria:** Main thread can continue processing while stdout is being read; thread terminates when bridge exits + +#### ✅ P2-T5: Implement Stderr Passthrough +- **Description:** Pass stderr from bridge directly to wrapper's stderr without modification +- **Priority:** P1 +- **Dependencies:** P2-T1 +- **Parallelizable:** yes +- **Outputs/Artifacts:** + - stderr forwarding in subprocess.Popen call +- **Acceptance Criteria:** Error messages from mcpbridge appear on terminal immediately + +#### ✅ P2-T6: Handle Bridge Process Lifecycle +- **Description:** Implement startup verification, clean shutdown on exit, and exit code propagation +- **Priority:** P1 +- **Dependencies:** P2-T1 +- **Parallelizable:** yes +- **Outputs/Artifacts:** + - `cleanup()` function; exit code handling in main +- **Acceptance Criteria:** Wrapper exits with same code as mcpbridge; no zombie processes left + +#### ✅ P2-T7: Forward Command-Line Arguments +- **Description:** Pass sys.argv[1:] to mcpbridge subprocess to support any bridge arguments +- **Priority:** P1 +- **Dependencies:** P2-T1 +- **Parallelizable:** yes +- **Outputs/Artifacts:** + - Argument forwarding in Popen args list +- **Acceptance Criteria:** Running `wrapper --help` shows mcpbridge help output + +--- + +### Phase 3: Response Transformation Engine + +#### ✅ P3-T1: Implement JSON Detection Logic +- **Description:** Detect whether a line is valid JSON or plain text per PRD §3.1 FR3 +- **Priority:** P0 +- **Dependencies:** P2-T3 +- **Parallelizable:** no +- **Outputs/Artifacts:** + - `is_json_line()` function in `src/mcpbridge_wrapper/transform.py` +- **Acceptance Criteria:** Returns True for `{"key": "value"}`; Returns False for `Plain text log` + +#### ✅ P3-T2: Implement JSON Parsing with Error Handling +- **Description:** Parse JSON lines with try/except; re-raise or handle decode errors per PRD §3.1 FR3 +- **Priority:** P0 +- **Dependencies:** P3-T1 +- **Parallelizable:** no +- **Outputs/Artifacts:** + - `parse_json_safe()` function returning (success, data) tuple +- **Acceptance Criteria:** Valid JSON returns (True, dict); Invalid JSON returns (False, original_line) + +#### ✅ P3-T3: Detect Non-Compliant Responses +- **Description:** Identify responses with `content` field but missing `structuredContent` per PRD §3.1 FR4 +- **Priority:** P0 +- **Dependencies:** P3-T2 +- **Parallelizable:** no +- **Outputs/Artifacts:** + - `needs_transformation()` function checking result object structure +- **Acceptance Criteria:** Returns True for `{"result": {"content": []}}`; Returns False if `structuredContent` exists + +#### ✅ P3-T4: Extract Text from Content Array +- **Description:** Find first content item with `type: "text"` and extract its `text` field per PRD §3.1 FR5 +- **Priority:** P0 +- **Dependencies:** P3-T3 +- **Parallelizable:** no +- **Outputs/Artifacts:** + - `extract_text_content()` function +- **Acceptance Criteria:** Given `[{"type": "image"}, {"type": "text", "text": "data"}]`, returns `"data"`; returns None if no text items + +#### ✅ P3-T5: Parse Extracted Text as JSON +- **Description:** Attempt to parse extracted text content as JSON object per PRD §3.1 FR6 +- **Priority:** P0 +- **Dependencies:** P3-T4 +- **Parallelizable:** no +- **Outputs/Artifacts:** + - `parse_structured_content()` function +- **Acceptance Criteria:** `{"result": true}` string becomes dict; `"plain string"` becomes string primitive; invalid JSON raises exception + +#### ✅ P3-T6: Implement Fallback Wrapper for Invalid JSON +- **Description:** On JSON decode error, wrap text in `{"text": content}` structure per PRD §3.1 FR7 +- **Priority:** P1 +- **Dependencies:** P3-T5 +- **Parallelizable:** no +- **Outputs/Artifacts:** + - Fallback logic in `parse_structured_content()` or caller +- **Acceptance Criteria:** Non-JSON text `"error message"` becomes `{"text": "error message"}` + +#### ✅ P3-T7: Inject structuredContent into Result +- **Description:** Add `structuredContent` field to result object with parsed JSON value per PRD §3.1 FR6-FR7 +- **Priority:** P0 +- **Dependencies:** P3-T5, P3-T6 +- **Parallelizable:** no +- **Outputs/Artifacts:** + - `inject_structured_content()` function +- **Acceptance Criteria:** Input `{"result": {"content": [{"text": "{}"}]}}` becomes `{"result": {"content": [...], "structuredContent": {}}}` + +#### ✅ P3-T8: Implement Non-JSON Output Passthrough +- **Description:** Pass through non-JSON lines (logs, errors) unmodified per PRD §3.1 FR8 +- **Priority:** P1 +- **Dependencies:** P3-T1 +- **Parallelizable:** yes +- **Outputs/Artifacts:** + - Branch in main processing loop for passthrough +- **Acceptance Criteria:** Plain text lines appear on stdout unchanged and unwrapped + +#### ✅ P3-T9: Implement Unbuffered Output +- **Description:** Use `flush=True` on all stdout write operations per PRD §3.1 FR9 +- **Priority:** P0 +- **Dependencies:** P3-T7, P3-T8 +- **Parallelizable:** yes +- **Outputs/Artifacts:** + - `print(..., flush=True)` or `sys.stdout.write()` with explicit flush +- **Acceptance Criteria:** Responses appear immediately (no buffering delay visible) + +#### ✅ P3-T10: Implement Main Response Processing Loop +- **Description:** Combine all transformation components into line_processor function per PRD §4.2 +- **Priority:** P0 +- **Dependencies:** P2-T4, P3-T7, P3-T8, P3-T9 +- **Parallelizable:** no +- **Outputs/Artifacts:** + - `process_response_line()` function + - `main()` entry point in `src/mcpbridge_wrapper/__main__.py` +- **Acceptance Criteria:** End-to-end: stdin → bridge → transform → stdout; all PRD test cases pass + +--- + +### Phase 4: Edge Case Handling + +#### ✅ P4-T1: Handle Empty Content Array +- **Description:** Pass through responses with `"content": []` without modification per PRD §5.1 +- **Priority:** P1 +- **Dependencies:** P3-T3 +- **Parallelizable:** yes +- **Outputs/Artifacts:** + - Empty list check in `needs_transformation()` +- **Acceptance Criteria:** `{"result": {"content": []}}` is passed through unchanged + +#### ✅ P4-T2: Handle Content with No Text Items +- **Description:** Pass through responses with only image or non-text content types per PRD §5.2 EC3 +- **Priority:** P1 +- **Dependencies:** P3-T4 +- **Parallelizable:** yes +- **Outputs/Artifacts:** + - None-found handling in `extract_text_content()` +- **Acceptance Criteria:** `[{"type": "image", "url": "..."}]` results in no transformation + +#### ✅ P4-T3: Handle Already Compliant Responses +- **Description:** Pass through responses that already have `structuredContent` field per PRD §5.2 EC2 +- **Priority:** P1 +- **Dependencies:** P3-T3 +- **Parallelizable:** yes +- **Outputs/Artifacts:** + - Presence check in `needs_transformation()` +- **Acceptance Criteria:** `{"structuredContent": {...}}` responses are not modified + +#### ✅ P4-T4: Handle Responses Without Result Field +- **Description:** Pass through JSON objects that don't have a `result` key (notifications, errors) +- **Priority:** P1 +- **Dependencies:** P3-T3 +- **Parallelizable:** yes +- **Outputs/Artifacts:** + - Field existence check in `needs_transformation()` +- **Acceptance Criteria:** `{"id": 1, "error": null}` is passed through unchanged + +#### ✅ P4-T5: Handle Bridge Process Crash +- **Description:** Detect bridge process termination and exit with same exit code per PRD §5.1 +- **Priority:** P1 +- **Dependencies:** P2-T6 +- **Parallelizable:** yes +- **Outputs/Artifacts:** + - Exit code propagation in main loop +- **Acceptance Criteria:** When mcpbridge exits with code 1, wrapper also exits with code 1 + +#### ✅ P4-T6: Handle Client Disconnect +- **Description:** Cleanly shutdown when stdin closes (client disconnects) per PRD §5.1 +- **Priority:** P1 +- **Dependencies:** P2-T2 +- **Parallelizable:** yes +- **Outputs/Artifacts:** + - EOF detection in stdin forwarding loop (already implemented) + - Validation report confirming graceful shutdown +- **Acceptance Criteria:** Wrapper terminates gracefully when stdin pipe is closed + +#### ✅ P4-T7: Handle Malformed JSON from Bridge +- **Description:** Pass through unparseable JSON lines unchanged per PRD §5.1 +- **Priority:** P1 +- **Dependencies:** P3-T2 +- **Parallelizable:** yes +- **Outputs/Artifacts:** + - Exception handling returning original line +- **Acceptance Criteria:** Partial JSON `{"broken` is output exactly as received + +#### ✅ P4-T8: Handle Nested JSON String Content +- **Description:** Correctly handle text content that is a valid JSON string primitive per PRD §5.2 EC4 +- **Priority:** P2 +- **Dependencies:** P3-T5 +- **Parallelizable:** yes +- **Outputs/Artifacts:** + - Test case in test suite +- **Acceptance Criteria:** Text `"plain string"` becomes `structuredContent: "plain string"` (not error) + +#### ✅ P4-T9: Handle Very Large JSON Responses +- **Description:** Ensure memory-efficient processing for large JSON payloads (>1MB) +- **Priority:** P2 +- **Dependencies:** P2-T3 +- **Parallelizable:** yes +- **Outputs/Artifacts:** + - Line-based processing (no buffering entire response) +- **Acceptance Criteria:** Can process 10MB JSON line without MemoryError; memory stays <10MB + +--- + +### Phase 5: Testing & Verification + +#### ✅ P5-T1: Create Unit Test Framework +- **Description:** Set up pytest structure with fixtures for common test data +- **Priority:** P0 +- **Dependencies:** P1-T4 +- **Parallelizable:** no +- **Outputs/Artifacts:** + - `tests/unit/__init__.py`, `conftest.py` with fixtures +- **Acceptance Criteria:** `pytest tests/unit` runs without import errors +- **Status:** COMPLETED (2026-02-08) + +#### ✅ P5-T2: Write Test for Valid Transformation (TC1) +- **Description:** Test response with content, no structuredContent gets injected per PRD §7.1 TC1 +- **Priority:** P0 +- **Dependencies:** P3-T7, P5-T1 +- **Parallelizable:** yes +- **Outputs/Artifacts:** + - `tests/unit/test_transform.py::test_valid_transformation` +- **Acceptance Criteria:** Test passes; coverage includes `process_response_line` + +#### ✅ P5-T3: Write Test for Already Compliant Response (TC2) +- **Description:** Test response with both fields remains unmodified per PRD §7.1 TC2 +- **Priority:** P0 +- **Dependencies:** P4-T3, P5-T1 +- **Parallelizable:** yes +- **Outputs/Artifacts:** + - `tests/unit/test_transform.py::test_already_compliant` +- **Acceptance Criteria:** Output JSON equals input JSON exactly + +#### ✅ P5-T4: Write Test for Non-JSON Text Content (TC3) +- **Description:** Test fallback to `{"text": content}` wrapper per PRD §7.1 TC3 +- **Priority:** P0 +- **Dependencies:** P3-T6, P5-T1 +- **Parallelizable:** yes +- **Outputs/Artifacts:** + - `tests/unit/test_transform.py::test_non_json_text_fallback` +- **Acceptance Criteria:** `structuredContent` equals `{"text": "plain text"}` + +#### ✅ P5-T5: Write Test for Non-JSON Line Passthrough (TC4) +- **Description:** Test plain text stdout lines pass through unmodified per PRD §7.1 TC4 +- **Priority:** P0 +- **Dependencies:** P3-T8, P5-T1 +- **Parallelizable:** yes +- **Outputs/Artifacts:** + - `tests/unit/test_transform.py::test_non_json_passthrough` +- **Acceptance Criteria:** Plain text input equals output exactly + +#### ✅ P5-T6: Write Test for Empty Content Array (TC5) +- **Description:** Test `{"content": []}` passes through unmodified per PRD §7.1 TC5 +- **Priority:** P1 +- **Dependencies:** P4-T1, P5-T1 +- **Parallelizable:** yes +- **Outputs/Artifacts:** + - `tests/unit/test_transform.py::test_empty_content` +- **Acceptance Criteria:** No transformation applied to empty content + +#### ✅ P5-T7: Write Test for No Result Field (TC6) +- **Description:** Test non-result JSON passes through per PRD §7.1 TC6 +- **Priority:** P1 +- **Dependencies:** P4-T4, P5-T1 +- **Parallelizable:** yes +- **Outputs/Artifacts:** + - `tests/unit/test_transform.py::test_no_result_field` +- **Acceptance Criteria:** Notifications/error objects unchanged + +#### ✅ P5-T8: Write Test for Mixed Content Types (EC1) +- **Description:** Test image + text content array extracts first text per PRD §5.2 EC1 +- **Priority:** P1 +- **Dependencies:** P3-T4, P5-T1 +- **Parallelizable:** yes +- **Outputs/Artifacts:** + - `tests/unit/test_transform.py::test_mixed_content` +- **Acceptance Criteria:** First text item extracted despite preceding image + +#### ✅ P5-T9: Write Test for Nested JSON String (EC4) +- **Description:** Test `"plain string"` becomes valid structuredContent per PRD §5.2 EC4 +- **Priority:** P2 +- **Dependencies:** P4-T8, P5-T1 +- **Parallelizable:** yes +- **Outputs/Artifacts:** + - `tests/unit/test_transform.py::test_json_string_primitive` +- **Acceptance Criteria:** String primitive preserved in structuredContent + +#### ✅ P5-T10: Create Integration Test with Mock Bridge +- **Description:** Create mock mcpbridge process for end-to-end testing +- **Priority:** P0 +- **Dependencies:** P2-T1, P3-T10, P5-T1 +- **Parallelizable:** no +- **Outputs/Artifacts:** + - `tests/integration/test_e2e.py` + - Mock bridge fixture that outputs canned responses +- **Acceptance Criteria:** Full stdin→transform→stdout cycle verified + +#### ✅ P5-T11: Implement Performance Benchmark +- **Description:** Time 1000 transformations to verify <5ms overhead per PRD §3.1 NFR1 +- **Priority:** P1 +- **Dependencies:** P3-T10, P5-T10 +- **Parallelizable:** yes +- **Outputs/Artifacts:** + - `tests/integration/test_performance.py` + - Benchmark report with average latency +- **Acceptance Criteria:** Average overhead <5ms; documented in test output + +#### ✅ P5-T12: Test with Real Xcode mcpbridge (Manual) +- **Description:** Manual integration test with actual Xcode 26.3+ running +- **Priority:** P0 +- **Dependencies:** P3-T10 +- **Parallelizable:** no +- **Outputs/Artifacts:** + - Test results document +- **Acceptance Criteria:** No errors during 5-minute continuous operation + +#### ✅ P5-T13: Verify All 20 Xcode MCP Tools (IT1-IT4) +- **Description:** Test each of the 20 tools listed in PRD §3.1 tool list +- **Priority:** P0 +- **Dependencies:** P5-T12 +- **Parallelizable:** no +- **Outputs/Artifacts:** + - Integration test suite covering all tools +- **Acceptance Criteria:** Each tool returns valid structuredContent without -32600 errors + +#### ✅ P5-T14: Achieve 90%+ Code Coverage +- **Description:** Run coverage report and fill gaps to reach 90% line coverage +- **Priority:** P1 +- **Dependencies:** P5-T2 through P5-T11 +- **Parallelizable:** no +- **Outputs/Artifacts:** + - Coverage report HTML + - Missing coverage addressed +- **Acceptance Criteria:** `pytest --cov` shows ≥90% coverage + +--- + +### Phase 6: Packaging & Distribution + +#### ✅ P6-T1: Create Standalone Executable Script +- **Description:** Create single-file `mcpbridge-wrapper` script suitable for `~/bin` installation +- **Priority:** P0 +- **Dependencies:** P3-T10 +- **Parallelizable:** no +- **Outputs/Artifacts:** + - `src/mcpbridge_wrapper/cli.py` or standalone `mcpbridge-wrapper` +- **Acceptance Criteria:** File runs directly: `./mcpbridge-wrapper`; all imports self-contained + +#### ✅ P6-T2: Add Executable Shebang and Permissions +- **Description:** Add `#!/usr/bin/env python3` and ensure file is executable per PRD §3.4 +- **Priority:** P0 +- **Dependencies:** P6-T1 +- **Parallelizable:** no +- **Outputs/Artifacts:** + - Executable bit set on script file +- **Acceptance Criteria:** `ls -l` shows `x` permission; runs without `python` prefix + +#### ✅ P6-T3: Create Installation Script +- **Description:** Create shell script that installs to `~/bin/mcpbridge-wrapper` +- **Priority:** P1 +- **Dependencies:** P6-T2 +- **Parallelizable:** no +- **Outputs/Artifacts:** + - `scripts/install.sh` +- **Acceptance Criteria:** Running `scripts/install.sh` creates `~/bin/mcpbridge-wrapper`; script is executable + +#### ✅ P6-T4: Create Cursor MCP Configuration Template +- **Description:** Create `~/.cursor/mcp.json` configuration example per PRD §6.1 +- **Priority:** P0 +- **Dependencies:** P6-T1 +- **Parallelizable:** yes +- **Outputs/Artifacts:** + - `config/cursor-mcp.json` example file + - Documentation snippet +- **Acceptance Criteria:** JSON is valid; path uses `$HOME` or documents username replacement + +#### ✅ P6-T5: Create Claude Code Configuration Template +- **Description:** Document `claude mcp add` command for Claude Code per PRD §3.4 +- **Priority:** P2 +- **Dependencies:** P6-T1 +- **Parallelizable:** yes +- **Outputs/Artifacts:** + - Configuration snippet in docs +- **Acceptance Criteria:** Command `claude mcp add --transport stdio xcode -- /Users/$USER/bin/mcpbridge-wrapper` is documented + +#### ✅ P6-T6: Create Codex CLI Configuration Template +- **Description:** Document `codex mcp add` command for Codex CLI per PRD §3.4 +- **Priority:** P2 +- **Dependencies:** P6-T1 +- **Parallelizable:** yes +- **Outputs/Artifacts:** + - Configuration snippet in docs +- **Acceptance Criteria:** Command `codex mcp add xcode -- /Users/$USER/bin/mcpbridge-wrapper` is documented + +#### ✅ P6-T7: Configure pip Installable Package +- **Description:** Ensure `pip install` creates executable entry point +- **Priority:** P2 +- **Dependencies:** P1-T2, P6-T1 +- **Parallelizable:** yes +- **Outputs/Artifacts:** + - `[project.scripts]` entry in pyproject.toml +- **Acceptance Criteria:** After `pip install`, `mcpbridge-wrapper` command is available in PATH + +#### ✅ P6-T8: Create GitHub Release Workflow +- **Description:** GitHub Actions workflow to create releases with attached artifacts +- **Priority:** P3 +- **Dependencies:** P1-T8, P6-T1 +- **Parallelizable:** yes +- **Outputs/Artifacts:** + - `.github/workflows/release.yml` +- **Acceptance Criteria:** Pushing tag creates release with downloadable script + +#### ✅ P6-T9: Create Uninstall Script +- **Description:** Create uninstall script to remove mcpbridge-wrapper from ~/bin and pip +- **Priority:** P2 +- **Dependencies:** P6-T3 +- **Parallelizable:** yes +- **Outputs/Artifacts:** + - `scripts/uninstall.sh` - Removes wrapper script from ~/bin, uninstalls pip package, optionally cleans config +- **Acceptance Criteria:** + - Running `scripts/uninstall.sh` removes `~/bin/mcpbridge-wrapper` + - pip uninstalls the mcpbridge-wrapper package + - Script has dry-run mode and confirmation prompts + +#### ✅ P6-T10: Create GitHub CI Workflow +- **Description:** Create GitHub Actions workflow for continuous integration that checks project state: build, tests, lint, typecheck +- **Priority:** P1 +- **Dependencies:** P1-T2, P1-T3, P1-T4 +- **Parallelizable:** yes +- **Outputs/Artifacts:** + - `.github/workflows/ci.yml` +- **Acceptance Criteria:** + - Workflow triggers on push/PR to main + - Runs lint (ruff check) + - Runs format check (ruff format --check) + - Runs type check (mypy) + - Runs tests with pytest across Python 3.9-3.12 + - Builds package and validates with twine + - All checks must pass + +--- + +### Phase 7: Documentation + +#### ✅ P7-T1: Write Installation Guide Section +- **Description:** Document step-by-step installation for end users per PRD §4.1 +- **Priority:** P0 +- **Dependencies:** P6-T3 +- **Parallelizable:** no +- **Outputs/Artifacts:** + - `docs/installation.md` or README section +- **Acceptance Criteria:** New user can follow instructions without external help + +#### ✅ P7-T2: Write Cursor Configuration Guide +- **Description:** Document GUI and JSON configuration for Cursor per PRD §4.1 +- **Priority:** P0 +- **Dependencies:** P6-T4 +- **Parallelizable:** yes +- **Outputs/Artifacts:** + - `docs/cursor-setup.md` +- **Acceptance Criteria:** Cursor successfully loads xcode-tools after following guide + +#### ✅ P7-T3: Write Claude Code Configuration Guide +- **Description:** Document one-liner setup for Claude Code +- **Priority:** P1 +- **Dependencies:** P6-T5 +- **Parallelizable:** yes +- **Outputs/Artifacts:** + - `docs/claude-setup.md` or combined doc +- **Acceptance Criteria:** `claude mcp list` shows xcode-tools after setup + +#### ✅ P7-T4: Write Codex CLI Configuration Guide +- **Description:** Document one-liner setup for Codex CLI +- **Priority:** P1 +- **Dependencies:** P6-T6 +- **Parallelizable:** yes +- **Outputs/Artifacts:** + - `docs/codex-setup.md` or combined doc +- **Acceptance Criteria:** `codex mcp list` shows xcode-tools after setup + +#### ✅ P7-T5: Document Environment Variables +- **Description:** Document `MCP_XCODE_PID` and `MCP_XCODE_SESSION_ID` per PRD §6.2 +- **Priority:** P1 +- **Dependencies:** P2-T1 +- **Parallelizable:** yes +- **Outputs/Artifacts:** + - Environment variables table in documentation +- **Acceptance Criteria:** User understands when and how to use optional environment variables + +#### ✅ P7-T6: Write Troubleshooting Guide +- **Description:** Document common errors and solutions per PRD §4.3 +- **Priority:** P1 +- **Dependencies:** P4-T1 through P4-T9 +- **Parallelizable:** yes +- **Outputs/Artifacts:** + - `docs/troubleshooting.md` covering -32600 error and others +- **Acceptance Criteria:** Each error has symptom, cause, and solution clearly documented + +#### ✅ P7-T7: Document the 20 Xcode MCP Tools +- **Description:** List and briefly describe all available tools per PRD tool list +- **Priority:** P2 +- **Dependencies:** P5-T13 +- **Parallelizable:** yes +- **Outputs/Artifacts:** + - Tool reference table in documentation +- **Acceptance Criteria:** All 20 tools documented with name and description + +#### ✅ P7-T8: Write Architecture Overview +- **Description:** Document how the wrapper works internally for contributors +- **Priority:** P2 +- **Dependencies:** P3-T10 +- **Parallelizable:** yes +- **Outputs/Artifacts:** + - `docs/architecture.md` with diagrams +- **Acceptance Criteria:** Reader understands data flow from stdin to stdout + +#### ✅ P7-T9: Create Usage Examples +- **Description:** Document sample workflows (build, test, read file) +- **Priority:** P2 +- **Dependencies:** P5-T13 +- **Parallelizable:** yes +- **Outputs/Artifacts:** + - Example commands in documentation +- **Acceptance Criteria:** 3+ practical examples with expected output + +#### ✅ P7-T10: Write Final README +- **Description:** Complete README with all essential information +- **Priority:** P0 +- **Dependencies:** P7-T1, P7-T2, P7-T6 +- **Parallelizable:** no +- **Outputs/Artifacts:** + - `README.md` at project root +- **Acceptance Criteria:** README includes: what, why, install, configure, usage, troubleshoot + +#### ✅ P7-T11: Create CHANGELOG +- **Description:** Document version history and changes +- **Priority:** P2 +- **Dependencies:** P7-T10 +- **Parallelizable:** yes +- **Outputs/Artifacts:** + - `CHANGELOG.md` with initial 1.0.0 entry +- **Acceptance Criteria:** Follows Keep a Changelog format; all phases summarized + +#### ✅ P7-T12: Move Cursor IDE uvx settings before installation instructions in README +- **Description:** Reorder the README.md so that the Cursor IDE uvx configuration snippet (currently under Configuration > Cursor > "Using uvx (Recommended)") appears before the Installation section. This gives Cursor users the fastest path to getting started — they only need to paste the JSON block into `~/.cursor/mcp.json` and they're done, without scrolling through five installation options first. Include both the basic uvx snippet and the uvx-with-Web-UI variant (`--web-ui`, `--web-ui-port 8080`) so users can choose either option up front. +- **Priority:** P1 +- **Dependencies:** P7-T10 +- **Parallelizable:** yes +- **Outputs/Artifacts:** + - Updated `README.md` with reordered sections +- **Acceptance Criteria:** + - The Cursor uvx `mcp.json` snippet (basic) is visible in the README before the "Installation" heading + - The Cursor uvx-with-Web-UI `mcp.json` snippet (`--web-ui`, `--web-ui-port 8080`) is also shown alongside the basic snippet + - All other README content (installation options, other client configs, usage, etc.) remains intact and in a logical order + - No broken markdown links or formatting issues + +--- + +### Phase 8: Documentation Publishing + +**Intent:** Set up automated documentation generation and publishing using Apple DocC for hosting on GitHub Pages. + +#### ✅ P8-T1: Support Apple DocC for documentation and publishing on soundblaster.github.io Pages +- **Description:** Configure Apple DocC to generate documentation and publish to GitHub Pages at soundblaster.github.io/XcodeMCPWrapper (superseding the original `/mcpbridge-wrapper` path target). +- **Priority:** P2 +- **Dependencies:** P7-T10 +- **Parallelizable:** yes +- **Outputs/Artifacts:** + - DocC documentation catalog (`.docc`) + - GitHub Actions workflow for automated publishing (`.github/workflows/docs.yml`) + - Published docs at `soundblaster.github.io/XcodeMCPWrapper` +- **Acceptance Criteria:** + - DocC builds documentation without errors + - GitHub Pages site is live at `soundblaster.github.io/XcodeMCPWrapper/` + - Documentation updates automatically on pushes to main + +#### ✅ P8-T2: Restructure DocC to Canonical Swift Package Format +- **Description:** Move DocC catalog from root-level `mcpbridge-wrapper.docc/` to canonical Swift Package Manager structure under `Sources/XcodeMCPWrapper/Documentation.docc/` +- **Priority:** P2 +- **Dependencies:** P8-T1 +- **Parallelizable:** yes +- **Outputs/Artifacts:** + - New directory: `Sources/XcodeMCPWrapper/Documentation.docc/` + - Main DocC file: `Sources/XcodeMCPWrapper/Documentation.docc/XcodeMCPWrapper.md` + - All existing DocC articles moved to new location + - Updated GitHub Actions workflow with correct paths +- **Acceptance Criteria:** + - DocC catalog follows Apple's canonical SPM structure + - GitHub Actions workflow builds from new location + - All existing documentation content preserved + - GitHub Pages deployment still works correctly + - Old `mcpbridge-wrapper.docc/` directory removed +- **Canonical Structure:** + ``` + Sources/ + XcodeMCPWrapper/ + Documentation.docc/ + XcodeMCPWrapper.md # Main landing page + GettingStarted.md + Installation.md + Configuration.md + CursorSetup.md + ClaudeCodeSetup.md + CodexCLISetup.md + Troubleshooting.md + Architecture.md + EnvironmentVariables.md + ``` +- **Reference Implementation:** + ```yaml + name: Deploy DocC Documentation + on: + push: + branches: [main] + pull_request: + branches: [main] + workflow_dispatch: + permissions: + contents: read + pages: write + id-token: write + concurrency: + group: "pages" + cancel-in-progress: false + jobs: + build: + runs-on: macos-14 + steps: + - uses: actions/checkout@v4 + - uses: maxim-lobanov/setup-xcode@v1 + with: + xcode-version: latest-stable + - name: Build Documentation + run: | + swift package --allow-writing-to-directory ./docs \ + generate-documentation \ + --target mcpbridge-wrapper \ + --output-path ./docs \ + --transform-for-static-hosting \ + --hosting-base-path mcpbridge-wrapper + - name: Add .nojekyll and index.html redirect + run: | + touch docs/.nojekyll + cat > docs/index.html << 'EOF' + + + + + Redirecting to mcpbridge-wrapper Documentation + + + + +

Redirecting to mcpbridge-wrapper Documentation...

+ + + + EOF + - uses: actions/upload-pages-artifact@v3 + if: github.event_name == 'push' + with: + path: "./docs" + deploy: + if: github.event_name == 'push' && github.ref == 'refs/heads/main' + needs: build + runs-on: ubuntu-latest + environment: + name: github-pages + url: ${{ steps.deployment.outputs.page_url }} + steps: + - id: deployment + uses: actions/deploy-pages@v4 + ``` + +#### ✅ P8-T3: Change Deployment Path to xcodemcpwrapper +- **Description:** Update all public-facing documentation, scripts, and configuration templates to use the new deployment path `/Users/YOUR_USERNAME/bin/xcodemcpwrapper` instead of `/Users/YOUR_USERNAME/bin/mcpbridge-wrapper`. The Python package name (`mcpbridge_wrapper`) remains unchanged - only the deployed executable name changes. +- **Priority:** P1 +- **Dependencies:** none +- **Parallelizable:** yes +- **Outputs/Artifacts:** + - Updated `scripts/install.sh` - Creates `~/bin/xcodemcpwrapper` instead of `~/bin/mcpbridge-wrapper` + - Updated `scripts/uninstall.sh` - Removes `~/bin/xcodemcpwrapper` + - Updated `config/cursor-mcp.json` - New path in JSON template + - Updated `config/claude-code.txt` - New path in command examples + - Updated `config/codex-cli.txt` - New path in command examples + - Updated `config/zed-agent.json` - New path in JSON template + - Updated `README.md` - All path references + - Updated `AGENTS.md` - Configuration examples + - Updated `CONTRIBUTING.md` - Development references + - Updated `docs/*.md` - All documentation files + - Updated `Sources/XcodeMCPWrapper/Documentation.docc/*.md` - DocC documentation +- **Acceptance Criteria:** + - All public docs show `xcodemcpwrapper` as the executable name + - Installation script creates `~/bin/xcodemcpwrapper` + - Configuration templates use new path + - No references to `~/bin/mcpbridge-wrapper` remain in active documentation + - Historical archives (SPECS/ARCHIVE/) are NOT modified + - Python source code and package names remain unchanged + - All tests pass after changes + +Phase 8 Follow-up Backlog +- [x] FU-P8-T1-1: Reconcile P8-T1 URL criteria with current GitHub Pages path and resolve DocC reference warnings (P2) +- [x] FU-P6-T10-1: Align manual install script with Web UI configuration expectations (P1) + +#### ✅ FU-P8-T1-1: Reconcile P8-T1 URL criteria with current GitHub Pages path and resolve DocC reference warnings +- **Description:** Review follow-up to align Phase 8 tracking artifacts with the live documentation URL `soundblaster.github.io/XcodeMCPWrapper/` and remove remaining DocC ambiguity warnings in the Phase 8 documentation index. +- **Priority:** P2 +- **Dependencies:** P8-T3 +- **Parallelizable:** yes +- **Outputs/Artifacts:** + - Updated `SPECS/Workplan.md` P8-T1 references to match current GitHub Pages URL + - Updated Phase 8 validation/review artifacts with an explicit supersession note where applicable + - Updated `Sources/XcodeMCPWrapper/Documentation.docc/XcodeMCPWrapper.md` DocC links to avoid ambiguous references +- **Acceptance Criteria:** + - Workplan Phase 8 no longer references the legacy `/mcpbridge-wrapper` GitHub Pages URL as the active deployment URL + - Phase 8 review/validation artifacts clearly document that the active URL is `soundblaster.github.io/XcodeMCPWrapper/` + - `swift package generate-documentation --target XcodeMCPWrapper` completes without `Architecture` ambiguity warnings + +#### ✅ FU-P6-T10-1: Align manual install script with Web UI configuration expectations +- **Description:** Fix the mismatch where `scripts/install.sh` installs only the base package (`pip install -e .`) while docs/config examples allow `--web-ui` usage from `~/bin/xcodemcpwrapper`. This causes runtime failure when users enable Web UI without optional dependencies. Add an explicit installer mode for Web UI extras and update documentation to make the dependency requirement unambiguous. +- **Priority:** P1 +- **Dependencies:** P6-T3, P10-T1 +- **Parallelizable:** yes +- **Outputs/Artifacts:** + - Updated `scripts/install.sh` with Web UI-aware install option (e.g., `--webui`) that installs `-e ".[webui]"` + - Updated `README.md` and `docs/installation.md` with clear mapping: + - base install => no `--web-ui` args + - webui install => `--web-ui` args supported + - Updated `docs/troubleshooting.md` to include this specific symptom/cause/fix path +- **Acceptance Criteria:** + - Running `./scripts/install.sh` (default) keeps current base behavior and works with no Web UI args + - Running `./scripts/install.sh --webui` installs required Web UI dependencies (`fastapi`, `uvicorn`, etc.) + - `~/bin/xcodemcpwrapper --web-ui --web-ui-port 8080 --help` no longer fails after Web UI install mode + - Zed/Cursor configs that include `--web-ui` work when installer is run with Web UI mode + - Documentation examples do not imply Web UI works on base-only install + +--- + +## Known Issues / Bug Tracker + +### BUG-T0: Uptime widget on Web UI always shows 1h 0m 0s ✅ +- **Type:** Bug / Feature Issue +- **Status:** ✅ Complete +- **Priority:** P2 +- **Discovered:** 2026-02-13 +- **Component:** Web UI Dashboard +- **Affected:** Web UI uptime widget display + +#### Description +The uptime widget on the Web UI dashboard always displays a fixed value of "1h 0m 0s" instead of showing the actual runtime uptime of the XcodeMCPWrapper process. + +#### Symptoms +``` +Uptime widget shows: 1h 0m 0s +Expected: Dynamic uptime that increases over time (e.g., 1h 5m 23s after 5 minutes 23 seconds) +``` + +#### Root Cause Analysis +[To be diagnosed during implementation] + +#### Workaround +The uptime counter in the metrics sidebar may show accurate counts for requests/errors as an alternative indicator of runtime. + +#### Resolution Path +- [ ] Investigate Web UI metrics collection and uptime calculation +- [ ] Check if uptime is being calculated as fixed value instead of elapsed time +- [ ] Implement dynamic uptime display +- [ ] Add tests for uptime widget accuracy over time +- [ ] Verify dashboard updates correctly + +--- + +### BUG-T1: Kimi CLI MCP Connection Failure +- **Type:** Bug / Compatibility Issue +- **Status:** 🔴 Open - Client-Side Issue +- **Priority:** P2 +- **Discovered:** 2026-02-08 +- **Component:** Client Compatibility +- **Affected Client:** Kimi CLI v1.9.0 +- **Working Clients:** Cursor, Zed Agent, Claude Code, Codex CLI + +#### Description +Kimi CLI (v1.9.0) fails to maintain a stable stdio MCP connection with `xcodemcpwrapper`. The connection initializes successfully but is immediately closed with error: `Server session was closed unexpectedly`. + +#### Symptoms +``` +Error running tool: Client failed to connect: Server session was closed unexpectedly +``` + +#### Verification Results +| Test | Method | Result | +|------|--------|--------| +| Direct wrapper test | `echo '{"jsonrpc":"2.0",...}' \| xcodemcpwrapper` | ✅ Pass | +| Zed Agent integration | `XcodeListWindows` | ✅ Pass | +| Kimi CLI integration | `XcodeListWindows` | ❌ Fail | + +#### Root Cause Analysis +The wrapper correctly implements MCP protocol spec and responds properly: +```json +{"id":1,"jsonrpc":"2.0","result":{"capabilities":{"tools":{"listChanged":true}},"protocolVersion":"2024-11-05","serverInfo":{"name":"xcode-tools","version":"24571"}}} +``` + +The issue appears to be in Kimi CLI's stdio MCP transport/session management, not the wrapper itself. + +#### Workaround +Use alternative MCP clients that work correctly: +- ✅ **Zed Agent** - Tested and verified working +- ✅ **Cursor** - Primary target client, fully supported +- ✅ **Claude Code** - Documented and tested +- ✅ **Codex CLI** - Documented and tested + +#### Resolution Path +- [ ] Report issue to Kimi CLI development team +- [ ] Monitor Kimi CLI updates for MCP transport fixes +- [ ] Document limitation in troubleshooting guide +- [ ] Re-test when Kimi CLI v1.10.0+ is released + +#### References +- Kimi CLI version tested: 1.9.0 +- Wrapper version tested: 0.1.7 +- Related: P5-T13 (Tool verification across clients) + +--- + +### BUG-T2: `codex mcp add` with Web UI extras fails in `zsh` ✅ +- **Type:** Bug / Documentation / CLI UX +- **Status:** ✅ Complete +- **Priority:** P1 +- **Discovered:** 2026-02-14 +- **Completed:** 2026-02-14 +- **Component:** Installation & Configuration Docs +- **Affected Client:** Codex CLI (shell command setup) +- **Affected Shell:** `zsh` (default macOS shell) + +#### Description +The documented/pasted Web UI setup command for Codex using uvx extras fails in `zsh` because square brackets are treated as a glob pattern when unquoted. + +#### Symptoms +```zsh +egor@MacBook-Pro-Egor bin % codex mcp add xcode -- uvx --from mcpbridge-wrapper[webui] mcpbridge-wrapper --web-ui --web-ui-port 8080 +zsh: no matches found: mcpbridge-wrapper[webui] +``` + +#### Root Cause Analysis +`zsh` performs filename globbing on unquoted bracket expressions like `[webui]`. The token `mcpbridge-wrapper[webui]` is parsed as a glob instead of a literal package specifier. + +#### Workaround +Use any of the following forms so the extras spec is passed literally: +- `uvx --from 'mcpbridge-wrapper[webui]' mcpbridge-wrapper --web-ui --web-ui-port 8080` +- `uvx --from mcpbridge-wrapper\\[webui\\] mcpbridge-wrapper --web-ui --web-ui-port 8080` + +#### Resolution Path +- [x] Update README and setup docs to quote extras in all shell commands (`'mcpbridge-wrapper[webui]'`) +- [x] Update configuration templates/examples that include extras to avoid raw unquoted bracket syntax +- [x] Add a troubleshooting entry for `zsh: no matches found` with shell-safe examples +- [x] Add a short note explaining why quotes are required in `zsh` + +--- + +### BUG-T3: Web UI cannot stay available when MCP bridge initialization fails ✅ +- **Type:** Bug / Web UI / MCP Lifecycle +- **Status:** ✅ Complete +- **Priority:** P1 +- **Discovered:** 2026-02-14 +- **Completed:** 2026-02-14 +- **Component:** CLI startup and Web UI runtime +- **Affected Client:** Codex App / MCP users enabling `--web-ui` +- **Affected Surface:** Local dashboard (`http://localhost:8080`) + +#### Description +When users start the wrapper with `--web-ui` in MCP mode and the Xcode bridge handshake fails (or the MCP client disconnects early), the wrapper process exits before the dashboard remains usable. Users then see browser errors such as "Safari can't connect to the server" while trying to debug MCP connectivity. + +#### Symptoms +```text +MCP startup failed: handshaking with MCP server failed: connection closed: initialize response +Safari can't connect to the server +``` + +#### Root Cause Analysis +Web UI lifecycle is tightly coupled to MCP bridge lifecycle. If bridge startup/session fails, process shutdown also stops the Web UI, leaving no standalone dashboard mode for diagnosis. + +#### Workaround +Use standalone mode while diagnosing bridge issues: +- `xcodemcpwrapper --web-ui-only --web-ui-port 8080` +- `uvx --from 'mcpbridge-wrapper[webui]' mcpbridge-wrapper --web-ui-only --web-ui-port 8080` + +#### Resolution Path +- [x] Add a standalone `--web-ui-only` mode that starts dashboard services without launching `xcrun mcpbridge` +- [x] Ensure `--web-ui-only` implies Web UI enabled and respects `--web-ui-port` and config options +- [x] Add unit tests for arg parsing and main startup behavior in web-ui-only mode +- [x] Document when to use standalone dashboard mode for MCP connection troubleshooting + +--- + +### BUG-T4: Repeated Xcode permission prompts for each short-lived MCP client process +- **Type:** Bug / UX / Client Integration +- **Status:** 🔴 Open +- **Priority:** P1 +- **Discovered:** 2026-02-14 +- **Component:** MCP process lifecycle / Xcode authorization boundary +- **Affected Clients:** Codex CLI/App, other stdio MCP clients launching fresh processes +- **Affected Surface:** Xcode "agent wants to use Xcode's tools" prompt frequency + +#### Description +When MCP clients run in short-lived sessions, each session starts a new wrapper process (new PID). Xcode may request authorization again for each new process, creating repeated prompts and friction during normal usage. + +#### Symptoms +```text +The agent "Codex" ... PID 29854 wants to use Xcode's tools... +The agent "Codex" ... PID 30421 wants to use Xcode's tools... +``` + +#### Root Cause Analysis +Current integration is per-client `stdio`: each client launch creates a separate process and upstream bridge lifecycle. There is no persistent shared broker session to Xcode across clients. + +#### Workaround +Keep a single long-lived client/session running to reduce process churn. This is operationally fragile and does not solve multi-client workflows. + +#### Resolution Path +- [x] Design persistent broker architecture for shared upstream Xcode session (P13-T1) +- [x] Implement long-lived broker daemon with single upstream bridge connection (P13-T2) +- [x] Add multi-client transport + stdio proxy mode to reuse broker session (P13-T3, P13-T4) +- [ ] Validate reduced prompt behavior and document rollout/migration steps (P13-T5, P13-T6) — P13-T5 resolved to FAIL in FU-P13-T14 due broker UID verification rejection (`-32003`); broker credential fallback shipped in FU-P13-T15, prompt behavior now needs re-validation + +--- + +### BUG-T5: Empty-content tool results can still violate strict `structuredContent` contract ✅ +- **Type:** Bug / MCP Protocol Compliance +- **Status:** ✅ Resolved (2026-02-14) +- **Priority:** P0 +- **Discovered:** 2026-02-14 +- **Component:** Response transformation engine +- **Affected Clients:** Strict MCP clients (Codex App/Cursor class behavior) + +#### Description +Some tool responses with `result.content: []` are currently passed through without adding `result.structuredContent`. For tools declaring output schema, strict clients may reject this as protocol-invalid. + +#### Symptoms +```text +Tool has output schema but did not return structured content +``` + +#### Root Cause Analysis +`needs_transformation()` intentionally skips empty content arrays, which can leave schema-required `structuredContent` absent for strict client validation paths. + +#### Workaround +Use clients/builds with compatibility fallback behavior. This is not reliable for strict validation paths. + +#### Resolution Path +- [x] Implement FU-P13-T7 +- [ ] Add strict empty-content regression tests +- [ ] Verify behavior in Codex App and Codex CLI with same wrapper binary + +--- + +### ✅ BUG-T6: Web UI port collisions (`--web-ui-port`) create unstable multi-process behavior +- **Type:** Bug / Runtime / Process Lifecycle +- **Status:** ✅ Done (2026-02-14, PASS) +- **Priority:** P0 +- **Discovered:** 2026-02-14 +- **Component:** CLI startup + Web UI runtime +- **Affected Surface:** Local dashboard and MCP startup reliability + +#### Description +Multiple stale/orphan wrapper instances can compete for the same Web UI port (for example `8080`), producing repeated bind failures and noisy startup state that complicates MCP diagnostics. + +#### Symptoms +```text +ERROR: [Errno 48] error while attempting to bind on address ('127.0.0.1', 8080): address already in use +``` + +#### Root Cause Analysis +Current startup does not enforce a single active Web UI instance per port nor provide deterministic collision recovery behavior. + +#### Workaround +Manually kill stale wrapper/uvx processes or use unique `--web-ui-port` values per client. + +#### Resolution Path +- [x] Implement FU-P13-T8 +- [x] Add deterministic collision handling tests +- [x] Document stale-process cleanup in troubleshooting (done in FU-BUG-T6-1) + +--- + +### ✅ BUG-T7: Unsupported `resources/*` methods can return non-standard error shape +- **Type:** Bug / MCP Compatibility / Error Normalization +- **Status:** ✅ Fixed (2026-02-14) +- **Priority:** P0 +- **Discovered:** 2026-02-14 +- **Component:** Response normalization for non-tool methods +- **Affected Clients:** Clients expecting strict JSON-RPC error envelopes + +#### Description +For unsupported methods like `resources/list` and `resources/templates/list`, upstream may return tool-style `result.isError/content` payloads instead of JSON-RPC `error`. Some clients classify this as unexpected response type. + +#### Symptoms +```text +resources/list failed: Unexpected response type +resources/templates/list failed: Unexpected response type +``` + +#### Root Cause Analysis +Wrapper currently focuses on tool result `structuredContent` transformation and does not normalize unsupported non-tool method failures into canonical JSON-RPC `error` responses. + +#### Workaround +Ignore resource-listing failures when tool calls still work; behavior remains noisy and client-dependent. + +#### Resolution Path +- [x] Implement FU-P13-T9 +- [x] Add method-aware normalization regression tests +- [ ] Validate strict-client compatibility for `resources/*` probing (manual, future) + +--- + +### ✅ BUG-T8: Audit log dashboard shows only entries from the current process (per-process in-memory storage) +- **Type:** Bug / Web UI / Audit Log +- **Status:** ✅ Fixed (2026-02-15) +- **Priority:** P0 +- **Discovered:** 2026-02-15 +- **Component:** `AuditLogger` (`webui/audit.py`), `__main__.py` +- **Affected Clients:** All clients in multi-process setups (Cursor, Zed) + +#### Description +The audit log dashboard only shows entries recorded by the process that currently serves the web UI. In multi-process setups (e.g. Cursor or Zed spawning a new wrapper process per connection), the web-serving process often handles only the initial `initialize` handshake, while subsequent tool calls arrive in sibling processes that have their own `AuditLogger` instance. Those sibling instances write to the shared JSONL file on disk but their in-memory `_entries` list is never visible to the web server. + +#### Symptoms +- "Per-Tool Latency Statistics" shows all historical tool calls (e.g. `BuildProject: 1`, `XcodeListWindows: 2`, `initialize: 3`) +- "Audit Log" table shows only 1 entry (the `initialize` from the current process) + +#### Root Cause Analysis +`SharedMetricsStore` uses SQLite (`~/.cache/mcpbridge-wrapper/metrics.db`) so all processes write to and read from the same store. `AuditLogger` uses an in-memory `self._entries` list that is reset on each process start. `AuditLogger.__init__` opens the JSONL file in append mode but never reads existing entries back into memory. The dashboard's `/api/audit` endpoint reads only from `self._entries`, so it is blind to entries written by any other process. + +#### Workaround +Export audit logs as JSON/CSV (the JSONL file on disk contains the complete history). + +#### Resolution Path +- [x] `AuditLogger._load_history()` reads existing `audit_*.jsonl` files at startup into `self._entries`, capped at `_max_memory_entries` (10 000), skipping malformed lines. +- [x] Add 4 regression tests in `TestStartupHistoryLoad`. + +#### Related Items +- **BUG-T6** ✅ — Port collision multi-process behavior is the direct precondition: it's what causes one process to own the web UI while others silently skip it, creating the split that makes BUG-T8 observable. +- **P10-T2** ✅ — Fixed the same class of problem for metrics by introducing `SharedMetricsStore`; the pattern established there (SQLite cross-process store) is the reference for Option B of BUG-T8. +- **P12-T1** — Adds `client` column to `SharedMetricsStore` schema; if BUG-T8 adopts Option B (SQLite audit store), P12-T1-style schema additions should be designed together to avoid double migration. +- **Phase 13 (P13-T1 – P13-T6)** — Persistent broker will collapse multiple short-lived wrapper processes into one long-lived connection, which would naturally eliminate the multi-process split that causes BUG-T8; fix should remain lightweight (Option A) rather than duplicating broker-level work. +- **BUG-T4** — Repeated Xcode permission prompts per spawn; same root cause (each client spawn starts a fresh process with no shared state), Phase 13 is the intended long-term fix for both. + +--- + +### ✅ BUG-T9: Orphaned Web UI server process blocks port after MCP client disconnect or config change +- **Type:** Bug / Web UI / Process Lifecycle +- **Status:** ✅ Fixed (2026-02-25, PASS) +- **Priority:** P1 +- **Discovered:** 2026-02-15 +- **Completed:** 2026-02-25 +- **Component:** `__main__.py` (main loop), `server.py` (Web UI thread) +- **Affected Clients:** All clients (Cursor, Claude Code, Codex CLI, Zed) + +#### Description +When an MCP client (e.g. Cursor) disconnects, crashes, or changes its server configuration, the old `mcpbridge-wrapper` process can remain alive indefinitely. The orphaned process keeps its Web UI server bound to the configured port (e.g. 8080), preventing any newly spawned process from starting its own Web UI. The user sees the stale dashboard with no indication that it belongs to a dead session. + +#### Reproduction Steps +1. Configure Cursor with `mcpbridge-wrapper --web-ui --web-ui-port 8080` (uvx or local). +2. Open dashboard at `http://localhost:8080` — works normally. +3. Change Cursor's `mcp.json` to a different command path (e.g. switch from uvx to local .venv). +4. Restart Cursor / reconnect MCP server. +5. Dashboard still shows the **old** process's data. New process silently failed to bind port 8080. + +#### Root Cause Analysis +The main loop in `__main__.py` blocks on `output_queue.get()` waiting for stdout EOF from the `mcpbridge` subprocess. When the MCP client disconnects: +1. The wrapper's **stdin** closes → `stdin_forwarder` daemon thread exits. +2. But `mcpbridge` (child process) may keep running because it hasn't received a shutdown signal. +3. `mcpbridge` stdout stays open → `output_queue.get()` blocks forever. +4. The Web UI daemon thread keeps the server socket alive on port 8080. +5. The process is effectively orphaned — no parent, no stdin, but still holding resources. + +#### Impact +- Users see stale dashboards with outdated metrics and audit data. +- New wrapper instances silently skip Web UI startup due to port conflict (per BUG-T6 handling). +- Manual `kill` or `lsof -ti :8080 | xargs kill` is required to recover. + +#### Resolution Path +- [x] Detect stdin EOF in the forwarder thread and trigger deterministic upstream shutdown. +- [x] Add a bounded termination helper (`SIGTERM` then timeout-backed `SIGKILL` fallback) for stuck upstream processes. +- [x] Wire one-shot stdin-closed callback in `main()` so repeated EOF/error events do not trigger duplicate termination attempts. +- [x] Add automated regression tests for EOF callback invocation and graceful-vs-force shutdown behavior. + +#### Related Items +- **BUG-T6** ✅ — Port collision handling (warns but doesn't fix orphans). +- **FU-BUG-T6-1** ✅ — Documents manual stale-process cleanup; BUG-T9 would make that unnecessary. +- **BUG-T4** — Repeated Xcode permission prompts; same root cause (no shared long-lived process). +- **Phase 13** — Persistent broker would eliminate the orphan problem by design. + +--- + +### BUG-T10: Tool chart colors change on update of tool type count +- **Type:** Bug / Web UI / Data Stability +- **Status:** ✅ Fixed (2026-02-20) +- **Priority:** P1 +- **Discovered:** 2026-02-16 +- **Component:** Web UI Dashboard (`webui/static/`, metrics visualization) +- **Affected Clients:** All clients using Web UI dashboard +- **Affected Surface:** Web UI tool usage charts (bar, pie, timeline visualizations) + +#### Description +The colors of tools on charts change when the count of tool types is updated. For example, a red tool becomes blue when a new tool type is added or removed. This is unstable and misleading behavior that undermines user trust in the dashboard data. Color associations must be stable between user sessions and stable across tool type population changes. + +#### Symptoms +``` +Initial state: BuildProject (red), XcodeListWindows (blue), XcodeExecuteCommand (green) +After adding new tool: +New state: BuildProject (blue), XcodeListWindows (green), XcodeExecuteCommand (red) +User sees: Colors have changed, appears to be different tools or corrupted data +``` + +#### Root Cause Analysis +Chart color assignment likely relies on tool index position in the data array or unsorted dictionary iteration, creating non-deterministic color mapping. When new tools are added/removed, array indices shift, causing existing tools to be assigned different colors. This is compounded by lack of persistent tool color mapping across sessions. + +#### Workaround +Refresh the browser page to reset the color assignment, but this persists only for the current session and does not solve the core issue. + +#### Resolution Path +- [ ] Implement stable tool color mapping using a deterministic function (e.g. hash-based assignment or fixed color palette keyed by tool name) +- [ ] Persist tool-to-color mappings in user configuration or database so colors remain stable across sessions +- [ ] Ensure chart rendering uses the stable color mapping instead of array-index-based assignment +- [ ] Add tests to verify tool colors remain consistent when tool type count changes +- [ ] Add tests to verify tool colors persist across dashboard page reloads +- [ ] Validate user experience with stable color scheme across multiple sessions + +#### Related Items +- **P10-T1** — Web UI Control & Audit Dashboard; the metrics and chart visualization system is the direct component affected +- **P10-T1.3** — Frontend dashboard with Chart.js visualizations; color management is part of this sub-task + +--- + +### BUG-T11: Chart Request Timeline never shows actual events +- **Type:** Bug / Web UI / Chart Data +- **Status:** ✅ Fixed (2026-02-25) +- **Priority:** P1 +- **Discovered:** 2026-02-16 +- **Completed:** 2026-02-25 +- **Component:** Web UI Dashboard (`webui/static/`, request timeline chart) +- **Affected Clients:** All clients using Web UI dashboard +- **Affected Surface:** Request Timeline chart on the Web UI dashboard + +#### Description +The "Request Timeline" chart never displays the actual events. Regardless of how many requests are made or how many errors occur, the chart always shows a static display of 1 request and 0 errors. The chart does not reflect real activity and is effectively non-functional as a monitoring tool. + +#### Symptoms +``` +Actual traffic: 50 requests, 3 errors over 10 minutes +Chart displays: 1 request, 0 errors (static, never changes) +Expected: Chart should update in real-time to show 50 requests and 3 errors +``` + +#### Root Cause Analysis +The request timeline chart is likely not consuming live metrics data correctly. Possible causes include: +- The chart data source is reading a snapshot or default value instead of the accumulated timeseries data from `SharedMetricsStore` +- The WebSocket `metrics_update` payload may not include the timeseries buckets needed by the timeline chart, causing it to fall back to a static default +- The frontend chart update logic may be replacing the dataset with a single-point summary instead of appending new data points over time +- Related to the earlier timeseries format mismatch (see FU-P10-T1-BUG-1) — the fix may not have fully resolved the issue for the request timeline specifically + +#### Workaround +None. The chart is non-functional for monitoring purposes. Users must rely on the raw counters or audit log to observe request activity. + +#### Resolution Path +- [x] Trace the data flow from `SharedMetricsStore.get_timeseries()` through the WebSocket payload to the frontend chart rendering for the request timeline +- [x] Verify that the timeseries data returned by the API contains correct per-bucket request and error counts +- [x] Verify that the frontend chart update function appends new data points rather than replacing the entire dataset with a summary +- [x] Ensure the chart x-axis (time) and y-axis (counts) are correctly bound to the timeseries data +- [x] Add integration test that simulates multiple requests and asserts the timeline chart data reflects actual counts +- [x] Validate fix with live traffic to confirm the chart updates in real-time + +#### Related Items +- **P10-T1** — Web UI Control & Audit Dashboard; the request timeline chart is part of this component +- **FU-P10-T1-BUG-1** — Earlier timeseries format mismatch bug; may share the same root cause +- **BUG-T10** — Chart color instability; related chart rendering issue + +--- + +### BUG-T12: Audit Log does not show new calls +- **Type:** Bug / Web UI / Audit Log +- **Status:** ✅ Fixed (2026-02-20) +- **Priority:** P1 +- **Discovered:** 2026-02-18 +- **Component:** Web UI Dashboard (`webui/static/`, audit log table) +- **Affected Clients:** All clients using Web UI dashboard +- **Affected Surface:** Audit Log section on the Web UI dashboard + +#### Description +New MCP tool calls are not appearing in the Audit Log table on the dashboard. The table remains static after the initial page load and does not reflect tool calls that occur while the dashboard is open. + +#### Symptoms +``` +User makes tool calls via MCP client while dashboard is open +Audit Log table: does not update, shows only entries from before page load (or stays empty) +Expected: new rows should appear in real-time (or on each refresh cycle) +``` + +#### Root Cause Analysis +Possible causes: +- The audit log WebSocket/polling update path is not delivering new entries to the frontend +- The frontend audit table rendering is not appending new rows on update (may be re-rendering from scratch and losing entries, or not re-rendering at all) +- `AuditLogger` is writing to disk but the in-memory ring buffer that feeds the `/api/audit` endpoint is not being populated + +#### Workaround +Export audit log via `/api/audit/export/json` or `/api/audit/export/csv` for a snapshot of recorded entries. + +#### Resolution Path +- [ ] Confirm that `AuditLogger` is writing entries to the in-memory ring buffer on each tool call +- [ ] Confirm that `/api/audit` returns new entries after tool calls complete +- [ ] Trace how the frontend polls or subscribes to audit updates and verify new entries are rendered +- [ ] Add a test that records a tool call and asserts the audit API returns the new entry + +#### Related Items +- **BUG-T8** ✅ — Cross-process audit log visibility; earlier fix ensured entries are shared across processes + +--- + +### BUG-T13: Per-Tool Latency Statistics does not show params when `capture_params` is false +- **Type:** Bug / Web UI / Configuration +- **Status:** ✅ Fixed (2026-02-25) +- **Priority:** P2 +- **Discovered:** 2026-02-18 +- **Completed:** 2026-02-25 +- **Component:** Web UI Dashboard (`webui/static/`, per-tool latency table), `webui/config.py` +- **Affected Clients:** All clients using Web UI dashboard with default config +- **Affected Surface:** Per-Tool Latency Statistics table + +#### Description +With `metrics.capture_params` set to `false` (the default), the Per-Tool Latency Statistics table shows no parameter information. There is no UI indication that this feature is disabled, leaving users unaware that enabling `capture_params: true` via `--web-ui-config` would unlock parameter-level analysis. + +#### Observed Config +```json +{ + "metrics": { + "capture_params": false + } +} +``` + +#### Symptoms +``` +Tool calls are made; Per-Tool Latency Statistics table shows call counts and latency. +No parameter key data is shown anywhere in the table. +No tooltip, label, or hint explains that capture_params must be enabled. +``` + +#### Root Cause Analysis +`capture_params: false` is correct by design — parameter capture is opt-in for privacy. The bug is a UX gap: the dashboard silently omits the params column/section without explaining why or how to enable it. + +#### Workaround +Enable parameter capture by passing `--web-ui-config` with `metrics.capture_params: true`. See [Web UI Dashboard docs](docs/webui-setup.md#using---web-ui-config-in-mcpjson). + +#### Resolution Path +- [x] Add a disabled-state hint in the Per-Tool Latency Statistics table when `capture_params` is false (e.g. greyed-out column with tooltip "Enable capture_params in webui config to see parameter data") +- [x] Expose the current value of `capture_params` in the `/api/config` response (already done) and have the frontend read it to conditionally render the hint +- [x] Add a test asserting the hint is present when `capture_params` is false + +#### Related Items +- **P12-T2** ✅ — Add Tool Parameter Frequency Analysis; the feature this bug surfaces + +--- + +### BUG-T14: Rows in Per-Tool Latency Statistics fold automatically immediately after unfolding +- **Type:** Bug / Web UI / UI Stability +- **Status:** ✅ Fixed (2026-02-20) +- **Priority:** P1 +- **Discovered:** 2026-02-18 +- **Completed:** 2026-02-20 +- **Component:** Web UI Dashboard (`webui/static/`, per-tool latency table) +- **Affected Clients:** All clients using Web UI dashboard +- **Affected Surface:** Per-Tool Latency Statistics table + +#### Description +Expanded or highlighted rows in the Per-Tool Latency Statistics table fold/collapse automatically immediately (or within about 1 second) after the user unfolds them. This coincides with the dashboard's default WebSocket refresh interval (`dashboard.refresh_interval_ms: 1000`), suggesting the table is being fully re-rendered on each update, discarding user interaction state (expanded rows, hover highlights, selected rows). + +#### Symptoms +``` +User expands a row in the Per-Tool Latency Statistics table to inspect details. +Immediately (or after ~1 second) the row collapses back to its default state. +Behaviour repeats on every subsequent refresh cycle. +``` + +#### Root Cause Analysis +The frontend table update logic likely replaces the entire table DOM on each WebSocket message rather than performing a targeted data update (e.g. diffing rows by tool name). This causes all interactive state to be lost on every refresh. + +#### Workaround +Increase `dashboard.refresh_interval_ms` in the webui config to a higher value (e.g. `10000`) to reduce the frequency of resets. + +#### Resolution Path +- [x] Refactor the per-tool latency table update to preserve row state during periodic updates +- [x] Preserve expanded/selected row state across updates by tracking it in frontend JS state +- [x] Add a UI test (or manual test checklist) that confirms row state survives a refresh cycle + +#### Related Items +- **BUG-T10** — Chart color changes on update; same root cause (full re-render on refresh) +- **BUG-T11** — Request Timeline never updates; related dashboard refresh issues + +--- + +### ✅ BUG-T15: Web UI fails to come up in MCP client runs when `--web-ui-port` and `--web-ui-config` are combined +- **Type:** Bug / Web UI / MCP Client Integration +- **Status:** ✅ Fixed (2026-02-20) +- **Priority:** P1 +- **Discovered:** 2026-02-20 +- **Component:** CLI arg handling in `__main__.py`, Web UI docs/examples +- **Affected Clients:** Cursor (reported), potentially Claude Code/Codex CLI/Zed +- **Affected Surface:** Dashboard startup and discoverability (`http://localhost:8080`) + +#### Description +In MCP client configuration, supplying both `--web-ui-port 8080` and `--web-ui-config /path/to/webui.json` can result in the dashboard being unreachable, while using `--web-ui` with only `--web-ui-config` works in the same environment. + +#### Reproduction Steps +1. Configure MCP with: + - `--web-ui --web-ui-port 8080 --web-ui-config /Users/egor/.config/xcodemcpwrapper/webui.json` +2. Start/restart MCP server from Cursor. +3. Open `http://localhost:8080`. +4. Observe browser cannot connect. +5. Reconfigure MCP to: + - `--web-ui --web-ui-config /Users/egor/.config/xcodemcpwrapper/webui.json` +6. Restart MCP server and verify dashboard becomes reachable. + +#### Root Cause Analysis +- `--web-ui-port` explicitly overrides the config file port, which may force an unavailable/incorrect bind target for that process lifecycle. +- Existing docs include combined examples that may be correct in isolation but fragile in real MCP multi-process launches. +- Port-collision handling may degrade into "dashboard skipped" behavior that users experience as "Web UI broken." + +#### Workaround +Use `--web-ui` with `--web-ui-config` only, and set the port in the config file. + +#### Resolution Path +- [x] Reproduce in automated/integration flow for MCP-launched process with both flags. +- [x] Capture startup stderr logs and confirm exact failure mode (`address already in use`, bind host mismatch, or other). +- [x] Decide and implement one behavior: + - Prefer config file port when both are supplied, or + - Keep CLI override but improve diagnostics and docs with explicit precedence and failure guidance. +- [x] Update docs/examples to avoid misleading combined-flag configuration for MCP clients. +- [x] Add regression test(s) covering argument precedence and dashboard startup behavior. + +#### Related Items +- **BUG-T6** ✅ — Web UI port collision behavior; likely same user-visible failure path. +- **FU-BUG-T6-1** ✅ — Current mitigation is documentation-only cleanup. +- **docs/webui-setup.md** — Contains combined `--web-ui-port` + `--web-ui-config` `mcp.json` example. + +--- + +### BUG-T16: Tool Distribution (Pie) widget is cropped at medium widths +- **Type:** Bug / Web UI / Responsive Layout +- **Status:** ✅ Fixed (2026-02-20) +- **Priority:** P1 +- **Discovered:** 2026-02-20 +- **Completed:** 2026-02-20 +- **Component:** Web UI Dashboard (`webui/static/`, chart layout CSS) +- **Affected Clients:** All clients using Web UI dashboard +- **Affected Surface:** Tool Distribution (Pie) widget + +#### Description +The "Tool Distribution (Pie)" widget does not flow correctly when the dashboard width is reduced to medium breakpoints. At around `1450px` layout is fine, around `1200px` the pie widget gets cropped, and below `768px` it becomes okay again. + +#### Symptoms +```text +~1450px viewport: widget displays correctly +~1200px viewport: Tool Distribution (Pie) widget is cropped +<768px viewport: widget displays correctly again +``` + +#### Root Cause Analysis +Likely a responsive breakpoint/layout gap between desktop and mobile rules where the chart container keeps a width/min-width/flex basis that overflows or clips at mid-range viewport sizes. + +#### Workaround +Resize the browser to either wide desktop (`~1450px+`) or narrow mobile (`<768px`) widths where the widget renders correctly. + +#### Resolution Path +- [x] Reproduce and capture exact breakpoint range where cropping begins/ends +- [x] Inspect chart container/grid/flex CSS rules for mid-width breakpoints (especially min-width, fixed widths, and overflow settings) +- [x] Update responsive CSS so the pie widget reflows without clipping at medium widths +- [x] Add/extend frontend responsiveness test or manual regression checklist for `1200px` range +- [x] Validate layout at `1450px`, `1200px`, `1024px`, `768px`, and mobile widths + +#### Related Items +- **P10-T1** ✅ — Web UI Dashboard implementation (chart layout subsystem) +- **BUG-T10** — Tool chart rendering instability; related chart UX surface + +--- + +### BUG-T17: Rows in Audit Log table automatically fold after user unfolds them +- **Type:** Bug / Web UI / UI Stability +- **Status:** ✅ Fixed (2026-02-20) +- **Priority:** P1 +- **Discovered:** 2026-02-20 +- **Completed:** 2026-02-20 +- **Component:** Web UI Dashboard (`webui/static/`, audit log table rendering) +- **Affected Clients:** All clients using Web UI dashboard +- **Affected Surface:** Audit Log table row expand/collapse behavior + +#### Description +Rows in the Audit Log table automatically fold/collapse several seconds after a user unfolds them. This interrupts inspection workflows and suggests expanded row UI state is being reset during periodic dashboard updates. + +#### Symptoms +```text +User unfolds an Audit Log row to inspect details. +After several seconds, the row folds automatically without user action. +``` + +#### Root Cause Analysis +Likely caused by full table re-render on refresh/WebSocket updates without preserving expanded-row state, similar to other dashboard state-reset issues. + +#### Workaround +Temporarily increase dashboard refresh interval via config to reduce frequency of auto-fold behavior. + +#### Resolution Path +- [x] Reproduce with default refresh interval and identify the exact trigger (WebSocket update vs polling refresh) +- [x] Refactor Audit Log table updates to patch rows by stable entry ID instead of full DOM replacement +- [x] Persist expanded/collapsed row state across refresh cycles +- [x] Add regression test (or manual checklist) confirming unfolded rows stay unfolded across updates +- [x] Validate behavior under active tool-call traffic + +#### Related Items +- **BUG-T12** — Audit Log update path not showing new calls; same component/surface +- **BUG-T14** ✅ — Per-Tool Latency row state now preserved across refresh + +--- + +### ✅ BUG-T18: Error Breakdown widget must be full width streatched +- **Type:** Bug / Web UI / Layout +- **Status:** ✅ Fixed (2026-02-26) +- **Priority:** P2 +- **Discovered:** 2026-02-20 +- **Completed:** 2026-02-26 +- **Component:** Web UI Dashboard (`webui/static/index.html`, `webui/static/dashboard.css`) +- **Affected Clients:** All clients using Web UI dashboard +- **Affected Surface:** Error Breakdown widget + +#### Description +The "Error Breakdown" widget must be displayed as a full-width stretched widget in the dashboard layout. + +#### Symptoms +```text +Error Breakdown appears as a regular half-width card in the chart grid. +Expected: Error Breakdown spans the full row width. +``` + +#### Root Cause Analysis +Likely the widget container is using the default chart card class and is not marked to span all grid columns. + +#### Workaround +None. + +#### Resolution Path +- [x] Reproduce on current dashboard layout and confirm non-full-width rendering +- [x] Update chart container/layout rules so Error Breakdown spans full width +- [x] Validate responsive behavior at desktop/tablet/mobile breakpoints +- [x] Add regression coverage for full-width Error Breakdown layout + +#### Related Items +- **P10-T1** ✅ — Web UI dashboard chart layout baseline +- **BUG-T16** ✅ — Another chart layout responsiveness fix + +--- + +### BUG-T19: Audit Log and Session Timeline are inconsistent with tool charts in multi-process runs +- **Type:** Bug / Web UI / Data Consistency +- **Status:** ✅ Fixed (2026-02-25) +- **Priority:** P1 +- **Discovered:** 2026-02-20 +- **Completed:** 2026-02-25 +- **Component:** Web UI backend (`webui/server.py`, `webui/audit.py`, `webui/shared_metrics.py`) +- **Affected Clients:** Cursor and other short-lived multi-process MCP clients +- **Affected Surface:** Audit Log table, Session Timeline, Tool usage charts + +#### Description +Dashboard surfaces are inconsistent: tool charts show fresh activity while Audit Log and Session Timeline remain stale (or show only old rows such as an earlier `initialize`), especially after reconnecting Cursor. + +#### Symptoms +```text +New tool usage appears in chart widgets. +Audit Log does not add a new row for reconnect/initialize. +Session Timeline still shows older last event. +``` + +#### Root Cause Analysis +Likely split data source model: +- charts/KPIs use `SharedMetricsStore` (cross-process SQLite), +- audit/sessions use process-local `AuditLogger` in-memory entries loaded at startup. +When a different wrapper process receives new events, chart data advances but local audit/session views can lag. + +#### Workaround +Use export endpoints (`/api/audit/export/json` or `/api/audit/export/csv`) for a broader snapshot, but real-time consistency remains unreliable. + +#### Resolution Path +- [x] Reproduce with repeated Cursor reconnects in a multi-process setup and capture API deltas between `/api/metrics`, `/api/audit`, and `/api/sessions` +- [x] Choose and implement a single shared source of truth for audit/session data across processes (SQLite-backed audit store or equivalent) +- [x] Ensure `/api/audit` reflects newly recorded entries regardless of which wrapper process logged them +- [x] Ensure `/api/sessions` is computed from the same shared data source as Audit Log +- [x] Add integration regression test covering reconnect + new initialize row visibility in Audit Log and Session Timeline +- [x] Document consistency guarantees and limitations in `docs/webui-setup.md` and troubleshooting guide + +#### Related Items +- **BUG-T12** ✅ — Audit Log live refresh path improved but did not fully solve cross-process consistency +- **BUG-T8** ✅ — Cross-process audit visibility baseline; likely related implementation surface +- **P10-T2** ✅ — Shared metrics store pattern reference + +--- + +### BUG-T20: Session Timeline can show negative duration due to incorrect entry ordering +- **Type:** Bug / Web UI / Session Analytics +- **Status:** ✅ Fixed (2026-02-25) +- **Priority:** P1 +- **Discovered:** 2026-02-20 +- **Completed:** 2026-02-25 +- **Component:** Session detection path (`webui/server.py`, `webui/sessions.py`) +- **Affected Clients:** All clients using Session Timeline +- **Affected Surface:** Session Timeline duration and ordering + +#### Description +Session Timeline can display impossible negative durations (for example, `-174224s`) and stale-looking last events. + +#### Symptoms +```text +Session shows negative duration. +Session start/end ordering appears inverted. +``` + +#### Root Cause Analysis +`detect_sessions()` expects entries sorted by timestamp ascending, but callers pass most-recent-first audit entries, causing inverted session boundaries and invalid duration math. + +#### Workaround +None. + +#### Resolution Path +- [x] Normalize session input ordering to ascending timestamps before calling `detect_sessions()` +- [x] Add defensive sorting (or contract enforcement) in session computation path +- [x] Add regression test asserting non-negative session duration for mixed/newest-first inputs +- [x] Validate timeline rendering shows monotonic ordering and correct latest event after reconnect/activity + +#### Related Items +- **BUG-T19** — Shared audit/session consistency issue may amplify session timeline staleness +- **P11-T2** ✅ — Session Timeline feature implementation + +--- + +### Phase 10: Web UI Control & Audit Dashboard + +**Intent:** Create a web-based dashboard for real-time monitoring, control, and audit logging of the XcodeMCPWrapper. Provides visibility into MCP tool usage, performance metrics, and operational control. + +#### ✅ P10-T1: Implement Web UI Control & Audit Dashboard + +**Description:** +Create a comprehensive web dashboard for monitoring and controlling the XcodeMCPWrapper. The dashboard will provide real-time metrics (RPS, latency, error rates), tool usage analytics with visualizations, request/response inspector for debugging, persistent audit logging, and service control interface. Implement using FastAPI for the backend with WebSocket support for live updates, and a modern HTML/CSS/JS frontend with Chart.js visualizations. Include configurable authentication, log rotation, and export capabilities. + +**Priority:** P1 + +**Dependencies:** P9-T1 + +**Parallelizable:** no + +**Outputs/Artifacts:** +- `src/mcpbridge_wrapper/webui/` package with: + - `server.py` - FastAPI web server with REST API and WebSocket + - `metrics.py` - Thread-safe metrics collection system + - `audit.py` - Structured audit logging with rotation + - `config.py` - Web UI configuration management + - `static/` - Frontend dashboard assets (HTML, CSS, JS) +- `config/webui.json` - Configuration template +- Updated `src/mcpbridge_wrapper/cli.py` - Add `--web-ui` flag +- Updated `pyproject.toml` - Optional webui dependencies +- Tests in `tests/unit/webui/` and `tests/integration/webui/` +- Documentation in `docs/webui-setup.md` + +**Acceptance Criteria:** +- [ ] Dashboard accessible at `http://localhost:8080` when `--web-ui` flag is used +- [ ] Real-time metrics update via WebSocket every second +- [ ] Tool usage charts (bar, pie, timeline) display accurate data +- [ ] Audit logs capture all MCP tool calls with timestamps +- [ ] Log export produces valid JSON/CSV files +- [ ] Web UI has < 1% performance impact on wrapper core +- [ ] All existing tests pass with Web UI enabled +- [ ] New unit tests achieve > 90% coverage for webui module +- [ ] Documentation includes setup and troubleshooting guide +- [ ] Optional authentication works correctly +- [ ] Log rotation prevents unbounded disk usage + +**Sub-tasks:** +1. P10-T1.1: Create webui package structure and metrics collection hooks +2. P10-T1.2: Implement FastAPI server with REST endpoints and WebSocket +3. P10-T1.3: Build frontend dashboard with Chart.js visualizations +4. P10-T1.4: Implement audit logging with rotation +5. P10-T1.5: Add CLI integration and configuration +6. P10-T1.6: Write tests and documentation + +--- + +#### ✅ P10-T2: Fix Web UI timeseries charts showing no data + +**Description:** +The Web UI dashboard shows "Connected" and counters work correctly, but the timeseries charts ("Request timeline" and "Latency") show no data. The issue is that `SharedMetricsStore.get_timeseries()` returns data in a different format than the frontend expects: + +- **Current (wrong):** `{"data": [{"timestamp": "...", "requests": N, "errors": N, "latency_ms": N}]}` +- **Expected by frontend:** `{"requests": [{"t": seconds_ago, "v": count}], "errors": [...], "latencies": [{"t": seconds_ago, "v": latency}]}` + +The frontend JavaScript expects arrays of `{t, v}` objects for each metric type, with time as "seconds ago" relative to now. The SharedMetricsStore currently returns minute-bucketed data with string timestamps. + +**Root Cause:** +When migrating from in-memory `MetricsCollector` (which had the correct format) to `SharedMetricsStore` (SQLite-based for multi-process support), the `get_timeseries()` method was implemented with a different return format that doesn't match the frontend expectations. + +**Priority:** P1 + +**Dependencies:** P10-T1 + +**Parallelizable:** no + +**Outputs/Artifacts:** +- Fixed `src/mcpbridge_wrapper/webui/shared_metrics.py` - Update `get_timeseries()` to return format matching frontend expectations +- Updated tests in `tests/unit/webui/test_shared_metrics.py` - Verify timeseries format +- Validation report confirming charts display data correctly + +**Acceptance Criteria:** +- [ ] `/api/metrics/timeseries` returns data in format `{"requests": [...], "errors": [...], "latencies": [...]}` +- [ ] Each array contains objects with `t` (seconds ago) and `v` (value) properties +- [ ] Request timeline chart displays data points +- [ ] Latency chart displays data points +- [ ] Charts update in real-time via WebSocket +- [ ] All existing tests pass +- [ ] New tests verify timeseries format matches frontend expectations + +--- + +#### ✅ P10-T3: Recover main branch after accidental Web UI merge + +**Description:** +Main branch is currently unstable after an accidental merge of the Phase 10 Web UI branch. Diagnose regressions introduced by that merge and restore main to a releasable state without discarding intended Web UI functionality. + +**Priority:** P0 + +**Dependencies:** P10-T2 + +**Parallelizable:** no + +**Outputs/Artifacts:** +- Regression report listing failures introduced by the accidental merge +- Corrective patch set (revert and/or forward-fix) to restore stability +- Updated tests/docs where behavior changed during stabilization + +**Acceptance Criteria:** +- [ ] `pytest` passes on the recovery branch +- [ ] `ruff check src/` and `mypy src/` pass +- [ ] Web UI functionality from P10 remains operational after stabilization +- [ ] No known merge-regression failures remain on the branch proposed for `main` + +--- + +### Phase 9: Release Management + +**Intent:** Manage version releases, including version bumps, changelog updates, and automated publishing. + +#### ✅ P9-T2: Update Documentation with uvx Installation Method +- **Description:** Update all documentation to include uvx as the recommended installation method. The package is now published to PyPI and MCP Registry, and uvx provides the easiest way to install without cloning the repository or manually setting up paths. Update README.md, all docs/*.md files, AGENTS.md, and config templates with uvx instructions. +- **Priority:** P1 +- **Dependencies:** P9-T1 +- **Parallelizable:** no +- **Outputs/Artifacts:** + - Updated `README.md` - Primary uvx method documented, manual install as alternative + - Updated `docs/installation.md` - uvx installation section + - Updated `docs/cursor-setup.md` - uvx configuration examples + - Updated `docs/claude-setup.md` - uvx configuration examples + - Updated `docs/codex-setup.md` - uvx configuration examples + - Updated `AGENTS.md` - uvx method in Quick Start + - Updated `config/cursor-mcp.json` - uvx template option + - Updated `config/claude-code.txt` - uvx command option + - Updated `config/codex-cli.txt` - uvx command option +- **Acceptance Criteria:** + - All documentation shows uvx as the primary/recommended installation method + - Manual installation is documented as an alternative for development + - All config templates include uvx options + - uvx installation verified working (already tested by user) + - No breaking changes to existing manual installation paths + +--- + +#### ✅ P9-T1: Release version 0.2.0 +- **Description:** Bump version to 0.2.0, update CHANGELOG, create git tag, and trigger automated publishing to PyPI and MCP Registry +- **Priority:** P1 +- **Dependencies:** P8-T2 +- **Parallelizable:** no +- **Outputs/Artifacts:** + - Version updated in `pyproject.toml` (0.1.7 → 0.2.0) + - Version updated in `server.json` (0.1.7 → 0.2.0) + - CHANGELOG.md entry for v0.2.0 + - Git tag `v0.2.0` pushed to origin + - GitHub Release created automatically +- **Acceptance Criteria:** + - `pyproject.toml` shows version 0.2.0 + - `server.json` shows version 0.2.0 + - CHANGELOG has entry for [0.2.0] with release date + - Git tag `v0.2.0` exists on GitHub + - GitHub Actions workflow publishes to PyPI successfully + - MCP Registry receives the new version +- **Release Checklist:** + - [ ] Update version in `pyproject.toml` + - [ ] Update version in `server.json` + - [ ] Add CHANGELOG entry for 0.2.0 + - [ ] Commit changes: "Bump version to 0.2.0" + - [ ] Create git tag: `git tag v0.2.0` + - [ ] Push tag: `git push origin v0.2.0` + - [ ] Verify GitHub Actions workflow completes + - [ ] Verify PyPI package updated + - [ ] Verify MCP Registry updated + +--- + +#### ✅ P9-T3: Release version 0.3.0 (Web UI Feature Release) +- **Description:** Prepare and publish version 0.3.0 as the Web UI release. Include final version bumps, release notes for the new dashboard feature set, and tagged publication to PyPI and MCP Registry. +- **Priority:** P1 +- **Dependencies:** P10-T3, FU-REBUILD-P10-T1-6 +- **Parallelizable:** no +- **Outputs/Artifacts:** + - Version updated in `pyproject.toml` (0.2.0 -> 0.3.0) + - Version updated in `server.json` (0.2.0 -> 0.3.0) + - CHANGELOG.md entry for v0.3.0 with Web UI highlights + - Git tag `v0.3.0` pushed to origin + - GitHub Release created automatically +- **Acceptance Criteria:** + - `pyproject.toml` shows version 0.3.0 + - `server.json` shows version 0.3.0 + - CHANGELOG has entry for [0.3.0] with release date and Web UI feature summary + - Git tag `v0.3.0` exists on GitHub + - GitHub Actions workflow publishes to PyPI successfully + - MCP Registry receives version 0.3.0 +- **Release Checklist:** + - [x] Update version in `pyproject.toml` + - [x] Update version in `server.json` + - [x] Add CHANGELOG entry for 0.3.0 (Web UI release) + - [x] Commit changes: "Bump version to 0.3.0" + - [x] Create git tag: `git tag v0.3.0` + - [x] Push tag: `git push origin v0.3.0` + - [x] Verify GitHub Actions workflow completes + - [x] Verify PyPI package updated + - [x] Verify MCP Registry updated + +--- + +#### ✅ P9-T4: Create the publishing helper +- **Description:** Create a helper script to streamline release version updates for publishing. The script should update all required version fields in one run (at minimum `pyproject.toml` and `server.json`), validate the provided semantic version, and guide the user through the remaining publish steps documented in `PUBLISHING.md`. +- **Priority:** P1 +- **Dependencies:** P9-T3 +- **Parallelizable:** yes +- **Outputs/Artifacts:** + - New helper script at `scripts/publish_helper.py` (or equivalent script under `scripts/`) + - Documentation update in `PUBLISHING.md` showing how to run the helper + - Optional `Makefile` convenience target for version bumping +- **Acceptance Criteria:** + - Running the helper with a target version updates `pyproject.toml` and `server.json` to the exact same version value + - The helper rejects invalid version formats with a clear error message + - The helper supports a dry-run mode that prints planned changes without modifying files + - The helper prints next release commands (commit, tag, push) aligned with `PUBLISHING.md` + - Existing tests and quality gates continue to pass after integrating the helper + +--- + +Phase 9 Follow-up Backlog +- [x] FU-P9-T2-1: Fix uvx Web UI examples to include `webui` extras (P1) +- [x] FU-P9-T4-1: Align publish_helper output with protected main branch workflow (P1) +- [x] FU-P9-T2-2: Add troubleshooting guidance for stale uvx cache/process versions (P1) + +#### ✅ FU-P9-T2-1: Fix uvx Web UI examples to include `webui` extras +- **Description:** Resolve documentation/config mismatch where examples use `uvx --from mcpbridge-wrapper ... --web-ui` without optional dependencies. Update all uvx Web UI examples to install extras via `--from mcpbridge-wrapper[webui]`, and align troubleshooting/runtime guidance with the correct uvx command. +- **Priority:** P1 +- **Dependencies:** P9-T2 +- **Parallelizable:** yes +- **Outputs/Artifacts:** + - Updated `README.md` uvx + Web UI snippets use `mcpbridge-wrapper[webui]` + - Updated `docs/cursor-setup.md`, `docs/claude-setup.md`, `docs/codex-setup.md` uvx + Web UI commands + - Updated config templates: `config/cursor-mcp.json`, `config/zed-agent.json`, `config/claude-code.txt`, `config/codex-cli.txt` + - Updated troubleshooting guidance to include uvx extras fix path + - Optional: improved runtime error message when `--web-ui` is used without extras +- **Acceptance Criteria:** + - No remaining documented command/config combines `--web-ui` with `uvx --from mcpbridge-wrapper` (base-only) + - All uvx Web UI examples consistently use `uvx --from mcpbridge-wrapper[webui] mcpbridge-wrapper` + - A user can copy/paste the documented Cursor JSON Web UI config and connect without `ModuleNotFoundError: uvicorn` + - Troubleshooting docs include both solutions: + - use `mcpbridge-wrapper[webui]` for uvx + - remove `--web-ui` args when dashboard is not needed + +--- + +#### ✅ FU-P9-T4-1: Align publish_helper output with protected main branch workflow +- **Description:** Update `scripts/publish_helper.py` release guidance so it does not instruct direct commits/tags from `main` in repositories where `main` is protected. Guidance should explicitly recommend creating a release branch, pushing branch commits, opening a PR into `main`, and only tagging after merge. +- **Priority:** P1 +- **Dependencies:** P9-T4 +- **Parallelizable:** yes +- **Outputs/Artifacts:** + - Updated `scripts/publish_helper.py` summary text and command block + - Updated tests in `tests/unit/test_publish_helper.py` validating protected-branch-safe guidance + - Optional alignment update in `PUBLISHING.md` if command sequence is duplicated there +- **Acceptance Criteria:** + - [x] Running `python scripts/publish_helper.py ` no longer suggests direct push-to-main flow + - [x] Printed commands include branch creation + push + PR-to-main step before tagging + - [x] Guidance still includes tag creation/push after merge so GitHub publish workflow is triggered + - [x] `pytest tests/unit/test_publish_helper.py` passes + +--- + +#### ✅ FU-P9-T2-2: Add troubleshooting guidance for stale uvx cache/process versions +- **Description:** Document the failure mode where Cursor or manual Web UI sessions keep running an older `uvx` environment (for example `mcpbridge-wrapper==0.3.2`) after a fix is released, causing stale behavior such as uptime stuck at `1h 0m 0s`. Add explicit verification and recovery steps using port/PID inspection, version checks, process restart, and `uvx --refresh`. +- **Priority:** P1 +- **Dependencies:** P9-T2, FU-P9-T2-1 +- **Parallelizable:** yes +- **Outputs/Artifacts:** + - Updated `docs/troubleshooting.md` with a dedicated section for stale `uvx` cache/process diagnosis + - Updated setup docs (at minimum `docs/cursor-setup.md`) with a one-time `--refresh` guidance note after new releases + - Optional quick-check snippet in `README.md` for confirming active runtime package version on a dashboard port +- **Acceptance Criteria:** + - [x] Troubleshooting docs include symptom/cause/fix for: uptime or behavior unchanged after upgrade + - Docs provide concrete commands to: + - [x] identify process listening on Web UI port + - [x] inspect active runtime version from that process + - [x] restart with `uvx --refresh --from mcpbridge-wrapper[webui] ...` + - [x] Recovery steps explicitly mention that multiple wrapper processes can coexist and mask upgrades + - [x] Guidance is validated against a local repro where an old process serves stale behavior and a refreshed process resolves it + +--- + +### Phase 11: Web UI UX Improvements + +**Intent:** Enhance the dashboard with better debugging tools, session awareness, theming, and keyboard-driven workflows. + +#### ✅ P11-T1: Add Tool Call Detail Inspector (Request/Response Viewer) +- **Description:** Add a clickable row expansion or slide-out panel in the audit table that displays the full JSON-RPC request and response payloads. Payloads are syntax-highlighted and collapsible. Backend stores truncated payloads in a bounded ring buffer (last 500, max 64KB each) behind an optional `capture_payload` config flag (default off for privacy). New API: `GET /api/audit/{request_id}/detail` returns `{request: {...}, response: {...}}`. Frontend: click audit row to expand inline or open side panel with pretty-printed JSON. +- **Priority:** P1 +- **Dependencies:** P10-T1 +- **Parallelizable:** yes +- **Outputs/Artifacts:** + - Updated `src/mcpbridge_wrapper/webui/audit.py` - payload capture and storage + - New SQLite table or column for request/response payloads + - New API endpoint in `src/mcpbridge_wrapper/webui/server.py` + - Updated `src/mcpbridge_wrapper/webui/static/dashboard.js` - row expansion UI + - Updated `src/mcpbridge_wrapper/webui/static/dashboard.css` - detail panel styling + - Updated `src/mcpbridge_wrapper/webui/config.py` - `capture_payload` flag + - Tests in `tests/unit/webui/test_audit.py` and `tests/unit/webui/test_server.py` +- **Status:** ✅ DONE (2026-02-15) +- **Acceptance Criteria:** + - [x] `capture_payload: true` in config enables payload storage + - [x] `GET /api/audit/{request_id}/detail` returns full request/response JSON + - [x] Clicking an audit row in the dashboard expands to show payload detail + - [x] Payloads are truncated at 64KB to bound storage + - [x] Ring buffer retains last 500 payloads and evicts oldest + - [x] Default behavior (flag off) is unchanged — no payload capture overhead + - [x] Tests cover payload capture, retrieval, truncation, and ring buffer eviction + +--- + +#### ✅ P11-T2: Add Session Timeline View +- **Description:** Add a vertical timeline view that groups tool calls into sessions detected by configurable idle gaps (default 5 min). Each session shows a compact sequence of tool calls with icons, durations, and error badges. New API: `GET /api/sessions` returns `[{id, start, end, tool_count, error_count, tools: [...]}]`. Frontend: new tab/view with vertical timeline using CSS. Each node is a tool call; hover shows summary; click opens detail inspector (P11-T1). +- **Priority:** P1 +- **Dependencies:** P11-T1 +- **Parallelizable:** no +- **Outputs/Artifacts:** + - New module `src/mcpbridge_wrapper/webui/sessions.py` - session detection logic + - New API endpoint `GET /api/sessions` in `src/mcpbridge_wrapper/webui/server.py` + - Updated `src/mcpbridge_wrapper/webui/static/index.html` - timeline tab + - Updated `src/mcpbridge_wrapper/webui/static/dashboard.js` - timeline rendering + - Updated `src/mcpbridge_wrapper/webui/static/dashboard.css` - timeline styling + - Updated `src/mcpbridge_wrapper/webui/config.py` - `session_gap_seconds` setting + - Tests in `tests/unit/webui/test_sessions.py` +- **Acceptance Criteria:** + - [x] Sessions are detected by idle gap (configurable, default 300s) + - [x] `GET /api/sessions` returns session list with tool call summaries + - [x] Dashboard displays vertical timeline with tool call nodes + - [x] Hover on node shows tool name, latency, error status + - [x] Click on node opens detail inspector (if P11-T1 payload capture enabled) + - [x] Sessions update via periodic poll (15s) + manual Refresh + - [x] Tests cover session boundary detection, edge cases (single-call sessions, zero-gap) + +--- + +#### ✅ FU-P11-T2-1: Push session data via WebSocket for real-time timeline updates +- **Description:** Extend the WebSocket `metrics_update` message in `server.py` to include current session data from `detect_sessions()`. Update `dashboard.js` to refresh the timeline on every WebSocket message instead of using the 15s poll. This fulfills the original P11-T2 acceptance criterion of "real-time via WebSocket." +- **Priority:** P3 +- **Dependencies:** P11-T2 +- **Parallelizable:** yes +- **Outputs/Artifacts:** + - Updated `src/mcpbridge_wrapper/webui/server.py` — include sessions in WebSocket payload + - Updated `src/mcpbridge_wrapper/webui/static/dashboard.js` — consume sessions from WS message +- **Acceptance Criteria:** + - [ ] WebSocket `metrics_update` message includes `sessions` key + - [ ] Dashboard timeline updates immediately on each WebSocket push + - [ ] 15s poll fallback removed or made redundant + +--- + +#### ✅ FU-P11-T2-2: Add `limit` query param to `GET /api/sessions` +- **Description:** Add an optional `limit` query parameter (default: all, max: 10000) to `GET /api/sessions` that caps the number of audit entries fed to `detect_sessions()`. This prevents slow responses for large audit logs. +- **Priority:** P3 +- **Dependencies:** P11-T2 +- **Parallelizable:** yes +- **Outputs/Artifacts:** + - Updated `src/mcpbridge_wrapper/webui/server.py` — `limit` query param on sessions endpoint +- **Acceptance Criteria:** + - [ ] `GET /api/sessions?limit=500` fetches at most 500 most-recent entries before session grouping + - [ ] Default (no `limit`) retains current behavior (up to 10,000 entries) + - [ ] Tests updated to cover limit parameter behavior + +--- + +#### ✅ FU-P11-T2-3: Reorder sessions from the last to the first +- **Description:** Fix session ordering so the Session Timeline shows the most recent session first (newest-to-oldest). Current behavior shows the oldest session first, which makes fresh activity harder to find. +- **Priority:** P2 +- **Dependencies:** P11-T2 +- **Parallelizable:** yes +- **Outputs/Artifacts:** + - Updated `src/mcpbridge_wrapper/webui/sessions.py` — enforce newest-first session ordering + - Updated `src/mcpbridge_wrapper/webui/server.py` and/or `src/mcpbridge_wrapper/webui/static/dashboard.js` — preserve newest-first ordering in API/UI rendering + - Updated tests in `tests/unit/webui/test_sessions.py` and/or `tests/unit/webui/test_server.py` +- **Acceptance Criteria:** + - [ ] `GET /api/sessions` returns sessions ordered by latest start time first + - [ ] Timeline labels show the newest group as `SESSION 1` + - [ ] Refresh and live updates keep the same newest-first ordering + - [ ] Tests cover ordering with at least two sessions at different timestamps + +--- + +#### ✅ FU-P11-T2-4: Add one-command Web UI restart workflow +- **Description:** Add a simple restart workflow for developers and users that reliably frees the configured Web UI port and starts a fresh dashboard process after updates. +- **Priority:** P2 +- **Dependencies:** P11-T2 +- **Parallelizable:** yes +- **Outputs/Artifacts:** + - Updated `src/mcpbridge_wrapper/__main__.py` and/or helper script — support restart semantics (`stop stale listener on port`, then `start`) + - Updated `Makefile` — add a `webui-restart` target + - Updated `docs/troubleshooting.md` and `Sources/XcodeMCPWrapper/Documentation.docc/Troubleshooting.md` — document one-step restart command +- **Acceptance Criteria:** + - [ ] A single documented command restarts Web UI on a chosen port without manual PID hunting + - [ ] Restart flow attempts graceful stop first, then force-kill only if needed + - [ ] Works for both local/dev install and uvx usage + - [ ] Tests cover restart behavior and port-occupied edge case(s) where practical + +--- + +#### ✅ P11-T3: Add Dashboard Theme Toggle (Dark/Light) +- **Description:** Implement CSS-variable-based theme system with a toggle button in the header. Refactor all hardcoded colors in `dashboard.css` to CSS custom properties on `:root`. Add `[data-theme="light"]` overrides. Store user preference in `localStorage`. Update Chart.js color defaults on theme toggle. +- **Priority:** P2 +- **Dependencies:** P10-T1 +- **Parallelizable:** yes +- **Outputs/Artifacts:** + - Updated `src/mcpbridge_wrapper/webui/static/dashboard.css` - CSS variable refactor + light theme + - Updated `src/mcpbridge_wrapper/webui/static/dashboard.js` - theme toggle logic and Chart.js color sync + - Updated `src/mcpbridge_wrapper/webui/static/index.html` - theme toggle button in header +- **Acceptance Criteria:** + - [ ] All colors in CSS use custom properties (no hardcoded hex in selectors) + - [ ] Toggle button switches between dark and light themes + - [ ] Chart.js chart colors update on theme change without page reload + - [ ] Theme preference persists across page reloads via `localStorage` + - [ ] Default theme matches current dark theme (no visual regression) + +--- + +#### ✅ P11-T4: Add Keyboard Shortcuts & Command Palette +- **Description:** Add lightweight keyboard shortcuts for dashboard navigation. `1-4` to focus chart sections, `a` to jump to audit log, `r` to reset metrics (with confirmation), `e` to export JSON, `?` to show shortcut help overlay. Pure JS `keydown` listener with a shortcut map. Small modal overlay for `?` help. No library needed. +- **Priority:** P3 +- **Dependencies:** P10-T1 +- **Parallelizable:** yes +- **Outputs/Artifacts:** + - Updated `src/mcpbridge_wrapper/webui/static/dashboard.js` - shortcut handler + - Updated `src/mcpbridge_wrapper/webui/static/dashboard.css` - help overlay styling + - Updated `src/mcpbridge_wrapper/webui/static/index.html` - help overlay markup +- **Acceptance Criteria:** + - [x] `?` key opens/closes shortcut help overlay + - [x] Number keys `1-4` scroll to corresponding chart section + - [x] `a` key scrolls to audit log section + - [x] `r` key triggers reset metrics with confirmation dialog + - [x] `e` key triggers JSON export download + - [x] Shortcuts are disabled when focus is in an input field (audit filter) + - [x] Help overlay lists all available shortcuts with descriptions + +--- + +### Phase 12: Data Collection Enhancements + +**Intent:** Enrich collected telemetry with client identity, parameter patterns, and structured error classification for deeper operational insight. + +#### ✅ P12-T1: Add MCP Client Identification +- **Description:** Detect the calling MCP client from the `initialize` handshake. The `clientInfo` field in the initialize request contains `{name, version}`. Capture this and tag all subsequent metrics with the client identity. Add `client` column to shared metrics SQLite schema. Dashboard: new KPI card "Active Client" showing the connected client name and version. Charts: optional client-based breakdown in tool usage. +- **Priority:** P0 +- **Dependencies:** P10-T1 +- **Parallelizable:** yes +- **Outputs/Artifacts:** + - Updated `src/mcpbridge_wrapper/__main__.py` - extract `clientInfo` from initialize request + - Updated `src/mcpbridge_wrapper/schemas.py` - add `MCPInitializeParams` model with `clientInfo` + - Updated `src/mcpbridge_wrapper/webui/metrics.py` - `client_name` field in metrics + - Updated `src/mcpbridge_wrapper/webui/shared_metrics.py` - `client` column in SQLite schema + - Updated `src/mcpbridge_wrapper/webui/server.py` - expose client info in metrics summary + - Updated `src/mcpbridge_wrapper/webui/static/index.html` - "Active Client" KPI card + - Updated `src/mcpbridge_wrapper/webui/static/dashboard.js` - render client KPI + - Tests in `tests/unit/test_main.py` and `tests/unit/webui/test_metrics.py` +- **Acceptance Criteria:** + - [ ] `initialize` request `clientInfo.name` and `clientInfo.version` are captured + - [ ] Metrics summary includes `client_name` and `client_version` fields + - [ ] Dashboard displays "Active Client" KPI card (e.g. "Cursor 1.2.3") + - [ ] Metrics reset clears client info + - [ ] If `initialize` has no `clientInfo`, fields default to "unknown" + - [ ] SQLite schema migration is backward-compatible (nullable column) + - [ ] Tests cover initialize parsing, missing clientInfo, and metric tagging + +--- + +#### ✅ P12-T2: Add Tool Parameter Frequency Analysis +- **Description:** Optionally capture and aggregate tool call parameter keys (not values by default) for pattern analysis. Config flag `capture_params: bool` (default off). On request capture, extract `params.arguments` key names. Store parameter key signatures per tool (e.g. `XcodeGrep(pattern, path, tabIdentifier)`). New API: `GET /api/analytics/param-patterns?tool=` returns top-N parameter combinations. Dashboard: expandable section in latency table showing common param combos. +- **Priority:** P3 +- **Dependencies:** P12-T1 +- **Parallelizable:** yes +- **Outputs/Artifacts:** + - Updated `src/mcpbridge_wrapper/__main__.py` - extract argument keys from tool call params + - New module or section in `src/mcpbridge_wrapper/webui/metrics.py` - param pattern aggregation + - Updated `src/mcpbridge_wrapper/webui/shared_metrics.py` - `param_keys` column in requests table + - New API endpoint `GET /api/analytics/param-patterns` in `src/mcpbridge_wrapper/webui/server.py` + - Updated `src/mcpbridge_wrapper/webui/config.py` - `capture_params` flag + - Updated `src/mcpbridge_wrapper/webui/static/dashboard.js` - param pattern display + - Tests in `tests/unit/webui/test_metrics.py` +- **Acceptance Criteria:** + - [ ] `capture_params: true` enables parameter key capture + - [ ] Only argument key names are stored (not values) by default + - [ ] `GET /api/analytics/param-patterns?tool=XcodeGrep` returns ranked param combos + - [ ] Dashboard shows expandable param pattern info per tool + - [ ] Default behavior (flag off) is unchanged — no extra capture overhead + - [ ] Tests cover param extraction, aggregation, and API response format + +--- + +#### ✅ P12-T3: Add Error Classification & Categorization +- **Description:** Parse JSON-RPC error codes and messages from responses. Categorize into buckets: protocol errors (-326xx), tool execution errors (Xcode-side failures), timeout errors, connection errors. Extend `record_response` to accept `error_code: Optional[int]` and `error_message: Optional[str]`. New metrics: `error_counts_by_code: Dict[int, int]`. Dashboard: replace single "Total Errors" KPI with error breakdown doughnut chart. Audit table: color-code error column by severity. +- **Priority:** P1 +- **Dependencies:** P10-T1 +- **Parallelizable:** yes +- **Outputs/Artifacts:** + - Updated `src/mcpbridge_wrapper/__main__.py` - extract error code and message from responses + - Updated `src/mcpbridge_wrapper/schemas.py` - expose `error.code` and `error.message` accessors + - Updated `src/mcpbridge_wrapper/webui/metrics.py` - `error_counts_by_code` tracking + - Updated `src/mcpbridge_wrapper/webui/shared_metrics.py` - `error_code` and `error_message` columns + - Updated `src/mcpbridge_wrapper/webui/server.py` - expose error breakdown in metrics summary + - Updated `src/mcpbridge_wrapper/webui/static/index.html` - error breakdown chart container + - Updated `src/mcpbridge_wrapper/webui/static/dashboard.js` - error doughnut chart + audit color-coding + - Updated `src/mcpbridge_wrapper/webui/static/dashboard.css` - error severity color classes + - Tests in `tests/unit/webui/test_metrics.py` and `tests/unit/webui/test_shared_metrics.py` +- **Acceptance Criteria:** + - [ ] JSON-RPC error code and message are extracted from error responses + - [ ] Metrics summary includes `error_counts_by_code` map (e.g. `{-32600: 5, -32601: 2}`) + - [ ] Dashboard displays error breakdown doughnut chart alongside or replacing "Total Errors" KPI + - [ ] Audit table error column is color-coded by severity (red for protocol, orange for tool, yellow for timeout) + - [ ] Error categories are defined: protocol (-326xx), tool (positive codes), timeout, unknown + - [ ] Non-error responses leave error code/message as null + - [ ] Tests cover code extraction, categorization, and metric aggregation + +--- + +#### ✅ P12-T4: Add documentation about data storage +- **Description:** Document the structure of all data storage containers used by mcpbridge-wrapper, including the SQLite database schema (`shared_metrics.db`), the in-memory metrics structures (`MetricsCollector`, `SharedMetricsCollector`), audit log format, and any other persistent or transient data containers. This documentation should explain table schemas, column semantics, retention policies, and how data flows between components (e.g. from `__main__.py` capture → `metrics.py` aggregation → `shared_metrics.py` persistence → Web UI API). +- **Priority:** P2 +- **Dependencies:** P12-T1, P12-T3 +- **Parallelizable:** yes +- **Outputs/Artifacts:** + - New `docs/data-storage.md` - comprehensive reference for all data containers + - Updated docstrings in `src/mcpbridge_wrapper/webui/shared_metrics.py` describing each table/column + - Updated docstrings in `src/mcpbridge_wrapper/webui/metrics.py` describing in-memory structures + - Optional: ER diagram or table relationship overview in `docs/data-storage.md` +- **Acceptance Criteria:** + - [ ] SQLite schema documented with all tables, columns, types, and nullability + - [ ] In-memory `MetricsCollector` and `SharedMetricsCollector` fields documented + - [ ] Audit log format (CSV export columns and semantics) documented + - [ ] Data flow from capture to storage to API explained + - [ ] Retention/reset behavior documented (e.g. what resets on metrics clear) + - [ ] Document is discoverable from `README.md` or `docs/` index + +--- + +### Phase 13: Persistent Broker & Shared Xcode Session + +**Intent:** Introduce a long-lived broker process that owns the Xcode bridge connection and multiplexes multiple MCP clients through one upstream session. + +#### ✅ P13-T1: Design persistent broker architecture and protocol contract +- **Status:** ✅ Completed (2026-02-16) +- **Description:** Define daemon lifecycle, client transport choice (Unix domain socket first), request/response correlation strategy, reconnect behavior, and failure boundaries between broker, upstream bridge, and client proxies. +- **Priority:** P0 +- **Dependencies:** P2-T6, P3-T10 +- **Parallelizable:** no +- **Outputs/Artifacts:** + - Broker architecture spec (sequence diagrams and lifecycle states) + - ADR documenting transport and security choices + - Initial module scaffold under `src/mcpbridge_wrapper/broker/` +- **Acceptance Criteria:** + - [ ] Architecture covers startup, shutdown, reconnect, and stale-socket recovery + - [ ] Correlation strategy for concurrent JSON-RPC requests is specified + - [ ] Security boundary for local clients is documented (socket permissions/token) + - [ ] Design is reviewed and approved for implementation + +--- + +#### ✅ P13-T2: Implement persistent broker daemon with single upstream Xcode bridge +- **Description:** Add daemon mode that launches and owns one `xcrun mcpbridge` subprocess, keeps it alive, and exposes broker readiness state to clients. +- **Priority:** P0 +- **Dependencies:** P13-T1 +- **Parallelizable:** no +- **Outputs/Artifacts:** + - `src/mcpbridge_wrapper/broker/daemon.py` + - PID/lock handling + stale lock cleanup + - Health endpoint or status command (`broker status`) +- **Acceptance Criteria:** + - [ ] Starting broker twice does not spawn duplicate upstream bridge instances + - [ ] Broker survives client disconnects without restarting upstream bridge + - [ ] Graceful shutdown terminates upstream process and cleans lock/socket files + - [ ] Crash recovery path is covered by tests + +--- + +#### ✅ FU-P13-T2-1: Replace run_forever() polling loop with asyncio.Event-based wait +- **Status:** ✅ Completed (2026-02-18) +- **Type:** Enhancement +- **Priority:** P3 +- **Discovered:** 2026-02-17 (REVIEW_P13-T2) +- **Component:** BrokerDaemon.run_forever() +- **Description:** Current implementation uses `asyncio.sleep(0.1)` polling which introduces up to 100ms stop-signal latency. Replace with `asyncio.Event.wait()` for idiomatic zero-latency shutdown. +- **Acceptance Criteria:** + - [x] `run_forever()` responds to stop signal within one event loop tick + - [x] Existing `test_run_forever_starts_and_stops` passes without change + +--- + +#### ✅ FU-P13-T2-2: Move PID file write to after successful upstream launch +- **Status:** ✅ Completed (2026-02-18) +- **Type:** Robustness +- **Priority:** P3 +- **Discovered:** 2026-02-17 (REVIEW_P13-T2) +- **Component:** BrokerDaemon.start() +- **Description:** PID file is currently written before upstream subprocess is launched. A crash between write and launch leaves a live-PID lock that blocks future starts until the owning process dies. Move the write to after successful launch. +- **Acceptance Criteria:** + - [x] PID file is written only after `_launch_upstream()` succeeds + - [x] Stale-lock tests continue to pass + +--- + +#### ✅ P13-T3: Implement multi-client transport and JSON-RPC multiplexing +- **Description:** Add local transport server (Unix socket) that accepts multiple clients and multiplexes JSON-RPC traffic to/from the single upstream bridge while preserving per-client response routing. +- **Priority:** P0 +- **Dependencies:** P13-T2 +- **Parallelizable:** no +- **Outputs/Artifacts:** + - `src/mcpbridge_wrapper/broker/transport.py` + - Client session manager and request ID routing map + - Backpressure/queue limits and timeout handling +- **Status:** ✅ Completed 2026-02-18 +- **Acceptance Criteria:** + - [x] At least two concurrent clients can perform tool calls successfully + - [x] Responses are routed back to the correct client/request + - [x] Broker handles malformed client payloads without affecting other clients + - [x] Queue/timeout behavior is tested and deterministic + +--- + +#### ✅ P13-T4: Add stdio proxy mode for compatibility with existing MCP clients +- **Description:** Implement a proxy mode where standard MCP clients still use stdio, but the wrapper process forwards traffic to the persistent local broker instead of spawning a new upstream bridge. +- **Priority:** P1 +- **Dependencies:** P13-T3 +- **Parallelizable:** yes +- **Outputs/Artifacts:** + - CLI flags for broker usage (e.g., `--broker-connect`, `--broker-spawn`) + - Proxy adapter module under `src/mcpbridge_wrapper/broker/proxy.py` + - Backward-compatible default behavior toggle strategy +- **Acceptance Criteria:** + - [x] Existing MCP client configs can opt into broker mode with minimal changes + - [x] Proxy process exit does not terminate broker daemon + - [x] Legacy direct mode remains available for fallback + - [x] Unit tests cover proxy connect/disconnect and reconnect behavior + +--- + +#### ✅ 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:** + - [x] All `asyncio.get_event_loop()` calls in proxy.py replaced with `asyncio.get_running_loop()` + - [x] Tests still pass + +--- + +#### ✅ FU-P13-T4-2: Implement or remove reconnect parameter in BrokerProxy +- **Status:** ✅ Completed (2026-02-18) +- **Description:** The `reconnect: bool` parameter is stored but never used in `_run_bridge`. Either implement the reconnect loop (retry once on broken socket before stdin EOF) or remove the parameter entirely and add a comment referencing P13-T5. +- **Priority:** P2 +- **Dependencies:** P13-T4 +- **Parallelizable:** yes +- **Outputs/Artifacts:** + - Updated `src/mcpbridge_wrapper/broker/proxy.py` + - Updated `tests/unit/test_broker_proxy.py` +- **Acceptance Criteria:** + - [x] `reconnect=True` either reconnects on broken socket or the parameter is removed + - [x] No dead/unused code remains + - [x] Tests pass + +--- + +#### ✅ P13-T5: Validate prompt reduction and multi-client stability +- **Status:** ❌ FAIL (2026-02-19, broker-mode validation blocked by UID mismatch rejection) +- **Description:** Add integration and manual verification that repeated short-lived client sessions can reuse the broker session without repeated upstream churn, plus load tests for concurrent calls. +- **Priority:** P1 +- **Dependencies:** P13-T4 +- **Parallelizable:** no +- **Outputs/Artifacts:** + - `tests/integration/test_broker_multi_client.py` + - Manual validation report for Xcode permission prompt behavior + - Metrics comparison (direct mode vs broker mode process churn) +- **Acceptance Criteria:** + - [x] Sequential short-lived clients reuse one broker-owned upstream bridge process + - [x] Concurrent client tool calls remain stable under load + - [x] Manual prompt criterion resolved in FU-P13-T14 (**FAIL**: broker-mode proxy sessions rejected with `-32003 UID mismatch`) + - [x] Regression suite passes with broker mode enabled + +--- + +#### ✅ P13-T6: Document broker mode configuration, migration, and rollback +- **Status:** ✅ PASS (2026-02-18) +- **Description:** Update setup and troubleshooting docs for broker mode adoption, including client config examples, operational commands, limitations, and rollback to direct mode. +- **Priority:** P1 +- **Dependencies:** P13-T4 +- **Parallelizable:** yes +- **Outputs/Artifacts:** + - Updated `README.md`, `docs/cursor-setup.md`, `docs/claude-setup.md`, `docs/codex-setup.md`, `docs/troubleshooting.md` + - Added `docs/broker-mode.md` deep-dive + - Config templates with broker-mode variants +- **Acceptance Criteria:** + - [x] Docs include one-command start/stop/status flows for broker mode + - [x] Client examples are provided for Codex/Cursor/Claude + - [x] Troubleshooting includes socket/lock and stale-broker recovery + - [x] Rollback steps to direct mode are explicit and tested + +--- + +#### ✅ FU-P13-T7: Enforce strict `structuredContent` compliance for empty-content tool results +- **Description:** Fix transformation logic so strict MCP clients no longer fail when a tool response includes `result.content: []` without `result.structuredContent`. Add a fallback injection strategy for transformable tool results with empty content. +- **Priority:** P0 +- **Dependencies:** P3-T3, P4-T1, P5-T6 +- **Parallelizable:** yes +- **Outputs/Artifacts:** + - Updated `src/mcpbridge_wrapper/transform.py` transformation conditions for empty-content results + - Updated `tests/unit/test_transform.py` coverage for strict empty-content compliance + - Updated troubleshooting/docs note clarifying strict-client behavior +- **Acceptance Criteria:** + - [ ] For tool responses missing `structuredContent`, empty `content` results are normalized to include `structuredContent` fallback + - [ ] Existing already-compliant responses remain unchanged + - [ ] Non-tool JSON-RPC notifications and unrelated payloads are not regressed + - [ ] New unit tests fail before fix and pass after fix + +--- + +#### ✅ FU-P13-T8: Prevent Web UI port collision from destabilizing MCP sessions +- **Description:** Harden startup behavior when `--web-ui` port is already occupied (common with stale/orphan wrapper processes). Ensure collision handling is deterministic and does not silently degrade MCP client stability. +- **Priority:** P0 +- **Dependencies:** P10-T1 +- **Parallelizable:** yes +- **Status:** ✅ Implemented (2026-02-16) +- **Outputs/Artifacts:** + - `run_server()` catches `SystemExit` from uvicorn bind failure (TOCTOU window) + - TOCTOU regression test in `tests/unit/test_main_webui.py` + - Stale-process troubleshooting docs in `docs/troubleshooting.md` (FU-BUG-T6-1) +- **Acceptance Criteria:** + - [x] When requested Web UI port is occupied, wrapper behavior is explicit and deterministic (clear error or safe fallback) + - [x] MCP stdio protocol output remains valid JSON-RPC only on stdout + - [x] Repeated client startups no longer accumulate conflicting Web UI listeners on the same port + - [x] Tests cover occupied-port and restart scenarios + +--- + +#### ✅ FU-P13-T9: Normalize unsupported `resources/*` method failures to standard JSON-RPC errors +- **Description:** Add protocol normalization for non-tool method failures where upstream returns tool-style `result.isError/content` payloads. Convert these into standard JSON-RPC `error` envelopes for strict MCP clients. +- **Priority:** P0 +- **Status:** ✅ Implemented in BUG-T7 (2026-02-14) +- **Dependencies:** P3-T10 +- **Parallelizable:** yes +- **Outputs/Artifacts:** + - Updated response normalization logic in `src/mcpbridge_wrapper/__main__.py` and/or `src/mcpbridge_wrapper/transform.py` + - Request/response correlation support for method-aware normalization + - Regression tests for `resources/list` and `resources/templates/list` compatibility +- **Acceptance Criteria:** + - [x] Unsupported non-tool methods return JSON-RPC `error` responses with stable code/message shape + - [x] Codex/Cursor strict MCP paths no longer report "Unexpected response type" for normalized unsupported methods + - [x] Tool-call success/error behavior remains backward compatible + - [x] Integration tests cover normalization without false positives on valid tool results + +--- + +#### ✅ FU-P13-T10: Implement explicit broker daemon entrypoint and operational CLI flows +- **Description:** Make broker host mode first-class by implementing a real daemon entrypoint (`--broker-daemon` or equivalent broker subcommand) in `__main__.py`, ensuring `--broker-spawn` can reliably auto-start and connect. Replace doc-only one-liner operational flows with supported CLI commands for start/status/stop. +- **Priority:** P0 +- **Dependencies:** P13-T2, P13-T4 +- **Parallelizable:** no +- **Outputs/Artifacts:** + - Updated `src/mcpbridge_wrapper/__main__.py` broker daemon branch and command parsing + - Updated `src/mcpbridge_wrapper/broker/proxy.py` spawn target (if needed) + - Integration test covering `--broker-spawn` end-to-end readiness + - Updated `docs/broker-mode.md` and setup docs with first-class broker host commands +- **Acceptance Criteria:** + - [ ] Running `mcpbridge-wrapper --broker-daemon` starts broker host mode and creates live PID/socket state + - [ ] `--broker-spawn` successfully auto-starts broker and connects without manual bootstrap + - [ ] No broker-only flags are accidentally forwarded to `xcrun mcpbridge` + - [ ] Start/status/stop commands are documented as supported CLI flows (not private inline Python snippets) + +--- + +#### ✅ FU-P13-T11: Preserve JSON-RPC numeric request ID fidelity in broker transport +- **Status:** ✅ Completed (2026-02-19) +- **Description:** Replaced lossy 20-bit integer ID bitmask with a reversible per-session counter (`_alloc_local_id`) and reverse map (`id_restore`). Large, negative, and concurrent integer IDs now round-trip exactly. O(1) restore replaces previous O(n) scan. +- **Priority:** P1 +- **Dependencies:** P13-T3 +- **Parallelizable:** yes +- **Outputs/Artifacts:** + - Updated `src/mcpbridge_wrapper/broker/transport.py` ID remap/restore strategy + - Updated `src/mcpbridge_wrapper/broker/types.py` session mapping fields + - New/updated unit tests for large, negative, and concurrent numeric IDs +- **Acceptance Criteria:** + - [x] Integer IDs (including negative and > 20-bit) are returned unchanged to clients + - [x] Distinct concurrent numeric IDs cannot collide within a session + - [x] Existing string-ID routing behavior remains backward compatible + - [x] Broker transport tests cover ID round-trip fidelity for int and string IDs + +--- + +#### ✅ FU-P13-T12: Enforce local Unix-socket security boundary for broker clients +- **Status:** ✅ Completed (2026-02-19) +- **Description:** Implement same-UID peer credential verification for broker socket clients and enforce owner-only socket permissions, aligning runtime behavior with P13-T1 ADR security decisions. +- **Priority:** P1 +- **Dependencies:** P13-T1, P13-T3 +- **Parallelizable:** yes +- **Outputs/Artifacts:** + - Updated `src/mcpbridge_wrapper/broker/transport.py` peer credential checks and rejection path + - Updated broker socket creation flow to enforce `0600` permissions + - Unit tests for accepted/rejected client credential cases + - Documentation update in `docs/broker-mode.md` and/or `docs/troubleshooting.md` +- **Acceptance Criteria:** + - [x] Broker accepts only same-UID local clients + - [x] Connections failing UID verification are rejected without affecting active sessions + - [x] Broker socket file is owner-readable/writable only (`0600`) + - [x] Security-boundary behavior is documented and test-covered + +--- + +#### ✅ FU-P13-T13: Make broker startup transactional when transport bind/start fails — Completed (2026-02-19) +- **Description:** Harden `BrokerDaemon.start()` so partial startup failures (for example socket bind errors after upstream launch) perform full rollback, leaving no orphaned upstream process or stale PID/socket files. +- **Priority:** P1 +- **Dependencies:** P13-T2, P13-T3 +- **Parallelizable:** yes +- **Outputs/Artifacts:** + - Updated `src/mcpbridge_wrapper/broker/daemon.py` startup failure rollback path + - Regression tests for transport-start failure after upstream launch + - Troubleshooting note for deterministic failure behavior +- **Acceptance Criteria:** + - [x] If transport startup fails, upstream subprocess is terminated and waited + - [x] PID/socket files are cleaned up on startup failure + - [x] Broker state returns to a safe non-ready state after rollback + - [x] Unit tests cover rollback behavior and prevent regression + +--- + +#### ✅ FU-P13-T13-FU-1: Set _stopped_event and _stop_event in _rollback_startup for defensive consistency — Completed (2026-02-19, PASS) +- **Description:** After `_rollback_startup()` sets state to STOPPED, also call `self._stopped_event.set()` and `self._stop_event.set()` so all event states are consistent with the STOPPED contract. These paths are currently unreachable by callers but the defensive fix ensures correctness if future callers are added. +- **Priority:** P3 (Low — optional defensibility improvement) +- **Dependencies:** FU-P13-T13 (✅) +- **Parallelizable:** yes +- **Outputs/Artifacts:** + - Updated `src/mcpbridge_wrapper/broker/daemon.py` `_rollback_startup()` method + - Updated tests asserting event state after rollback +- **Acceptance Criteria:** + - [x] `_stopped_event.set()` called in `_rollback_startup()` + - [x] `_stop_event.set()` called in `_rollback_startup()` + - [x] Tests verify event states are set after a failed startup + +--- + +#### ✅ FU-P13-T14: Complete interactive Xcode prompt verification and close P13-T5 — Completed (2026-02-19, FAIL) +- **Description:** Execute and document the remaining human-run interactive validation for Xcode permission prompts in direct mode vs broker mode, then update P13-T5 verdict and linked acceptance states. +- **Priority:** P1 +- **Dependencies:** P13-T5, P13-T6 +- **Parallelizable:** no +- **Outputs/Artifacts:** + - Updated `SPECS/ARCHIVE/P13-T5_Validate_prompt_reduction_and_multi_client_stability/P13-T5_manual_prompt_validation.md` + - Updated `SPECS/ARCHIVE/P13-T5_Validate_prompt_reduction_and_multi_client_stability/P13-T5_Validation_Report.md` + - Workplan status update for P13-T5 acceptance line items +- **Acceptance Criteria:** + - [x] Interactive desktop run confirms observed prompt behavior for repeated short-lived sessions + - [x] P13-T5 manual prompt criterion is resolved to PASS or FAIL with concrete evidence (resolved to **FAIL**) + - [x] Any discovered deviations are captured in troubleshooting and/or follow-up bug tasks (`FU-P13-T15`) + - [x] BUG-T4 related resolution path is reconciled with the final validation outcome + +--- + +#### ✅ FU-P13-T15: Restore broker same-UID client acceptance when peer credential APIs are unavailable — Completed (2026-02-19, PASS) +- **Description:** Broker mode currently rejects same-user local clients with `-32003 UID mismatch` when peer credential lookup returns `Errno 42 (Protocol not available)`. Implement a platform-safe credential verification fallback that preserves local security boundaries while allowing same-UID clients to connect. +- **Priority:** P1 +- **Dependencies:** FU-P13-T12, FU-P13-T14 +- **Parallelizable:** no +- **Outputs/Artifacts:** + - Updated `src/mcpbridge_wrapper/broker/transport.py` peer credential verification path and fallback handling + - Added/updated tests covering `Errno 42`/unsupported credential API behavior +- **Acceptance Criteria:** + - [x] Same-user local broker clients connect successfully on environments where current credential path returns `Errno 42` + - [x] Cross-UID or unverifiable peers are still rejected with deterministic security errors + - [x] Integration tests for broker multi-client flows pass in supported local environments + +--- + +#### ✅ FU-P13-T16: Document multi-agent MCP usage and single Web UI host +- **Status:** ✅ Completed (2026-02-28, PASS) +- **Description:** Updated multi-agent documentation to clarify Web UI ownership, explain why MCP can remain healthy while the dashboard is unavailable, and document stable multi-agent topologies for both broker mode and direct mode. +- **Priority:** P1 +- **Dependencies:** P13-T6, FU-P13-T8, FU-P13-T10 +- **Parallelizable:** yes +- **Outputs/Artifacts:** + - Updated `README.md` with explicit multi-agent guidance and Web UI ownership notes + - Updated `docs/broker-mode.md` with multi-agent topology details and broker/Web UI behavior notes + - Updated `docs/webui-setup.md` with a dedicated multi-agent ownership model section + - Updated `docs/troubleshooting.md` with diagnostics for reachable MCP + unreachable dashboard states +- **Acceptance Criteria:** + - [x] Documentation states that only one process can bind a given Web UI `host:port` + - [x] Documentation explains why MCP can be healthy while Web UI is unavailable + - [x] Documentation provides a dedicated broker host + `--broker-connect` client pattern + - [x] Troubleshooting includes concrete checks for listener ownership and port conflicts + +--- + +#### ✅ FU-P13-T17: Enable broker-hosted Web UI with shared multi-client telemetry — Completed (2026-02-28, PASS) +- **Status:** ✅ Completed (2026-02-28, PASS) +- **Description:** Implement a broker-mode runtime path where `--broker-daemon --web-ui` starts both the persistent broker and dashboard in one host process, and broker-side request/response telemetry feeds one shared Web UI view for all connected agents. +- **Priority:** P0 +- **Dependencies:** P13-T10, FU-P13-T8 +- **Parallelizable:** no +- **Outputs/Artifacts:** + - Updated `src/mcpbridge_wrapper/__main__.py` to support broker-daemon + Web UI startup orchestration + - Updated `src/mcpbridge_wrapper/broker/transport.py` to record tool telemetry for broker-routed clients + - Updated `src/mcpbridge_wrapper/broker/proxy.py` to propagate Web UI spawn args in `--broker-spawn` flows + - Updated unit tests in `tests/unit/test_main.py`, `tests/unit/test_broker_proxy.py`, and `tests/unit/test_broker_transport.py` +- **Acceptance Criteria:** + - [x] `mcpbridge-wrapper --broker-daemon --web-ui --web-ui-config ` starts broker socket and dashboard without launching direct-mode bridge loop + - [x] `mcpbridge-wrapper --broker-spawn --web-ui --web-ui-config ` can auto-start a broker host with Web UI enabled + - [x] Tool calls from multiple broker-connected clients appear in one dashboard metrics/audit stream + - [x] Existing direct mode and broker-only behavior remain backward compatible + +--- + +#### ✅ FU-P13-T18: Document unified single-config setup for broker + Web UI multi-agent workflows — Completed (2026-02-28, PASS) +- **Status:** ✅ Completed (2026-02-28, PASS) +- **Description:** Update setup and troubleshooting docs so users can apply one MCP config across agents while reusing a shared broker host and one dashboard endpoint. +- **Priority:** P1 +- **Dependencies:** FU-P13-T17 +- **Parallelizable:** yes +- **Outputs/Artifacts:** + - Updated `README.md`, `docs/broker-mode.md`, `docs/webui-setup.md`, and `docs/troubleshooting.md` + - Updated mapped DocC files in `Sources/XcodeMCPWrapper/Documentation.docc/` +- **Acceptance Criteria:** + - [x] Docs include one-config examples for Zed/Cursor/Claude/Codex with broker + dashboard expectations + - [x] Docs clearly define dashboard ownership and fallback behavior + - [x] Troubleshooting includes broker-hosted Web UI diagnostics + +--- + +#### ✅ FU-P13-T19: Add integration coverage for broker-hosted Web UI observability — Completed (2026-02-28, PASS) +- **Status:** ✅ Completed (2026-02-28, PASS) +- **Description:** Added integration coverage validating broker-hosted Web UI observability by exercising multi-client broker traffic and asserting aggregated/error telemetry via `/api/metrics` and `/api/audit`. +- **Priority:** P1 +- **Dependencies:** FU-P13-T17 +- **Parallelizable:** yes +- **Outputs/Artifacts:** + - Added `tests/integration/webui/test_broker_observability.py` + - Added `SPECS/ARCHIVE/FU-P13-T19_Add_integration_coverage_for_broker-hosted_Web_UI_observability/FU-P13-T19_Validation_Report.md` +- **Acceptance Criteria:** + - [x] Tests demonstrate aggregated metrics visibility for broker-connected clients + - [x] Tests cover at least one error-path request and verify error reporting in metrics/audit output + - [x] CI remains stable without flaky timing assumptions + +--- + +### Phase 14: Release 0.4.0 Readiness + +#### ✅ P14-T5: Stabilize broker Unix-socket permission test against path-length limits — Completed (2026-02-20, PASS) +- **Description:** Make the socket-permission regression test deterministic across local environments by avoiding Unix domain socket path overflows in pytest temporary directories, while preserving verification of `0600` permissions. +- **Priority:** P1 +- **Dependencies:** FU-P13-T12 +- **Parallelizable:** yes +- **Outputs/Artifacts:** + - Updated `tests/unit/test_broker_transport.py` socket-permission test setup to use a safe short socket path + - Validation evidence that `pytest -q` passes without requiring `--basetemp` +- **Acceptance Criteria:** + - [x] `tests/unit/test_broker_transport.py::TestSocketPermissions::test_socket_created_with_0600_permissions` passes on macOS with default pytest temp paths + - [x] Full `pytest -q` passes without `AF_UNIX path too long` + - [x] Test still verifies socket mode is exactly `0o600` + +--- + +#### ✅ FU-P14-T5-1: Add macOS CI execution for broker socket-path regression coverage — Completed (2026-02-20, PASS) +- **Description:** Extend GitHub Actions CI with a macOS test path (similar to dedicated workflow lanes such as DocC) so the AF_UNIX path-length-sensitive broker socket test is exercised on macOS runners during PR validation. +- **Priority:** P1 +- **Dependencies:** P14-T5 +- **Parallelizable:** yes +- **Outputs/Artifacts:** + - Updated `.github/workflows/ci.yml` (or a dedicated workflow) to run broker transport tests on `macos-latest` + - CI documentation note describing why macOS coverage is required for AF_UNIX path-length behavior +- **Acceptance Criteria:** + - [x] GitHub Actions runs the broker socket permission/path regression test on a macOS runner for pull requests + - [x] The macOS job status is visible in PR checks and gates merges on failure + - [x] Existing Linux test matrix behavior remains unchanged + +--- + +#### ✅ P14-T1: Bound per-session ID restore maps in broker transport — Completed (2026-02-20, PASS) +- **Description:** Prevent unbounded memory growth in long-lived broker sessions by pruning/removing alias/restore entries once responses are routed (and define safe behavior when local ID space wraps). +- **Priority:** P1 +- **Dependencies:** FU-P13-T11, FU-P13-T15 +- **Parallelizable:** no +- **Outputs/Artifacts:** + - Updated `src/mcpbridge_wrapper/broker/transport.py` map lifecycle management for `id_restore`, `string_id_map`, and `int_id_map` + - Regression tests in `tests/unit/test_broker_transport.py` for long-lived/large-ID request streams +- **Acceptance Criteria:** + - [x] Per-session restore/alias maps do not grow unbounded for completed requests + - [x] Existing ID round-trip fidelity guarantees remain intact for int and string IDs + - [x] Tests cover wrap/prune behavior and pass in CI + +--- + +#### ✅ P14-T3: Reconcile declared Python support with tested matrix — Completed (2026-02-20, PASS) +- **Description:** Resolve mismatch between declared Python compatibility and CI coverage by aligning `requires-python`/classifiers/docs with supported and continuously-tested interpreter versions. +- **Priority:** P1 +- **Dependencies:** none +- **Parallelizable:** yes +- **Outputs/Artifacts:** + - Updated compatibility declarations in `pyproject.toml` and documentation badges/text + - Updated `.github/workflows/ci.yml` matrix and/or version floor to match declared support +- **Acceptance Criteria:** + - [x] Declared Python support exactly matches tested CI versions + - [x] README and packaging metadata communicate the same minimum Python version + - [x] CI passes on the finalized support matrix + +--- + +#### ✅ P14-T4: Replace deprecated setuptools license metadata with SPDX format — Completed (2026-02-20, PASS) +- **Description:** Remove packaging deprecation warnings by migrating license metadata to modern SPDX-based fields and dropping deprecated classifiers/structures. +- **Priority:** P2 +- **Dependencies:** none +- **Parallelizable:** yes +- **Outputs/Artifacts:** + - Updated `pyproject.toml` license metadata (`project.license` / `license-files` / classifiers as needed) + - Validation evidence from `python -m build` showing deprecation warnings are resolved +- **Acceptance Criteria:** + - [x] Build output no longer emits setuptools license deprecation warnings + - [x] Package metadata remains valid for PyPI and MCP registry publication + - [x] Existing `make check` pipeline remains green + +--- + +#### ✅ P14-T2: Align release metadata and changelog for 0.4.0 — Completed (2026-02-20, PASS) +- **Description:** Prepare publishable 0.4.0 release metadata by updating package/registry versions and adding a complete changelog entry matching delivered functionality. +- **Priority:** P1 +- **Dependencies:** P14-T1, P14-T3, P14-T4 +- **Parallelizable:** no +- **Outputs/Artifacts:** + - Updated version fields in `pyproject.toml` and `server.json` + - New `0.4.0` entry in `CHANGELOG.md` with release date, key changes, and release link +- **Acceptance Criteria:** + - [x] `pyproject.toml`, `server.json`, and `CHANGELOG.md` all reference `0.4.0` consistently + - [x] Changelog includes accurate notes for broker and Web UI work shipped since `0.3.2` + - [x] Release metadata passes existing build/publish validation checks + +--- + + +#### ✅ 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 +- **Parallelizable:** yes +- **Outputs/Artifacts:** + - Updated `pending_methods` handling in `src/mcpbridge_wrapper/__main__.py` + - Unit test that exercises high-volume in-flight requests without responses +- **Acceptance Criteria:** + - [x] `pending_methods` does not grow beyond a capped size under any traffic pattern + - [x] Existing BUG-T7 normalization behavior is unaffected + +--- + +#### ✅ FU-P12-T1-1: Remove or document `MCPInitializeParams` in schemas +- **Status:** ✅ Completed (2026-02-18) +- **Description:** `MCPInitializeParams` was added to `schemas.py` during P12-T1 but is not used anywhere in the codebase — `MCPParams.clientInfo` covers the same purpose. Either remove it to reduce confusion, or add a usage (e.g., a helper or test) that justifies its existence as a public export. +- **Priority:** P3 +- **Dependencies:** P12-T1 +- **Parallelizable:** yes +- **Outputs/Artifacts:** + - Updated `src/mcpbridge_wrapper/schemas.py` — `MCPInitializeParams` removed or documented with usage +- **Acceptance Criteria:** + - [x] `MCPInitializeParams` is either removed or has a clear, tested usage + - [x] `pytest` suite remains green + +--- + +#### ✅ FU-P12-T1-2: Add code comment clarifying stdin-only client capture in `on_request` +- **Status:** ✅ Completed (2026-02-18) +- **Description:** In `__main__.py`'s `on_request()`, the `initialize` client info capture only fires for requests arriving on stdin (client→bridge). Add a brief comment clarifying this intentional scope to prevent future confusion about whether stdout initialize messages are also handled. +- **Priority:** P3 +- **Dependencies:** P12-T1 +- **Parallelizable:** yes +- **Outputs/Artifacts:** + - Updated `src/mcpbridge_wrapper/__main__.py` — comment added near client info capture block +- **Acceptance Criteria:** + - [x] Comment clearly states stdin-only capture direction + - [x] No functional changes + +--- + +#### ✅ FU-P12-T1-3: Show multi-client widgets in Web UI instead of single overwritten active client +- **Status:** ✅ Completed (2026-02-18) +- **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:** + - [x] Dashboard shows multiple clients simultaneously when more than one client connects + - [x] Existing single-client behavior remains correct when only one client is present + - [x] Client widgets update in real time with the same refresh cadence as other KPIs + - [x] `pytest` suite remains green + +--- + +#### ✅ FU-P12-T1-5: Cap `_clients` dict and prune `client_identities` to prevent unbounded growth +- **Status:** ✅ Completed (2026-02-19) +- **Description:** The in-memory `_clients` dict in `MetricsCollector` and the `client_identities` SQLite table in `SharedMetricsStore` grow without limit — every unique `(name, version)` pair adds an entry that is never evicted. Add a soft cap (e.g. 50 entries, evict oldest by `last_seen`) to `_clients`, and add a `WHERE last_seen > ?` pruning clause for `client_identities` on write. This aligns with the project pattern established by FU-BUG-T7-1 (`pending_methods` cap). +- **Priority:** P2 +- **Dependencies:** FU-P12-T1-3 +- **Parallelizable:** yes +- **Outputs/Artifacts:** + - Updated `src/mcpbridge_wrapper/webui/metrics.py` — soft cap on `_clients` dict + - Updated `src/mcpbridge_wrapper/webui/shared_metrics.py` — pruning old `client_identities` rows + - Updated tests covering eviction behavior +- **Acceptance Criteria:** + - [x] `_clients` dict never exceeds the configured cap + - [x] Stale `client_identities` rows are pruned on write + - [x] Existing multi-client dashboard behavior is preserved + - [x] `pytest` suite remains green + +--- + +#### ✅ FU-P12-T1-6: Uniform HTML escaping in `renderClientWidgets` +- **Status:** ✅ Completed (2026-02-19) +- **Description:** In `dashboard.js` `renderClientWidgets`, the `count` integer and `lastSeen` string are interpolated into innerHTML without `escapeHtml()`, while `name` and `version` are escaped. Although `count` is always a number and `lastSeen` already passes through `escapeHtml` inside `formatRelativeAge`, the asymmetric pattern makes security auditing harder. Apply `escapeHtml()` uniformly to all interpolated values for consistency. +- **Priority:** P3 +- **Dependencies:** FU-P12-T1-3 +- **Parallelizable:** yes +- **Outputs/Artifacts:** + - Updated `src/mcpbridge_wrapper/webui/static/dashboard.js` — uniform escaping in `renderClientWidgets` +- **Acceptance Criteria:** + - [x] All interpolated values in `renderClientWidgets` are passed through `escapeHtml()` + - [x] No visual regression in client widget rendering + - [x] `pytest` suite remains green + +--- + +#### ✅ FU-P12-T1-4: Make `IN FLIGHT` KPI reflect real in-flight requests in shared-metrics mode +- **Status:** ✅ Completed (2026-02-19) +- **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:** + - [x] `IN FLIGHT` KPI is greater than zero while requests are in progress and returns to zero after responses + - [x] Works correctly with multiple concurrent clients/processes using the shared metrics database + - [x] No regressions in existing dashboard metrics endpoints + - [x] `pytest` suite remains green + +--- + +#### ✅ FU-P12-T3-1: Document unused `error_message` parameter in `MetricsCollector.record_response` +- **Status:** ✅ Completed (2026-02-19) +- **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 +- **Dependencies:** P12-T3 +- **Parallelizable:** yes +- **Outputs/Artifacts:** + - Updated `src/mcpbridge_wrapper/webui/metrics.py` — docstring clarification on `error_message` parameter +- **Acceptance Criteria:** + - [x] Docstring clearly notes `error_message` is accepted for API symmetry but not stored in-memory + - [x] No functional changes + +--- + +#### ✅ FU-P12-T3-2: Add `error_code` column to audit CSV export +- **Status:** ✅ Completed (2026-02-19) +- **Description:** `AuditLogger.export_csv()` uses a fixed column list that does not include `error_code`. Audit entries with error codes will silently omit this field from CSV exports. Add `error_code` to the CSV column list so it is included when present. +- **Priority:** P3 +- **Dependencies:** P12-T3 +- **Parallelizable:** yes +- **Outputs/Artifacts:** + - Updated `src/mcpbridge_wrapper/webui/audit.py` — `error_code` added to CSV export columns + - Updated `tests/unit/webui/test_audit.py` — test verifying `error_code` in CSV output +- **Acceptance Criteria:** + - [x] CSV export includes `error_code` column + - [x] Entries without `error_code` show empty string for the column + - [x] Existing CSV tests still pass + +--- + +#### ✅ FU-P12-T2-1: Fix stacking click event listeners in `updateLatencyTable` +- **Description:** The `tbody.addEventListener("click", ...)` call inside `updateLatencyTable()` in `dashboard.js` is re-registered on every metrics refresh (every 1 second), causing listeners to accumulate. Move the click handler to a one-time setup function called once during dashboard initialization, or guard with a `data-listener-attached` sentinel attribute on the tbody element. +- **Priority:** P3 +- **Dependencies:** P12-T2 +- **Parallelizable:** yes +- **Outputs/Artifacts:** + - Updated `src/mcpbridge_wrapper/webui/static/dashboard.js` — click handler moved out of `updateLatencyTable` +- **Acceptance Criteria:** + - [ ] Click handler on latency tbody is registered exactly once per page load + - [ ] Param pattern expand/collapse still works correctly after fix + - [ ] No regression in existing dashboard behavior + +--- + +#### ✅ FU-P11-T1-1: Refactor `_FakeWebUIConfig` test stub to use `MagicMock(spec=WebUIConfig)` +- **Description:** The hand-rolled `_FakeWebUIConfig` class in `tests/unit/test_main.py` must be manually updated every time a new property is added to `WebUIConfig`. Refactor it to use `MagicMock(spec=WebUIConfig)` with only the properties needed by the test wired up, so the spec auto-enforces the real interface and future property additions do not break the test. +- **Priority:** P3 +- **Dependencies:** P11-T1 +- **Parallelizable:** yes +- **Outputs/Artifacts:** + - Updated `tests/unit/test_main.py` — `_FakeWebUIConfig` replaced with `MagicMock(spec=WebUIConfig)` +- **Acceptance Criteria:** + - [ ] No hand-rolled `_FakeWebUIConfig` class remains in `test_main.py` + - [ ] All existing `test_main.py` tests pass without modification when new `WebUIConfig` properties are added + - [ ] `pytest` suite remains green + +--- + +#### ✅ FU-BUG-T6-1: Document stale-process cleanup for Web UI port collisions +- **Description:** Add a troubleshooting entry explaining how to identify and kill stale wrapper/uvx processes occupying the Web UI port. Include diagnostic commands (e.g., `lsof -i :` or `ps aux | grep mcpbridge`) and cleanup steps. +- **Priority:** P2 +- **Dependencies:** BUG-T6 +- **Parallelizable:** yes +- **Outputs/Artifacts:** + - Updated `docs/troubleshooting.md` with stale-process cleanup section +- **Acceptance Criteria:** + - [x] Troubleshooting entry covers the "port already in use" warning message + - [x] Commands for identifying and killing stale processes are included + - [x] Relates the fix to the BUG-T6 warning text so users can cross-reference + +--- + +### Phase 15: Next Release Readiness Validation + +#### ✅ P15-T1: Validate project readiness for the next release — Completed (2026-02-28, PASS) +- **Status:** ✅ Completed (2026-02-28, PASS) +- **Description:** Executed release-readiness quality gates, packaging/installability checks, and metadata consistency validation; produced a GO recommendation with documented non-blocking risks. +- **Priority:** P1 +- **Dependencies:** none +- **Parallelizable:** no +- **Outputs/Artifacts:** + - Added `SPECS/ARCHIVE/P15-T1_Validate_project_readiness_for_the_next_release/P15-T1_Validation_Report.md` + - Added `SPECS/ARCHIVE/P15-T1_Validate_project_readiness_for_the_next_release/P15-T1_Validate_project_readiness_for_the_next_release.md` + - Updated `SPECS/ARCHIVE/INDEX.md` with archived task and archive log entries +- **Acceptance Criteria:** + - [x] `pytest`, `ruff check src/`, and `mypy src/` are executed and results are captured in the readiness report + - [x] Packaging preflight (`python -m build`) and install smoke tests (`uvx --from ...` and pip install path) are validated or blockers are documented + - [x] Release metadata consistency is verified across `pyproject.toml`, `server.json`, and `CHANGELOG.md` for the target version + - [x] Readiness report includes an explicit go/no-go recommendation and a concrete blocker list when not ready + +--- + +## 4. Dependency Graph + +``` +P1-T1 → P1-T2 → P1-T3 + │ │ │ + │ │ └────→ P1-T5 + │ │ + │ └────────────→ P1-T4 + │ │ + │ └────→ P5-T1 + │ + └────────────────────→ P1-T6 + +P2-T1 → P2-T2 → P2-T4 → P3-T10 + │ │ ▲ │ + │ │ │ ▼ + │ │ P2-T3 → P3-T1 → P3-T2 → P3-T3 → P3-T4 → P3-T5 → P3-T6 → P3-T7 + │ │ │ │ │ │ │ │ + │ │ │ │ │ │ │ │ + │ │ ▼ ▼ ▼ ▼ ▼ ▼ + │ └─────────────────────────────────────────────────────────────────→ P4-T1 + │ │ │ │ │ │ │ │ │ + │ ▼ ▼ ▼ ▼ ▼ ▼ ▼ ▼ + │ P4-T4 P4-T2 P4-T3 P4-T4 P4-T8 P4-T9 (done) (done) + │ + ├──────────────────────────────────────────────────────────────────────────→ P2-T5 + ├──────────────────────────────────────────────────────────────────────────→ P2-T6 + ├──────────────────────────────────────────────────────────────────────────→ P2-T7 + │ + └──────────────────────────────────────────────────────────────────────────→ P5-T10 + +P3-T8 ─────────────────────────────────────────────────────────────────────→ P3-T10 +P3-T9 ─────────────────────────────────────────────────────────────────────→ P3-T10 + +P5-T1 → P5-T2, P5-T3, P5-T4, P5-T5, P5-T6, P5-T7, P5-T8, P5-T9 (parallel) + +P5-T10 → P5-T11 + │ + └────→ P5-T12 → P5-T13 + +P3-T10, P4-T1-P4-T9 → P6-T1 → P6-T2 → P6-T3 + │ │ │ + │ │ └────────────────────────────────────────────→ P7-T1 + │ │ + │ ├────────────────────────────────────────────────────→ P6-T4 → P7-T2 + │ ├────────────────────────────────────────────────────→ P6-T5 → P7-T3 + │ ├────────────────────────────────────────────────────→ P6-T6 → P7-T4 + │ └────────────────────────────────────────────────────→ P6-T7 + +P2-T1 → P7-T5 +P3-T10 → P7-T8 +P5-T13 → P7-T7, P7-T9 +P7-T1, P7-T2, P7-T6 → P7-T10 → P7-T11 +``` + +--- + +## 5. Traceability Matrix + +| PRD Section | Requirement | Task IDs | +|-------------|-------------|----------| +| §1.2 D1 | mcpbridge-wrapper script | P6-T1, P6-T2 | +| §1.2 D2 | Installation documentation | P7-T1 | +| §1.2 D3 | Configuration examples | P6-T4, P6-T5, P6-T6, P7-T2, P7-T3, P7-T4 | +| §1.3 S1 | Cursor compatibility | P5-T13 | +| §1.3 S2 | Transparency | P5-T10, P5-T12 | +| §1.3 S3 | Performance <5ms | P3-T10, P5-T11 | +| §1.3 S4 | 100% valid JSON success | P3-T2, P4-T7, P5-T2-P5-T7 | +| §3.1 FR1 | Intercept all stdout | P2-T3, P2-T4 | +| §3.1 FR2 | Forward stdin unmodified | P2-T2 | +| §3.1 FR3 | Parse JSON responses | P3-T1, P3-T2 | +| §3.1 FR4 | Detect missing structuredContent | P3-T3, P4-T3 | +| §3.1 FR5 | Extract text from content | P3-T4, P4-T2 | +| §3.1 FR6 | Parse text as JSON | P3-T5 | +| §3.1 FR7 | Fallback wrapper | P3-T6, P5-T4 | +| §3.1 FR8 | Passthrough non-JSON | P3-T8, P5-T5 | +| §3.1 FR9 | Unbuffered output | P3-T9 | +| §3.1 FR10 | Concurrent bidirectional I/O | P2-T4 | +| §3.1 NFR1 | Latency <5ms | P3-T10, P5-T11 | +| §3.1 NFR2 | Memory <10MB | P4-T9 | +| §5.1 | Error handling scenarios | P4-T1, P4-T5, P4-T6, P4-T7 | +| §5.2 | Edge cases EC1-EC4 | P5-T8, P5-T9 | +| §7.1 | Unit test cases TC1-TC6 | P5-T2-P5-T7 | +| §7.2 | Integration test IT1-IT4 | P5-T10, P5-T12, P5-T13 | + +--- + +## 6. Execution Checklist + +**Status: ✅ COMPLETE - 2026-02-08** + +Before starting implementation, verify: +- [x] Xcode 26.3+ is available for testing (P5-T12) - documented as manual test +- [x] Python 3.7+ is installed - verified 3.10.19 +- [x] Target `~/bin` directory is writable - install script handles this + +During execution: +- [x] P5-T12 (real Xcode test) documented as manual test procedure +- [x] P5-T11 (performance) passed - 0.0023ms avg (well under 5ms) +- [x] All P0 tasks complete - 100% (25/25 tasks) + +Completion criteria: +- [x] All P0 tasks: 100% complete (25/25) +- [x] All P1 tasks: 100% complete (29/29) +- [x] P5-T14 coverage: 98.2% (exceeds 90% requirement) +- [x] P5-T13: All 20 tools documented for manual verification +- [x] P5-T11: <5ms overhead verified (0.0023ms avg) + +Post-Completion Validation: +- [x] P8-T3 validated: Installation with new path `xcodemcpwrapper` tested successfully +- [x] Client compatibility verified: Zed Agent ✅, Cursor ✅, Claude Code ✅, Codex CLI ✅ +- [x] FU-REBUILD-P10-T1-4 completed: Web UI argument examples documented for Zed, Cursor, Claude Code, and Codex CLI (2026-02-11) +- [ ] Known issue documented: Kimi CLI v1.9.0 has MCP connection issues (BUG-T1) + +Phase 10: Web UI Dashboard +- [x] P10-T1: Web UI Control & Audit Dashboard (P1) +- [x] P10-T2: Fix Web UI timeseries charts showing no data +- [x] P10-T3: Recover main branch after accidental Web UI merge (P0) +- [x] REBUILD-P10-T1: Spec-driven rebuild package for Web UI feature + +Rebuild Follow-up Backlog +- [x] FU-REBUILD-P10-T1-1: Align websocket auth flow between backend and dashboard client (P2) +- [x] FU-REBUILD-P10-T1-2: Add explicit CLI validation/error messaging for invalid --web-ui-port values (P2) +- [x] FU-REBUILD-P10-T1-3: Reconcile docs/webui-setup.md env variable guidance with runtime behavior (P2) +- [x] FU-REBUILD-P10-T1-4: Add Web UI argument examples for client configs (Zed, Cursor, Claude Code, Codex CLI), including `--web-ui` and `--web-ui-port` usage (P2) +- [x] FU-REBUILD-P10-T1-5: Validate and fix documentation paths for local-running MCP server with Web UI (P1) +- [x] FU-REBUILD-P10-T1-6: Fix uninstall.sh package detection/removal asymmetry and venv cleanup (P2) +- [x] FU-REBUILD-P10-T1-7: Include Web UI static assets in published package artifacts (P1) + +--- + +#### ✅ FU-REBUILD-P10-T1-6: Fix uninstall.sh package detection/removal asymmetry and venv cleanup + +**Description:** +`scripts/uninstall.sh` has a logic mismatch between detection and removal. Detection checks for both `mcpbridge-wrapper` and `xcodemcpwrapper` pip packages (line 78: `pip3 show mcpbridge-wrapper || pip3 show xcodemcpwrapper`), but the actual uninstall step (line 133) only runs `pip3 uninstall mcpbridge-wrapper -y`. If only `xcodemcpwrapper` were installed as a pip package, the script reports it exists but then tries to uninstall the wrong name. The dry-run output (line 98) also only shows `mcpbridge-wrapper` info. + +Additionally, now that `install.sh` creates a `.venv` and embeds the venv Python path in `~/bin/xcodemcpwrapper`, the uninstall script should be updated to handle venv cleanup symmetrically. + +**Priority:** P2 + +**Dependencies:** FU-REBUILD-P10-T1-5 + +**Parallelizable:** yes + +**Problem Analysis:** + +1. **Detection/removal asymmetry:** Detection checks two package names but removal only targets one. If `xcodemcpwrapper` is the installed pip name, `pip3 uninstall mcpbridge-wrapper` silently fails or errors. + +2. **Dry-run output incomplete:** `pip3 show mcpbridge-wrapper` in dry-run may show nothing even though `xcodemcpwrapper` package is installed — misleading output. + +3. **No venv awareness:** After FU-REBUILD-P10-T1-5, `install.sh` now creates a `.venv`. The uninstall script should offer to clean up the venv or at minimum inform the user about it. + +**Affected Files:** +- `scripts/uninstall.sh` + +**Acceptance Criteria:** +- [ ] Detection and removal are symmetric: uninstall whichever package name is actually installed (or both) +- [ ] Dry-run output accurately reflects which package(s) would be removed +- [ ] Script handles the case where package is installed inside a project `.venv` +- [ ] Existing UX preserved: dry-run, --yes, confirmation flow, clean output + +--- + +#### ✅ FU-REBUILD-P10-T1-5: Validate and fix documentation paths for local-running MCP server with Web UI + +**Description:** +Documentation for the "manual installation" / "local running" scenario contains incorrect or misleading paths to the `mcpbridge-wrapper` executable. When a user follows the recommended development setup (creating a `.venv` virtual environment), the package entry point is installed at `.venv/bin/mcpbridge-wrapper`, but the documentation and configuration examples reference `~/bin/xcodemcpwrapper` (a shell wrapper that calls `python3 -m mcpbridge_wrapper` using the system Python, which may not have the package installed). + +**Priority:** P1 + +**Dependencies:** FU-REBUILD-P10-T1-4 + +**Parallelizable:** yes + +**Problem Analysis:** + +1. **`install.sh` runs `pip3 install -e .` without activating a venv:** On modern macOS with Homebrew Python, this fails due to PEP 668 (`externally-managed-environment`). The README correctly tells users to create a venv first, but the install script does not use one. + +2. **`~/bin/xcodemcpwrapper` wrapper uses system `python3`:** The generated shell script at `~/bin/xcodemcpwrapper` calls `exec python3 -m mcpbridge_wrapper "$@"`. If the package was installed inside `.venv/`, the system `python3` cannot find the `mcpbridge_wrapper` module. + +3. **DocC Installation Method 4 is broken:** `Sources/XcodeMCPWrapper/Documentation.docc/Installation.md` suggests `cp src/mcpbridge_wrapper/cli.py ~/bin/xcodemcpwrapper`. This single-file copy cannot work because `cli.py` imports from other modules in the `mcpbridge_wrapper` package. + +4. **Configuration examples for manual + Web UI use wrong path:** All config templates (cursor-mcp.json, zed-agent.json, claude-code.txt, codex-cli.txt) and all documentation files show `/Users/YOUR_USERNAME/bin/xcodemcpwrapper` for manual installation. For users who set up via venv (as recommended), the correct path should reference the venv entry point, e.g. `/path/to/XcodeMCPWrapper/.venv/bin/mcpbridge-wrapper`. + +**Affected Files:** +- `scripts/install.sh` - Needs venv-aware installation or correct system-level install +- `config/cursor-mcp.json` - Manual path options +- `config/zed-agent.json` - Manual path options +- `config/claude-code.txt` - Manual path examples +- `config/codex-cli.txt` - Manual path examples +- `README.md` - Manual installation configuration sections +- `docs/installation.md` - Installation methods and paths +- `docs/cursor-setup.md` - Manual installation option +- `docs/claude-setup.md` - Manual installation option +- `docs/codex-setup.md` - Manual installation option +- `docs/webui-setup.md` - Web UI usage examples with manual paths +- `Sources/XcodeMCPWrapper/Documentation.docc/Installation.md` - Method 4 broken copy command +- `Sources/XcodeMCPWrapper/Documentation.docc/CursorSetup.md` - Manual path +- `Sources/XcodeMCPWrapper/Documentation.docc/ClaudeCodeSetup.md` - Manual path +- `Sources/XcodeMCPWrapper/Documentation.docc/CodexCLISetup.md` - Manual path + +**Outputs/Artifacts:** +- Fixed `scripts/install.sh` - Either activate venv before pip install, or use the venv Python in the wrapper script +- Updated configuration templates with correct venv-based path option for local development +- Updated all documentation with a clear "Option: Local Development" section showing `.venv/bin/mcpbridge-wrapper` path +- Fixed DocC Installation Method 4 (remove broken single-file copy or replace with correct instructions) +- Validation report confirming all documented paths work end-to-end + +**Acceptance Criteria:** +- [ ] `scripts/install.sh` produces a working `xcodemcpwrapper` that can find the `mcpbridge_wrapper` module (either via venv-aware wrapper or correct system install) +- [ ] Configuration examples include a "local development" option with venv path: `/.venv/bin/mcpbridge-wrapper` +- [ ] Web UI examples for local development use the correct venv path: `/.venv/bin/mcpbridge-wrapper --web-ui --web-ui-port 8080` +- [ ] DocC Installation Method 4 either works correctly or is removed/replaced +- [ ] All existing uvx and pip installation paths remain unchanged and correct +- [ ] All documentation is consistent between README, docs/, config/, and DocC sources +- [ ] A new user following the development setup instructions can successfully run the MCP server locally with Web UI + +--- + +#### ✅ FU-REBUILD-P10-T1-7: Include Web UI static assets in published package artifacts + +**Description:** +Users running the published package via `uvx --from mcpbridge-wrapper[webui] mcpbridge-wrapper --web-ui` can start the dashboard server, but `http://localhost:8080` renders: + +`XcodeMCPWrapper Dashboard` / `Static files not found.` + +Root cause is packaging: the released wheel includes Python modules under `mcpbridge_wrapper/webui/` but omits frontend assets under `mcpbridge_wrapper/webui/static/` (`index.html`, `dashboard.css`, `dashboard.js`). The server falls back to the placeholder HTML when `index.html` is missing. + +**Priority:** P1 + +**Dependencies:** P10-T1, P9-T3 + +**Parallelizable:** yes + +**Outputs/Artifacts:** +- Updated packaging config to include `src/mcpbridge_wrapper/webui/static/*` in wheel/sdist artifacts +- Regression test(s) that fail if dashboard static assets are missing at runtime +- Updated troubleshooting docs with explicit symptom/cause for missing static assets (until patched release is published) +- Patch release plan entry (next version after `0.3.0`) noting Web UI packaging fix + +**Acceptance Criteria:** +- [ ] Built wheel contains: + - `mcpbridge_wrapper/webui/static/index.html` + - `mcpbridge_wrapper/webui/static/dashboard.css` + - `mcpbridge_wrapper/webui/static/dashboard.js` +- [ ] `uvx --from mcpbridge-wrapper[webui] mcpbridge-wrapper --web-ui --web-ui-port 8080` serves full dashboard UI (not fallback "Static files not found.") +- [ ] Automated tests cover dashboard HTML serving path and fail on missing static assets +- [ ] Release notes/changelog clearly call out this fix for Web UI users diff --git a/SPECS/Workplan.md b/SPECS/Workplan.md index 8961db96..9bcd1dda 100644 --- a/SPECS/Workplan.md +++ b/SPECS/Workplan.md @@ -1,3210 +1,12 @@ # Workplan: mcpbridge-wrapper -## 1. Overview +## Archived Baseline -### 1.1 Goal -Create a Python-based protocol compatibility wrapper that intercepts MCP responses from Xcode 26.3's `xcrun mcpbridge` and transforms non-compliant responses into MCP-spec-compliant format by injecting the required `structuredContent` field. +The previous workplan for release `0.4.0` was archived at: -### 1.2 Key Assumptions (from PRD §1.4) -- Xcode 26.3+ is installed with MCP bridge enabled -- Python 3.7+ is available (standard macOS installation) -- Target users are developers using Cursor or other strict MCP-spec-compliant clients -- Xcode must be running with a project open for tools to function +- [Workplan_0.4.0.md](ARCHIVE/_Historical/Workplan_0.4.0.md) -### 1.3 Constraints (from PRD §1.4) -- Wrapper must be a single executable Python script for easy distribution -- Response latency overhead must be < 5ms per request (NFR1) -- Memory footprint must be < 10MB (NFR2) -- Must handle concurrent bidirectional I/O (FR10) +## Current Cycle -### 1.4 Non-Goals -- Do NOT modify Xcode's mcpbridge binary or internal behavior -- Do NOT implement caching or response buffering beyond line-level -- Do NOT create a GUI or interactive configuration tool -- Do NOT support Python versions below 3.7 -- Do NOT implement the 20 Xcode MCP tools (only wrap the existing bridge) - ---- - -## 2. Phases - -### Phase 1: Foundation & Scaffolding -**Intent:** Establish project structure, Python packaging, and development tooling to support implementation and testing. - -### Phase 2: Core Bridge Implementation -**Intent:** Implement the subprocess wrapper around `xcrun mcpbridge` with bidirectional stdin/stdout piping and async I/O handling. - -### Phase 3: Response Transformation Engine -**Intent:** Implement JSON parsing, MCP compliance detection, and the `structuredContent` injection logic per PRD §3.1 Functional Requirements. - -### Phase 4: Edge Case Handling -**Intent:** Ensure robust handling of all failure scenarios and edge cases documented in PRD §5.1-5.2. - -### Phase 5: Testing & Verification -**Intent:** Validate all functional requirements, non-functional requirements, and success criteria through comprehensive unit and integration tests. - -### Phase 6: Packaging & Distribution -**Intent:** Create installable artifacts and configuration templates for Cursor, Claude Code, and Codex CLI. - -### Phase 7: Documentation -**Intent:** Produce user-facing documentation for installation, configuration, and troubleshooting. - -### Phase 10: Web UI Control & Audit Dashboard -**Intent:** Create a web-based dashboard for real-time monitoring, control, and audit logging of the XcodeMCPWrapper. - -### Phase 11: Web UI UX Improvements -**Intent:** Enhance the dashboard with better debugging tools, session awareness, theming, and keyboard-driven workflows. - -### Phase 12: Data Collection Enhancements -**Intent:** Enrich collected telemetry with client identity, parameter patterns, and structured error classification for deeper operational insight. - -### Phase 13: Persistent Broker & Shared Xcode Session -**Intent:** Introduce a long-lived broker connection to Xcode MCP so multiple short-lived clients can reuse one upstream session and reduce repeated Xcode permission prompts. - -### Phase 14: Release 0.4.0 Readiness -**Intent:** Close release-blocking findings from the 0.4.0 readiness review across broker runtime safety, package metadata, versioning, and build compliance. - -### Phase 15: Next Release Readiness Validation -**Intent:** Validate release readiness for the next version with full quality-gate, packaging, and metadata checks before publishing. - ---- - -## 3. Tasks - -### Phase 1: Foundation & Scaffolding - -#### ✅ P1-T1: Create Project Directory Structure -- **Description:** Create `src/mcpbridge_wrapper/`, `tests/unit/`, `tests/integration/`, and `scripts/` directories -- **Priority:** P0 -- **Dependencies:** none -- **Parallelizable:** no -- **Outputs/Artifacts:** - - Directory tree structure - - `src/mcpbridge_wrapper/__init__.py` (empty) -- **Acceptance Criteria:** All directories exist and are importable as Python packages - -#### ✅ P1-T2: Initialize Python Project with pyproject.toml -- **Description:** Create `pyproject.toml` with project metadata, Python 3.7+ requirement, and build system configuration -- **Priority:** P0 -- **Dependencies:** P1-T1 -- **Parallelizable:** no -- **Outputs/Artifacts:** - - `pyproject.toml` with [project], [build-system], and [project.scripts] sections -- **Acceptance Criteria:** `pip install -e .` succeeds and installs the package - -#### ✅ P1-T3: Configure Linting and Formatting Tools -- **Description:** Add ruff configuration for linting/formatting and mypy for type checking in pyproject.toml -- **Priority:** P1 -- **Dependencies:** P1-T2 -- **Parallelizable:** yes -- **Outputs/Artifacts:** - - Linting rules in `pyproject.toml` - - `.pre-commit-config.yaml` (optional) -- **Acceptance Criteria:** `ruff check src/` runs without configuration errors - -#### ✅ P1-T4: Set up pytest Configuration -- **Description:** Configure pytest with coverage reporting in pyproject.toml -- **Priority:** P0 -- **Dependencies:** P1-T2 -- **Parallelizable:** yes -- **Outputs/Artifacts:** - - `[tool.pytest.ini_options]` and `[tool.coverage.run]` in pyproject.toml -- **Acceptance Criteria:** `pytest --version` reads config without errors; `pytest` runs (even with 0 tests) - -#### ✅ P1-T5: Create Makefile with Common Tasks -- **Description:** Add Makefile targets for test, lint, format, typecheck, and install -- **Priority:** P2 -- **Dependencies:** P1-T3, P1-T4 -- **Parallelizable:** yes -- **Outputs/Artifacts:** - - `Makefile` with targets: test, lint, format, typecheck, install, clean -- **Acceptance Criteria:** `make test` runs pytest; `make lint` runs ruff check - -#### ✅ P1-T6: Add Python .gitignore -- **Description:** Create .gitignore with standard Python patterns (venv, __pycache__, *.pyc, etc.) -- **Priority:** P1 -- **Dependencies:** P1-T1 -- **Parallelizable:** yes -- **Outputs/Artifacts:** - - `.gitignore` file -- **Acceptance Criteria:** `git status` does not show Python cache files or virtual environment directories - ---- - -### Phase 2: Core Bridge Implementation - -#### ✅ P2-T1: Implement Subprocess Bridge to xcrun mcpbridge -- **Description:** Create subprocess.Popen wrapper that launches `xcrun mcpbridge` with stdin/stdout pipes per PRD §3.1 FR1-FR2 -- **Priority:** P0 -- **Dependencies:** P1-T1 -- **Parallelizable:** no -- **Outputs/Artifacts:** - - `src/mcpbridge_wrapper/bridge.py` with `create_bridge()` function -- **Acceptance Criteria:** Function returns a Popen object with readable stdout and writable stdin; process starts without errors when Xcode is running - -#### ✅ P2-T2: Implement Stdin Forwarding Loop -- **Description:** Forward all stdin lines from wrapper process to mcpbridge stdin unmodified per PRD §3.1 FR2 -- **Priority:** P0 -- **Dependencies:** P2-T1 -- **Parallelizable:** no -- **Outputs/Artifacts:** - - `forward_stdin()` function in `bridge.py` -- **Acceptance Criteria:** Raw bytes from sys.stdin appear identically on bridge.stdin; manual test with echo confirms passthrough - -#### ✅ P2-T3: Implement Stdout Capture with Line Buffering -- **Description:** Read stdout from bridge line-by-line with bufsize=1 (line buffering) per PRD §3.1 FR9 -- **Priority:** P0 -- **Dependencies:** P2-T1 -- **Parallelizable:** no -- **Outputs/Artifacts:** - - `read_stdout()` generator function in `bridge.py` -- **Acceptance Criteria:** Each yielded item is a complete line (ends with newline); no partial line buffering issues - -#### ✅ P2-T4: Add Daemon Thread for Async Stdout Reading -- **Description:** Spawn daemon thread that runs stdout reader to prevent blocking main thread per PRD §3.1 FR10 -- **Priority:** P0 -- **Dependencies:** P2-T3 -- **Parallelizable:** no -- **Outputs/Artifacts:** - - Thread spawning logic in `bridge.py` - - Queue for thread-safe line passing -- **Acceptance Criteria:** Main thread can continue processing while stdout is being read; thread terminates when bridge exits - -#### ✅ P2-T5: Implement Stderr Passthrough -- **Description:** Pass stderr from bridge directly to wrapper's stderr without modification -- **Priority:** P1 -- **Dependencies:** P2-T1 -- **Parallelizable:** yes -- **Outputs/Artifacts:** - - stderr forwarding in subprocess.Popen call -- **Acceptance Criteria:** Error messages from mcpbridge appear on terminal immediately - -#### ✅ P2-T6: Handle Bridge Process Lifecycle -- **Description:** Implement startup verification, clean shutdown on exit, and exit code propagation -- **Priority:** P1 -- **Dependencies:** P2-T1 -- **Parallelizable:** yes -- **Outputs/Artifacts:** - - `cleanup()` function; exit code handling in main -- **Acceptance Criteria:** Wrapper exits with same code as mcpbridge; no zombie processes left - -#### ✅ P2-T7: Forward Command-Line Arguments -- **Description:** Pass sys.argv[1:] to mcpbridge subprocess to support any bridge arguments -- **Priority:** P1 -- **Dependencies:** P2-T1 -- **Parallelizable:** yes -- **Outputs/Artifacts:** - - Argument forwarding in Popen args list -- **Acceptance Criteria:** Running `wrapper --help` shows mcpbridge help output - ---- - -### Phase 3: Response Transformation Engine - -#### ✅ P3-T1: Implement JSON Detection Logic -- **Description:** Detect whether a line is valid JSON or plain text per PRD §3.1 FR3 -- **Priority:** P0 -- **Dependencies:** P2-T3 -- **Parallelizable:** no -- **Outputs/Artifacts:** - - `is_json_line()` function in `src/mcpbridge_wrapper/transform.py` -- **Acceptance Criteria:** Returns True for `{"key": "value"}`; Returns False for `Plain text log` - -#### ✅ P3-T2: Implement JSON Parsing with Error Handling -- **Description:** Parse JSON lines with try/except; re-raise or handle decode errors per PRD §3.1 FR3 -- **Priority:** P0 -- **Dependencies:** P3-T1 -- **Parallelizable:** no -- **Outputs/Artifacts:** - - `parse_json_safe()` function returning (success, data) tuple -- **Acceptance Criteria:** Valid JSON returns (True, dict); Invalid JSON returns (False, original_line) - -#### ✅ P3-T3: Detect Non-Compliant Responses -- **Description:** Identify responses with `content` field but missing `structuredContent` per PRD §3.1 FR4 -- **Priority:** P0 -- **Dependencies:** P3-T2 -- **Parallelizable:** no -- **Outputs/Artifacts:** - - `needs_transformation()` function checking result object structure -- **Acceptance Criteria:** Returns True for `{"result": {"content": []}}`; Returns False if `structuredContent` exists - -#### ✅ P3-T4: Extract Text from Content Array -- **Description:** Find first content item with `type: "text"` and extract its `text` field per PRD §3.1 FR5 -- **Priority:** P0 -- **Dependencies:** P3-T3 -- **Parallelizable:** no -- **Outputs/Artifacts:** - - `extract_text_content()` function -- **Acceptance Criteria:** Given `[{"type": "image"}, {"type": "text", "text": "data"}]`, returns `"data"`; returns None if no text items - -#### ✅ P3-T5: Parse Extracted Text as JSON -- **Description:** Attempt to parse extracted text content as JSON object per PRD §3.1 FR6 -- **Priority:** P0 -- **Dependencies:** P3-T4 -- **Parallelizable:** no -- **Outputs/Artifacts:** - - `parse_structured_content()` function -- **Acceptance Criteria:** `{"result": true}` string becomes dict; `"plain string"` becomes string primitive; invalid JSON raises exception - -#### ✅ P3-T6: Implement Fallback Wrapper for Invalid JSON -- **Description:** On JSON decode error, wrap text in `{"text": content}` structure per PRD §3.1 FR7 -- **Priority:** P1 -- **Dependencies:** P3-T5 -- **Parallelizable:** no -- **Outputs/Artifacts:** - - Fallback logic in `parse_structured_content()` or caller -- **Acceptance Criteria:** Non-JSON text `"error message"` becomes `{"text": "error message"}` - -#### ✅ P3-T7: Inject structuredContent into Result -- **Description:** Add `structuredContent` field to result object with parsed JSON value per PRD §3.1 FR6-FR7 -- **Priority:** P0 -- **Dependencies:** P3-T5, P3-T6 -- **Parallelizable:** no -- **Outputs/Artifacts:** - - `inject_structured_content()` function -- **Acceptance Criteria:** Input `{"result": {"content": [{"text": "{}"}]}}` becomes `{"result": {"content": [...], "structuredContent": {}}}` - -#### ✅ P3-T8: Implement Non-JSON Output Passthrough -- **Description:** Pass through non-JSON lines (logs, errors) unmodified per PRD §3.1 FR8 -- **Priority:** P1 -- **Dependencies:** P3-T1 -- **Parallelizable:** yes -- **Outputs/Artifacts:** - - Branch in main processing loop for passthrough -- **Acceptance Criteria:** Plain text lines appear on stdout unchanged and unwrapped - -#### ✅ P3-T9: Implement Unbuffered Output -- **Description:** Use `flush=True` on all stdout write operations per PRD §3.1 FR9 -- **Priority:** P0 -- **Dependencies:** P3-T7, P3-T8 -- **Parallelizable:** yes -- **Outputs/Artifacts:** - - `print(..., flush=True)` or `sys.stdout.write()` with explicit flush -- **Acceptance Criteria:** Responses appear immediately (no buffering delay visible) - -#### ✅ P3-T10: Implement Main Response Processing Loop -- **Description:** Combine all transformation components into line_processor function per PRD §4.2 -- **Priority:** P0 -- **Dependencies:** P2-T4, P3-T7, P3-T8, P3-T9 -- **Parallelizable:** no -- **Outputs/Artifacts:** - - `process_response_line()` function - - `main()` entry point in `src/mcpbridge_wrapper/__main__.py` -- **Acceptance Criteria:** End-to-end: stdin → bridge → transform → stdout; all PRD test cases pass - ---- - -### Phase 4: Edge Case Handling - -#### ✅ P4-T1: Handle Empty Content Array -- **Description:** Pass through responses with `"content": []` without modification per PRD §5.1 -- **Priority:** P1 -- **Dependencies:** P3-T3 -- **Parallelizable:** yes -- **Outputs/Artifacts:** - - Empty list check in `needs_transformation()` -- **Acceptance Criteria:** `{"result": {"content": []}}` is passed through unchanged - -#### ✅ P4-T2: Handle Content with No Text Items -- **Description:** Pass through responses with only image or non-text content types per PRD §5.2 EC3 -- **Priority:** P1 -- **Dependencies:** P3-T4 -- **Parallelizable:** yes -- **Outputs/Artifacts:** - - None-found handling in `extract_text_content()` -- **Acceptance Criteria:** `[{"type": "image", "url": "..."}]` results in no transformation - -#### ✅ P4-T3: Handle Already Compliant Responses -- **Description:** Pass through responses that already have `structuredContent` field per PRD §5.2 EC2 -- **Priority:** P1 -- **Dependencies:** P3-T3 -- **Parallelizable:** yes -- **Outputs/Artifacts:** - - Presence check in `needs_transformation()` -- **Acceptance Criteria:** `{"structuredContent": {...}}` responses are not modified - -#### ✅ P4-T4: Handle Responses Without Result Field -- **Description:** Pass through JSON objects that don't have a `result` key (notifications, errors) -- **Priority:** P1 -- **Dependencies:** P3-T3 -- **Parallelizable:** yes -- **Outputs/Artifacts:** - - Field existence check in `needs_transformation()` -- **Acceptance Criteria:** `{"id": 1, "error": null}` is passed through unchanged - -#### ✅ P4-T5: Handle Bridge Process Crash -- **Description:** Detect bridge process termination and exit with same exit code per PRD §5.1 -- **Priority:** P1 -- **Dependencies:** P2-T6 -- **Parallelizable:** yes -- **Outputs/Artifacts:** - - Exit code propagation in main loop -- **Acceptance Criteria:** When mcpbridge exits with code 1, wrapper also exits with code 1 - -#### ✅ P4-T6: Handle Client Disconnect -- **Description:** Cleanly shutdown when stdin closes (client disconnects) per PRD §5.1 -- **Priority:** P1 -- **Dependencies:** P2-T2 -- **Parallelizable:** yes -- **Outputs/Artifacts:** - - EOF detection in stdin forwarding loop (already implemented) - - Validation report confirming graceful shutdown -- **Acceptance Criteria:** Wrapper terminates gracefully when stdin pipe is closed - -#### ✅ P4-T7: Handle Malformed JSON from Bridge -- **Description:** Pass through unparseable JSON lines unchanged per PRD §5.1 -- **Priority:** P1 -- **Dependencies:** P3-T2 -- **Parallelizable:** yes -- **Outputs/Artifacts:** - - Exception handling returning original line -- **Acceptance Criteria:** Partial JSON `{"broken` is output exactly as received - -#### ✅ P4-T8: Handle Nested JSON String Content -- **Description:** Correctly handle text content that is a valid JSON string primitive per PRD §5.2 EC4 -- **Priority:** P2 -- **Dependencies:** P3-T5 -- **Parallelizable:** yes -- **Outputs/Artifacts:** - - Test case in test suite -- **Acceptance Criteria:** Text `"plain string"` becomes `structuredContent: "plain string"` (not error) - -#### ✅ P4-T9: Handle Very Large JSON Responses -- **Description:** Ensure memory-efficient processing for large JSON payloads (>1MB) -- **Priority:** P2 -- **Dependencies:** P2-T3 -- **Parallelizable:** yes -- **Outputs/Artifacts:** - - Line-based processing (no buffering entire response) -- **Acceptance Criteria:** Can process 10MB JSON line without MemoryError; memory stays <10MB - ---- - -### Phase 5: Testing & Verification - -#### ✅ P5-T1: Create Unit Test Framework -- **Description:** Set up pytest structure with fixtures for common test data -- **Priority:** P0 -- **Dependencies:** P1-T4 -- **Parallelizable:** no -- **Outputs/Artifacts:** - - `tests/unit/__init__.py`, `conftest.py` with fixtures -- **Acceptance Criteria:** `pytest tests/unit` runs without import errors -- **Status:** COMPLETED (2026-02-08) - -#### ✅ P5-T2: Write Test for Valid Transformation (TC1) -- **Description:** Test response with content, no structuredContent gets injected per PRD §7.1 TC1 -- **Priority:** P0 -- **Dependencies:** P3-T7, P5-T1 -- **Parallelizable:** yes -- **Outputs/Artifacts:** - - `tests/unit/test_transform.py::test_valid_transformation` -- **Acceptance Criteria:** Test passes; coverage includes `process_response_line` - -#### ✅ P5-T3: Write Test for Already Compliant Response (TC2) -- **Description:** Test response with both fields remains unmodified per PRD §7.1 TC2 -- **Priority:** P0 -- **Dependencies:** P4-T3, P5-T1 -- **Parallelizable:** yes -- **Outputs/Artifacts:** - - `tests/unit/test_transform.py::test_already_compliant` -- **Acceptance Criteria:** Output JSON equals input JSON exactly - -#### ✅ P5-T4: Write Test for Non-JSON Text Content (TC3) -- **Description:** Test fallback to `{"text": content}` wrapper per PRD §7.1 TC3 -- **Priority:** P0 -- **Dependencies:** P3-T6, P5-T1 -- **Parallelizable:** yes -- **Outputs/Artifacts:** - - `tests/unit/test_transform.py::test_non_json_text_fallback` -- **Acceptance Criteria:** `structuredContent` equals `{"text": "plain text"}` - -#### ✅ P5-T5: Write Test for Non-JSON Line Passthrough (TC4) -- **Description:** Test plain text stdout lines pass through unmodified per PRD §7.1 TC4 -- **Priority:** P0 -- **Dependencies:** P3-T8, P5-T1 -- **Parallelizable:** yes -- **Outputs/Artifacts:** - - `tests/unit/test_transform.py::test_non_json_passthrough` -- **Acceptance Criteria:** Plain text input equals output exactly - -#### ✅ P5-T6: Write Test for Empty Content Array (TC5) -- **Description:** Test `{"content": []}` passes through unmodified per PRD §7.1 TC5 -- **Priority:** P1 -- **Dependencies:** P4-T1, P5-T1 -- **Parallelizable:** yes -- **Outputs/Artifacts:** - - `tests/unit/test_transform.py::test_empty_content` -- **Acceptance Criteria:** No transformation applied to empty content - -#### ✅ P5-T7: Write Test for No Result Field (TC6) -- **Description:** Test non-result JSON passes through per PRD §7.1 TC6 -- **Priority:** P1 -- **Dependencies:** P4-T4, P5-T1 -- **Parallelizable:** yes -- **Outputs/Artifacts:** - - `tests/unit/test_transform.py::test_no_result_field` -- **Acceptance Criteria:** Notifications/error objects unchanged - -#### ✅ P5-T8: Write Test for Mixed Content Types (EC1) -- **Description:** Test image + text content array extracts first text per PRD §5.2 EC1 -- **Priority:** P1 -- **Dependencies:** P3-T4, P5-T1 -- **Parallelizable:** yes -- **Outputs/Artifacts:** - - `tests/unit/test_transform.py::test_mixed_content` -- **Acceptance Criteria:** First text item extracted despite preceding image - -#### ✅ P5-T9: Write Test for Nested JSON String (EC4) -- **Description:** Test `"plain string"` becomes valid structuredContent per PRD §5.2 EC4 -- **Priority:** P2 -- **Dependencies:** P4-T8, P5-T1 -- **Parallelizable:** yes -- **Outputs/Artifacts:** - - `tests/unit/test_transform.py::test_json_string_primitive` -- **Acceptance Criteria:** String primitive preserved in structuredContent - -#### ✅ P5-T10: Create Integration Test with Mock Bridge -- **Description:** Create mock mcpbridge process for end-to-end testing -- **Priority:** P0 -- **Dependencies:** P2-T1, P3-T10, P5-T1 -- **Parallelizable:** no -- **Outputs/Artifacts:** - - `tests/integration/test_e2e.py` - - Mock bridge fixture that outputs canned responses -- **Acceptance Criteria:** Full stdin→transform→stdout cycle verified - -#### ✅ P5-T11: Implement Performance Benchmark -- **Description:** Time 1000 transformations to verify <5ms overhead per PRD §3.1 NFR1 -- **Priority:** P1 -- **Dependencies:** P3-T10, P5-T10 -- **Parallelizable:** yes -- **Outputs/Artifacts:** - - `tests/integration/test_performance.py` - - Benchmark report with average latency -- **Acceptance Criteria:** Average overhead <5ms; documented in test output - -#### ✅ P5-T12: Test with Real Xcode mcpbridge (Manual) -- **Description:** Manual integration test with actual Xcode 26.3+ running -- **Priority:** P0 -- **Dependencies:** P3-T10 -- **Parallelizable:** no -- **Outputs/Artifacts:** - - Test results document -- **Acceptance Criteria:** No errors during 5-minute continuous operation - -#### ✅ P5-T13: Verify All 20 Xcode MCP Tools (IT1-IT4) -- **Description:** Test each of the 20 tools listed in PRD §3.1 tool list -- **Priority:** P0 -- **Dependencies:** P5-T12 -- **Parallelizable:** no -- **Outputs/Artifacts:** - - Integration test suite covering all tools -- **Acceptance Criteria:** Each tool returns valid structuredContent without -32600 errors - -#### ✅ P5-T14: Achieve 90%+ Code Coverage -- **Description:** Run coverage report and fill gaps to reach 90% line coverage -- **Priority:** P1 -- **Dependencies:** P5-T2 through P5-T11 -- **Parallelizable:** no -- **Outputs/Artifacts:** - - Coverage report HTML - - Missing coverage addressed -- **Acceptance Criteria:** `pytest --cov` shows ≥90% coverage - ---- - -### Phase 6: Packaging & Distribution - -#### ✅ P6-T1: Create Standalone Executable Script -- **Description:** Create single-file `mcpbridge-wrapper` script suitable for `~/bin` installation -- **Priority:** P0 -- **Dependencies:** P3-T10 -- **Parallelizable:** no -- **Outputs/Artifacts:** - - `src/mcpbridge_wrapper/cli.py` or standalone `mcpbridge-wrapper` -- **Acceptance Criteria:** File runs directly: `./mcpbridge-wrapper`; all imports self-contained - -#### ✅ P6-T2: Add Executable Shebang and Permissions -- **Description:** Add `#!/usr/bin/env python3` and ensure file is executable per PRD §3.4 -- **Priority:** P0 -- **Dependencies:** P6-T1 -- **Parallelizable:** no -- **Outputs/Artifacts:** - - Executable bit set on script file -- **Acceptance Criteria:** `ls -l` shows `x` permission; runs without `python` prefix - -#### ✅ P6-T3: Create Installation Script -- **Description:** Create shell script that installs to `~/bin/mcpbridge-wrapper` -- **Priority:** P1 -- **Dependencies:** P6-T2 -- **Parallelizable:** no -- **Outputs/Artifacts:** - - `scripts/install.sh` -- **Acceptance Criteria:** Running `scripts/install.sh` creates `~/bin/mcpbridge-wrapper`; script is executable - -#### ✅ P6-T4: Create Cursor MCP Configuration Template -- **Description:** Create `~/.cursor/mcp.json` configuration example per PRD §6.1 -- **Priority:** P0 -- **Dependencies:** P6-T1 -- **Parallelizable:** yes -- **Outputs/Artifacts:** - - `config/cursor-mcp.json` example file - - Documentation snippet -- **Acceptance Criteria:** JSON is valid; path uses `$HOME` or documents username replacement - -#### ✅ P6-T5: Create Claude Code Configuration Template -- **Description:** Document `claude mcp add` command for Claude Code per PRD §3.4 -- **Priority:** P2 -- **Dependencies:** P6-T1 -- **Parallelizable:** yes -- **Outputs/Artifacts:** - - Configuration snippet in docs -- **Acceptance Criteria:** Command `claude mcp add --transport stdio xcode -- /Users/$USER/bin/mcpbridge-wrapper` is documented - -#### ✅ P6-T6: Create Codex CLI Configuration Template -- **Description:** Document `codex mcp add` command for Codex CLI per PRD §3.4 -- **Priority:** P2 -- **Dependencies:** P6-T1 -- **Parallelizable:** yes -- **Outputs/Artifacts:** - - Configuration snippet in docs -- **Acceptance Criteria:** Command `codex mcp add xcode -- /Users/$USER/bin/mcpbridge-wrapper` is documented - -#### ✅ P6-T7: Configure pip Installable Package -- **Description:** Ensure `pip install` creates executable entry point -- **Priority:** P2 -- **Dependencies:** P1-T2, P6-T1 -- **Parallelizable:** yes -- **Outputs/Artifacts:** - - `[project.scripts]` entry in pyproject.toml -- **Acceptance Criteria:** After `pip install`, `mcpbridge-wrapper` command is available in PATH - -#### ✅ P6-T8: Create GitHub Release Workflow -- **Description:** GitHub Actions workflow to create releases with attached artifacts -- **Priority:** P3 -- **Dependencies:** P1-T8, P6-T1 -- **Parallelizable:** yes -- **Outputs/Artifacts:** - - `.github/workflows/release.yml` -- **Acceptance Criteria:** Pushing tag creates release with downloadable script - -#### ✅ P6-T9: Create Uninstall Script -- **Description:** Create uninstall script to remove mcpbridge-wrapper from ~/bin and pip -- **Priority:** P2 -- **Dependencies:** P6-T3 -- **Parallelizable:** yes -- **Outputs/Artifacts:** - - `scripts/uninstall.sh` - Removes wrapper script from ~/bin, uninstalls pip package, optionally cleans config -- **Acceptance Criteria:** - - Running `scripts/uninstall.sh` removes `~/bin/mcpbridge-wrapper` - - pip uninstalls the mcpbridge-wrapper package - - Script has dry-run mode and confirmation prompts - -#### ✅ P6-T10: Create GitHub CI Workflow -- **Description:** Create GitHub Actions workflow for continuous integration that checks project state: build, tests, lint, typecheck -- **Priority:** P1 -- **Dependencies:** P1-T2, P1-T3, P1-T4 -- **Parallelizable:** yes -- **Outputs/Artifacts:** - - `.github/workflows/ci.yml` -- **Acceptance Criteria:** - - Workflow triggers on push/PR to main - - Runs lint (ruff check) - - Runs format check (ruff format --check) - - Runs type check (mypy) - - Runs tests with pytest across Python 3.9-3.12 - - Builds package and validates with twine - - All checks must pass - ---- - -### Phase 7: Documentation - -#### ✅ P7-T1: Write Installation Guide Section -- **Description:** Document step-by-step installation for end users per PRD §4.1 -- **Priority:** P0 -- **Dependencies:** P6-T3 -- **Parallelizable:** no -- **Outputs/Artifacts:** - - `docs/installation.md` or README section -- **Acceptance Criteria:** New user can follow instructions without external help - -#### ✅ P7-T2: Write Cursor Configuration Guide -- **Description:** Document GUI and JSON configuration for Cursor per PRD §4.1 -- **Priority:** P0 -- **Dependencies:** P6-T4 -- **Parallelizable:** yes -- **Outputs/Artifacts:** - - `docs/cursor-setup.md` -- **Acceptance Criteria:** Cursor successfully loads xcode-tools after following guide - -#### ✅ P7-T3: Write Claude Code Configuration Guide -- **Description:** Document one-liner setup for Claude Code -- **Priority:** P1 -- **Dependencies:** P6-T5 -- **Parallelizable:** yes -- **Outputs/Artifacts:** - - `docs/claude-setup.md` or combined doc -- **Acceptance Criteria:** `claude mcp list` shows xcode-tools after setup - -#### ✅ P7-T4: Write Codex CLI Configuration Guide -- **Description:** Document one-liner setup for Codex CLI -- **Priority:** P1 -- **Dependencies:** P6-T6 -- **Parallelizable:** yes -- **Outputs/Artifacts:** - - `docs/codex-setup.md` or combined doc -- **Acceptance Criteria:** `codex mcp list` shows xcode-tools after setup - -#### ✅ P7-T5: Document Environment Variables -- **Description:** Document `MCP_XCODE_PID` and `MCP_XCODE_SESSION_ID` per PRD §6.2 -- **Priority:** P1 -- **Dependencies:** P2-T1 -- **Parallelizable:** yes -- **Outputs/Artifacts:** - - Environment variables table in documentation -- **Acceptance Criteria:** User understands when and how to use optional environment variables - -#### ✅ P7-T6: Write Troubleshooting Guide -- **Description:** Document common errors and solutions per PRD §4.3 -- **Priority:** P1 -- **Dependencies:** P4-T1 through P4-T9 -- **Parallelizable:** yes -- **Outputs/Artifacts:** - - `docs/troubleshooting.md` covering -32600 error and others -- **Acceptance Criteria:** Each error has symptom, cause, and solution clearly documented - -#### ✅ P7-T7: Document the 20 Xcode MCP Tools -- **Description:** List and briefly describe all available tools per PRD tool list -- **Priority:** P2 -- **Dependencies:** P5-T13 -- **Parallelizable:** yes -- **Outputs/Artifacts:** - - Tool reference table in documentation -- **Acceptance Criteria:** All 20 tools documented with name and description - -#### ✅ P7-T8: Write Architecture Overview -- **Description:** Document how the wrapper works internally for contributors -- **Priority:** P2 -- **Dependencies:** P3-T10 -- **Parallelizable:** yes -- **Outputs/Artifacts:** - - `docs/architecture.md` with diagrams -- **Acceptance Criteria:** Reader understands data flow from stdin to stdout - -#### ✅ P7-T9: Create Usage Examples -- **Description:** Document sample workflows (build, test, read file) -- **Priority:** P2 -- **Dependencies:** P5-T13 -- **Parallelizable:** yes -- **Outputs/Artifacts:** - - Example commands in documentation -- **Acceptance Criteria:** 3+ practical examples with expected output - -#### ✅ P7-T10: Write Final README -- **Description:** Complete README with all essential information -- **Priority:** P0 -- **Dependencies:** P7-T1, P7-T2, P7-T6 -- **Parallelizable:** no -- **Outputs/Artifacts:** - - `README.md` at project root -- **Acceptance Criteria:** README includes: what, why, install, configure, usage, troubleshoot - -#### ✅ P7-T11: Create CHANGELOG -- **Description:** Document version history and changes -- **Priority:** P2 -- **Dependencies:** P7-T10 -- **Parallelizable:** yes -- **Outputs/Artifacts:** - - `CHANGELOG.md` with initial 1.0.0 entry -- **Acceptance Criteria:** Follows Keep a Changelog format; all phases summarized - -#### ✅ P7-T12: Move Cursor IDE uvx settings before installation instructions in README -- **Description:** Reorder the README.md so that the Cursor IDE uvx configuration snippet (currently under Configuration > Cursor > "Using uvx (Recommended)") appears before the Installation section. This gives Cursor users the fastest path to getting started — they only need to paste the JSON block into `~/.cursor/mcp.json` and they're done, without scrolling through five installation options first. Include both the basic uvx snippet and the uvx-with-Web-UI variant (`--web-ui`, `--web-ui-port 8080`) so users can choose either option up front. -- **Priority:** P1 -- **Dependencies:** P7-T10 -- **Parallelizable:** yes -- **Outputs/Artifacts:** - - Updated `README.md` with reordered sections -- **Acceptance Criteria:** - - The Cursor uvx `mcp.json` snippet (basic) is visible in the README before the "Installation" heading - - The Cursor uvx-with-Web-UI `mcp.json` snippet (`--web-ui`, `--web-ui-port 8080`) is also shown alongside the basic snippet - - All other README content (installation options, other client configs, usage, etc.) remains intact and in a logical order - - No broken markdown links or formatting issues - ---- - -### Phase 8: Documentation Publishing - -**Intent:** Set up automated documentation generation and publishing using Apple DocC for hosting on GitHub Pages. - -#### ✅ P8-T1: Support Apple DocC for documentation and publishing on soundblaster.github.io Pages -- **Description:** Configure Apple DocC to generate documentation and publish to GitHub Pages at soundblaster.github.io/XcodeMCPWrapper (superseding the original `/mcpbridge-wrapper` path target). -- **Priority:** P2 -- **Dependencies:** P7-T10 -- **Parallelizable:** yes -- **Outputs/Artifacts:** - - DocC documentation catalog (`.docc`) - - GitHub Actions workflow for automated publishing (`.github/workflows/docs.yml`) - - Published docs at `soundblaster.github.io/XcodeMCPWrapper` -- **Acceptance Criteria:** - - DocC builds documentation without errors - - GitHub Pages site is live at `soundblaster.github.io/XcodeMCPWrapper/` - - Documentation updates automatically on pushes to main - -#### ✅ P8-T2: Restructure DocC to Canonical Swift Package Format -- **Description:** Move DocC catalog from root-level `mcpbridge-wrapper.docc/` to canonical Swift Package Manager structure under `Sources/XcodeMCPWrapper/Documentation.docc/` -- **Priority:** P2 -- **Dependencies:** P8-T1 -- **Parallelizable:** yes -- **Outputs/Artifacts:** - - New directory: `Sources/XcodeMCPWrapper/Documentation.docc/` - - Main DocC file: `Sources/XcodeMCPWrapper/Documentation.docc/XcodeMCPWrapper.md` - - All existing DocC articles moved to new location - - Updated GitHub Actions workflow with correct paths -- **Acceptance Criteria:** - - DocC catalog follows Apple's canonical SPM structure - - GitHub Actions workflow builds from new location - - All existing documentation content preserved - - GitHub Pages deployment still works correctly - - Old `mcpbridge-wrapper.docc/` directory removed -- **Canonical Structure:** - ``` - Sources/ - XcodeMCPWrapper/ - Documentation.docc/ - XcodeMCPWrapper.md # Main landing page - GettingStarted.md - Installation.md - Configuration.md - CursorSetup.md - ClaudeCodeSetup.md - CodexCLISetup.md - Troubleshooting.md - Architecture.md - EnvironmentVariables.md - ``` -- **Reference Implementation:** - ```yaml - name: Deploy DocC Documentation - on: - push: - branches: [main] - pull_request: - branches: [main] - workflow_dispatch: - permissions: - contents: read - pages: write - id-token: write - concurrency: - group: "pages" - cancel-in-progress: false - jobs: - build: - runs-on: macos-14 - steps: - - uses: actions/checkout@v4 - - uses: maxim-lobanov/setup-xcode@v1 - with: - xcode-version: latest-stable - - name: Build Documentation - run: | - swift package --allow-writing-to-directory ./docs \ - generate-documentation \ - --target mcpbridge-wrapper \ - --output-path ./docs \ - --transform-for-static-hosting \ - --hosting-base-path mcpbridge-wrapper - - name: Add .nojekyll and index.html redirect - run: | - touch docs/.nojekyll - cat > docs/index.html << 'EOF' - - - - - Redirecting to mcpbridge-wrapper Documentation - - - - -

Redirecting to mcpbridge-wrapper Documentation...

- - - - EOF - - uses: actions/upload-pages-artifact@v3 - if: github.event_name == 'push' - with: - path: "./docs" - deploy: - if: github.event_name == 'push' && github.ref == 'refs/heads/main' - needs: build - runs-on: ubuntu-latest - environment: - name: github-pages - url: ${{ steps.deployment.outputs.page_url }} - steps: - - id: deployment - uses: actions/deploy-pages@v4 - ``` - -#### ✅ P8-T3: Change Deployment Path to xcodemcpwrapper -- **Description:** Update all public-facing documentation, scripts, and configuration templates to use the new deployment path `/Users/YOUR_USERNAME/bin/xcodemcpwrapper` instead of `/Users/YOUR_USERNAME/bin/mcpbridge-wrapper`. The Python package name (`mcpbridge_wrapper`) remains unchanged - only the deployed executable name changes. -- **Priority:** P1 -- **Dependencies:** none -- **Parallelizable:** yes -- **Outputs/Artifacts:** - - Updated `scripts/install.sh` - Creates `~/bin/xcodemcpwrapper` instead of `~/bin/mcpbridge-wrapper` - - Updated `scripts/uninstall.sh` - Removes `~/bin/xcodemcpwrapper` - - Updated `config/cursor-mcp.json` - New path in JSON template - - Updated `config/claude-code.txt` - New path in command examples - - Updated `config/codex-cli.txt` - New path in command examples - - Updated `config/zed-agent.json` - New path in JSON template - - Updated `README.md` - All path references - - Updated `AGENTS.md` - Configuration examples - - Updated `CONTRIBUTING.md` - Development references - - Updated `docs/*.md` - All documentation files - - Updated `Sources/XcodeMCPWrapper/Documentation.docc/*.md` - DocC documentation -- **Acceptance Criteria:** - - All public docs show `xcodemcpwrapper` as the executable name - - Installation script creates `~/bin/xcodemcpwrapper` - - Configuration templates use new path - - No references to `~/bin/mcpbridge-wrapper` remain in active documentation - - Historical archives (SPECS/ARCHIVE/) are NOT modified - - Python source code and package names remain unchanged - - All tests pass after changes - -Phase 8 Follow-up Backlog -- [x] FU-P8-T1-1: Reconcile P8-T1 URL criteria with current GitHub Pages path and resolve DocC reference warnings (P2) -- [x] FU-P6-T10-1: Align manual install script with Web UI configuration expectations (P1) - -#### ✅ FU-P8-T1-1: Reconcile P8-T1 URL criteria with current GitHub Pages path and resolve DocC reference warnings -- **Description:** Review follow-up to align Phase 8 tracking artifacts with the live documentation URL `soundblaster.github.io/XcodeMCPWrapper/` and remove remaining DocC ambiguity warnings in the Phase 8 documentation index. -- **Priority:** P2 -- **Dependencies:** P8-T3 -- **Parallelizable:** yes -- **Outputs/Artifacts:** - - Updated `SPECS/Workplan.md` P8-T1 references to match current GitHub Pages URL - - Updated Phase 8 validation/review artifacts with an explicit supersession note where applicable - - Updated `Sources/XcodeMCPWrapper/Documentation.docc/XcodeMCPWrapper.md` DocC links to avoid ambiguous references -- **Acceptance Criteria:** - - Workplan Phase 8 no longer references the legacy `/mcpbridge-wrapper` GitHub Pages URL as the active deployment URL - - Phase 8 review/validation artifacts clearly document that the active URL is `soundblaster.github.io/XcodeMCPWrapper/` - - `swift package generate-documentation --target XcodeMCPWrapper` completes without `Architecture` ambiguity warnings - -#### ✅ FU-P6-T10-1: Align manual install script with Web UI configuration expectations -- **Description:** Fix the mismatch where `scripts/install.sh` installs only the base package (`pip install -e .`) while docs/config examples allow `--web-ui` usage from `~/bin/xcodemcpwrapper`. This causes runtime failure when users enable Web UI without optional dependencies. Add an explicit installer mode for Web UI extras and update documentation to make the dependency requirement unambiguous. -- **Priority:** P1 -- **Dependencies:** P6-T3, P10-T1 -- **Parallelizable:** yes -- **Outputs/Artifacts:** - - Updated `scripts/install.sh` with Web UI-aware install option (e.g., `--webui`) that installs `-e ".[webui]"` - - Updated `README.md` and `docs/installation.md` with clear mapping: - - base install => no `--web-ui` args - - webui install => `--web-ui` args supported - - Updated `docs/troubleshooting.md` to include this specific symptom/cause/fix path -- **Acceptance Criteria:** - - Running `./scripts/install.sh` (default) keeps current base behavior and works with no Web UI args - - Running `./scripts/install.sh --webui` installs required Web UI dependencies (`fastapi`, `uvicorn`, etc.) - - `~/bin/xcodemcpwrapper --web-ui --web-ui-port 8080 --help` no longer fails after Web UI install mode - - Zed/Cursor configs that include `--web-ui` work when installer is run with Web UI mode - - Documentation examples do not imply Web UI works on base-only install - ---- - -## Known Issues / Bug Tracker - -### BUG-T0: Uptime widget on Web UI always shows 1h 0m 0s ✅ -- **Type:** Bug / Feature Issue -- **Status:** ✅ Complete -- **Priority:** P2 -- **Discovered:** 2026-02-13 -- **Component:** Web UI Dashboard -- **Affected:** Web UI uptime widget display - -#### Description -The uptime widget on the Web UI dashboard always displays a fixed value of "1h 0m 0s" instead of showing the actual runtime uptime of the XcodeMCPWrapper process. - -#### Symptoms -``` -Uptime widget shows: 1h 0m 0s -Expected: Dynamic uptime that increases over time (e.g., 1h 5m 23s after 5 minutes 23 seconds) -``` - -#### Root Cause Analysis -[To be diagnosed during implementation] - -#### Workaround -The uptime counter in the metrics sidebar may show accurate counts for requests/errors as an alternative indicator of runtime. - -#### Resolution Path -- [ ] Investigate Web UI metrics collection and uptime calculation -- [ ] Check if uptime is being calculated as fixed value instead of elapsed time -- [ ] Implement dynamic uptime display -- [ ] Add tests for uptime widget accuracy over time -- [ ] Verify dashboard updates correctly - ---- - -### BUG-T1: Kimi CLI MCP Connection Failure -- **Type:** Bug / Compatibility Issue -- **Status:** 🔴 Open - Client-Side Issue -- **Priority:** P2 -- **Discovered:** 2026-02-08 -- **Component:** Client Compatibility -- **Affected Client:** Kimi CLI v1.9.0 -- **Working Clients:** Cursor, Zed Agent, Claude Code, Codex CLI - -#### Description -Kimi CLI (v1.9.0) fails to maintain a stable stdio MCP connection with `xcodemcpwrapper`. The connection initializes successfully but is immediately closed with error: `Server session was closed unexpectedly`. - -#### Symptoms -``` -Error running tool: Client failed to connect: Server session was closed unexpectedly -``` - -#### Verification Results -| Test | Method | Result | -|------|--------|--------| -| Direct wrapper test | `echo '{"jsonrpc":"2.0",...}' \| xcodemcpwrapper` | ✅ Pass | -| Zed Agent integration | `XcodeListWindows` | ✅ Pass | -| Kimi CLI integration | `XcodeListWindows` | ❌ Fail | - -#### Root Cause Analysis -The wrapper correctly implements MCP protocol spec and responds properly: -```json -{"id":1,"jsonrpc":"2.0","result":{"capabilities":{"tools":{"listChanged":true}},"protocolVersion":"2024-11-05","serverInfo":{"name":"xcode-tools","version":"24571"}}} -``` - -The issue appears to be in Kimi CLI's stdio MCP transport/session management, not the wrapper itself. - -#### Workaround -Use alternative MCP clients that work correctly: -- ✅ **Zed Agent** - Tested and verified working -- ✅ **Cursor** - Primary target client, fully supported -- ✅ **Claude Code** - Documented and tested -- ✅ **Codex CLI** - Documented and tested - -#### Resolution Path -- [ ] Report issue to Kimi CLI development team -- [ ] Monitor Kimi CLI updates for MCP transport fixes -- [ ] Document limitation in troubleshooting guide -- [ ] Re-test when Kimi CLI v1.10.0+ is released - -#### References -- Kimi CLI version tested: 1.9.0 -- Wrapper version tested: 0.1.7 -- Related: P5-T13 (Tool verification across clients) - ---- - -### BUG-T2: `codex mcp add` with Web UI extras fails in `zsh` ✅ -- **Type:** Bug / Documentation / CLI UX -- **Status:** ✅ Complete -- **Priority:** P1 -- **Discovered:** 2026-02-14 -- **Completed:** 2026-02-14 -- **Component:** Installation & Configuration Docs -- **Affected Client:** Codex CLI (shell command setup) -- **Affected Shell:** `zsh` (default macOS shell) - -#### Description -The documented/pasted Web UI setup command for Codex using uvx extras fails in `zsh` because square brackets are treated as a glob pattern when unquoted. - -#### Symptoms -```zsh -egor@MacBook-Pro-Egor bin % codex mcp add xcode -- uvx --from mcpbridge-wrapper[webui] mcpbridge-wrapper --web-ui --web-ui-port 8080 -zsh: no matches found: mcpbridge-wrapper[webui] -``` - -#### Root Cause Analysis -`zsh` performs filename globbing on unquoted bracket expressions like `[webui]`. The token `mcpbridge-wrapper[webui]` is parsed as a glob instead of a literal package specifier. - -#### Workaround -Use any of the following forms so the extras spec is passed literally: -- `uvx --from 'mcpbridge-wrapper[webui]' mcpbridge-wrapper --web-ui --web-ui-port 8080` -- `uvx --from mcpbridge-wrapper\\[webui\\] mcpbridge-wrapper --web-ui --web-ui-port 8080` - -#### Resolution Path -- [x] Update README and setup docs to quote extras in all shell commands (`'mcpbridge-wrapper[webui]'`) -- [x] Update configuration templates/examples that include extras to avoid raw unquoted bracket syntax -- [x] Add a troubleshooting entry for `zsh: no matches found` with shell-safe examples -- [x] Add a short note explaining why quotes are required in `zsh` - ---- - -### BUG-T3: Web UI cannot stay available when MCP bridge initialization fails ✅ -- **Type:** Bug / Web UI / MCP Lifecycle -- **Status:** ✅ Complete -- **Priority:** P1 -- **Discovered:** 2026-02-14 -- **Completed:** 2026-02-14 -- **Component:** CLI startup and Web UI runtime -- **Affected Client:** Codex App / MCP users enabling `--web-ui` -- **Affected Surface:** Local dashboard (`http://localhost:8080`) - -#### Description -When users start the wrapper with `--web-ui` in MCP mode and the Xcode bridge handshake fails (or the MCP client disconnects early), the wrapper process exits before the dashboard remains usable. Users then see browser errors such as "Safari can't connect to the server" while trying to debug MCP connectivity. - -#### Symptoms -```text -MCP startup failed: handshaking with MCP server failed: connection closed: initialize response -Safari can't connect to the server -``` - -#### Root Cause Analysis -Web UI lifecycle is tightly coupled to MCP bridge lifecycle. If bridge startup/session fails, process shutdown also stops the Web UI, leaving no standalone dashboard mode for diagnosis. - -#### Workaround -Use standalone mode while diagnosing bridge issues: -- `xcodemcpwrapper --web-ui-only --web-ui-port 8080` -- `uvx --from 'mcpbridge-wrapper[webui]' mcpbridge-wrapper --web-ui-only --web-ui-port 8080` - -#### Resolution Path -- [x] Add a standalone `--web-ui-only` mode that starts dashboard services without launching `xcrun mcpbridge` -- [x] Ensure `--web-ui-only` implies Web UI enabled and respects `--web-ui-port` and config options -- [x] Add unit tests for arg parsing and main startup behavior in web-ui-only mode -- [x] Document when to use standalone dashboard mode for MCP connection troubleshooting - ---- - -### BUG-T4: Repeated Xcode permission prompts for each short-lived MCP client process -- **Type:** Bug / UX / Client Integration -- **Status:** 🔴 Open -- **Priority:** P1 -- **Discovered:** 2026-02-14 -- **Component:** MCP process lifecycle / Xcode authorization boundary -- **Affected Clients:** Codex CLI/App, other stdio MCP clients launching fresh processes -- **Affected Surface:** Xcode "agent wants to use Xcode's tools" prompt frequency - -#### Description -When MCP clients run in short-lived sessions, each session starts a new wrapper process (new PID). Xcode may request authorization again for each new process, creating repeated prompts and friction during normal usage. - -#### Symptoms -```text -The agent "Codex" ... PID 29854 wants to use Xcode's tools... -The agent "Codex" ... PID 30421 wants to use Xcode's tools... -``` - -#### Root Cause Analysis -Current integration is per-client `stdio`: each client launch creates a separate process and upstream bridge lifecycle. There is no persistent shared broker session to Xcode across clients. - -#### Workaround -Keep a single long-lived client/session running to reduce process churn. This is operationally fragile and does not solve multi-client workflows. - -#### Resolution Path -- [x] Design persistent broker architecture for shared upstream Xcode session (P13-T1) -- [x] Implement long-lived broker daemon with single upstream bridge connection (P13-T2) -- [x] Add multi-client transport + stdio proxy mode to reuse broker session (P13-T3, P13-T4) -- [ ] Validate reduced prompt behavior and document rollout/migration steps (P13-T5, P13-T6) — P13-T5 resolved to FAIL in FU-P13-T14 due broker UID verification rejection (`-32003`); broker credential fallback shipped in FU-P13-T15, prompt behavior now needs re-validation - ---- - -### BUG-T5: Empty-content tool results can still violate strict `structuredContent` contract ✅ -- **Type:** Bug / MCP Protocol Compliance -- **Status:** ✅ Resolved (2026-02-14) -- **Priority:** P0 -- **Discovered:** 2026-02-14 -- **Component:** Response transformation engine -- **Affected Clients:** Strict MCP clients (Codex App/Cursor class behavior) - -#### Description -Some tool responses with `result.content: []` are currently passed through without adding `result.structuredContent`. For tools declaring output schema, strict clients may reject this as protocol-invalid. - -#### Symptoms -```text -Tool has output schema but did not return structured content -``` - -#### Root Cause Analysis -`needs_transformation()` intentionally skips empty content arrays, which can leave schema-required `structuredContent` absent for strict client validation paths. - -#### Workaround -Use clients/builds with compatibility fallback behavior. This is not reliable for strict validation paths. - -#### Resolution Path -- [x] Implement FU-P13-T7 -- [ ] Add strict empty-content regression tests -- [ ] Verify behavior in Codex App and Codex CLI with same wrapper binary - ---- - -### ✅ BUG-T6: Web UI port collisions (`--web-ui-port`) create unstable multi-process behavior -- **Type:** Bug / Runtime / Process Lifecycle -- **Status:** ✅ Done (2026-02-14, PASS) -- **Priority:** P0 -- **Discovered:** 2026-02-14 -- **Component:** CLI startup + Web UI runtime -- **Affected Surface:** Local dashboard and MCP startup reliability - -#### Description -Multiple stale/orphan wrapper instances can compete for the same Web UI port (for example `8080`), producing repeated bind failures and noisy startup state that complicates MCP diagnostics. - -#### Symptoms -```text -ERROR: [Errno 48] error while attempting to bind on address ('127.0.0.1', 8080): address already in use -``` - -#### Root Cause Analysis -Current startup does not enforce a single active Web UI instance per port nor provide deterministic collision recovery behavior. - -#### Workaround -Manually kill stale wrapper/uvx processes or use unique `--web-ui-port` values per client. - -#### Resolution Path -- [x] Implement FU-P13-T8 -- [x] Add deterministic collision handling tests -- [x] Document stale-process cleanup in troubleshooting (done in FU-BUG-T6-1) - ---- - -### ✅ BUG-T7: Unsupported `resources/*` methods can return non-standard error shape -- **Type:** Bug / MCP Compatibility / Error Normalization -- **Status:** ✅ Fixed (2026-02-14) -- **Priority:** P0 -- **Discovered:** 2026-02-14 -- **Component:** Response normalization for non-tool methods -- **Affected Clients:** Clients expecting strict JSON-RPC error envelopes - -#### Description -For unsupported methods like `resources/list` and `resources/templates/list`, upstream may return tool-style `result.isError/content` payloads instead of JSON-RPC `error`. Some clients classify this as unexpected response type. - -#### Symptoms -```text -resources/list failed: Unexpected response type -resources/templates/list failed: Unexpected response type -``` - -#### Root Cause Analysis -Wrapper currently focuses on tool result `structuredContent` transformation and does not normalize unsupported non-tool method failures into canonical JSON-RPC `error` responses. - -#### Workaround -Ignore resource-listing failures when tool calls still work; behavior remains noisy and client-dependent. - -#### Resolution Path -- [x] Implement FU-P13-T9 -- [x] Add method-aware normalization regression tests -- [ ] Validate strict-client compatibility for `resources/*` probing (manual, future) - ---- - -### ✅ BUG-T8: Audit log dashboard shows only entries from the current process (per-process in-memory storage) -- **Type:** Bug / Web UI / Audit Log -- **Status:** ✅ Fixed (2026-02-15) -- **Priority:** P0 -- **Discovered:** 2026-02-15 -- **Component:** `AuditLogger` (`webui/audit.py`), `__main__.py` -- **Affected Clients:** All clients in multi-process setups (Cursor, Zed) - -#### Description -The audit log dashboard only shows entries recorded by the process that currently serves the web UI. In multi-process setups (e.g. Cursor or Zed spawning a new wrapper process per connection), the web-serving process often handles only the initial `initialize` handshake, while subsequent tool calls arrive in sibling processes that have their own `AuditLogger` instance. Those sibling instances write to the shared JSONL file on disk but their in-memory `_entries` list is never visible to the web server. - -#### Symptoms -- "Per-Tool Latency Statistics" shows all historical tool calls (e.g. `BuildProject: 1`, `XcodeListWindows: 2`, `initialize: 3`) -- "Audit Log" table shows only 1 entry (the `initialize` from the current process) - -#### Root Cause Analysis -`SharedMetricsStore` uses SQLite (`~/.cache/mcpbridge-wrapper/metrics.db`) so all processes write to and read from the same store. `AuditLogger` uses an in-memory `self._entries` list that is reset on each process start. `AuditLogger.__init__` opens the JSONL file in append mode but never reads existing entries back into memory. The dashboard's `/api/audit` endpoint reads only from `self._entries`, so it is blind to entries written by any other process. - -#### Workaround -Export audit logs as JSON/CSV (the JSONL file on disk contains the complete history). - -#### Resolution Path -- [x] `AuditLogger._load_history()` reads existing `audit_*.jsonl` files at startup into `self._entries`, capped at `_max_memory_entries` (10 000), skipping malformed lines. -- [x] Add 4 regression tests in `TestStartupHistoryLoad`. - -#### Related Items -- **BUG-T6** ✅ — Port collision multi-process behavior is the direct precondition: it's what causes one process to own the web UI while others silently skip it, creating the split that makes BUG-T8 observable. -- **P10-T2** ✅ — Fixed the same class of problem for metrics by introducing `SharedMetricsStore`; the pattern established there (SQLite cross-process store) is the reference for Option B of BUG-T8. -- **P12-T1** — Adds `client` column to `SharedMetricsStore` schema; if BUG-T8 adopts Option B (SQLite audit store), P12-T1-style schema additions should be designed together to avoid double migration. -- **Phase 13 (P13-T1 – P13-T6)** — Persistent broker will collapse multiple short-lived wrapper processes into one long-lived connection, which would naturally eliminate the multi-process split that causes BUG-T8; fix should remain lightweight (Option A) rather than duplicating broker-level work. -- **BUG-T4** — Repeated Xcode permission prompts per spawn; same root cause (each client spawn starts a fresh process with no shared state), Phase 13 is the intended long-term fix for both. - ---- - -### ✅ BUG-T9: Orphaned Web UI server process blocks port after MCP client disconnect or config change -- **Type:** Bug / Web UI / Process Lifecycle -- **Status:** ✅ Fixed (2026-02-25, PASS) -- **Priority:** P1 -- **Discovered:** 2026-02-15 -- **Completed:** 2026-02-25 -- **Component:** `__main__.py` (main loop), `server.py` (Web UI thread) -- **Affected Clients:** All clients (Cursor, Claude Code, Codex CLI, Zed) - -#### Description -When an MCP client (e.g. Cursor) disconnects, crashes, or changes its server configuration, the old `mcpbridge-wrapper` process can remain alive indefinitely. The orphaned process keeps its Web UI server bound to the configured port (e.g. 8080), preventing any newly spawned process from starting its own Web UI. The user sees the stale dashboard with no indication that it belongs to a dead session. - -#### Reproduction Steps -1. Configure Cursor with `mcpbridge-wrapper --web-ui --web-ui-port 8080` (uvx or local). -2. Open dashboard at `http://localhost:8080` — works normally. -3. Change Cursor's `mcp.json` to a different command path (e.g. switch from uvx to local .venv). -4. Restart Cursor / reconnect MCP server. -5. Dashboard still shows the **old** process's data. New process silently failed to bind port 8080. - -#### Root Cause Analysis -The main loop in `__main__.py` blocks on `output_queue.get()` waiting for stdout EOF from the `mcpbridge` subprocess. When the MCP client disconnects: -1. The wrapper's **stdin** closes → `stdin_forwarder` daemon thread exits. -2. But `mcpbridge` (child process) may keep running because it hasn't received a shutdown signal. -3. `mcpbridge` stdout stays open → `output_queue.get()` blocks forever. -4. The Web UI daemon thread keeps the server socket alive on port 8080. -5. The process is effectively orphaned — no parent, no stdin, but still holding resources. - -#### Impact -- Users see stale dashboards with outdated metrics and audit data. -- New wrapper instances silently skip Web UI startup due to port conflict (per BUG-T6 handling). -- Manual `kill` or `lsof -ti :8080 | xargs kill` is required to recover. - -#### Resolution Path -- [x] Detect stdin EOF in the forwarder thread and trigger deterministic upstream shutdown. -- [x] Add a bounded termination helper (`SIGTERM` then timeout-backed `SIGKILL` fallback) for stuck upstream processes. -- [x] Wire one-shot stdin-closed callback in `main()` so repeated EOF/error events do not trigger duplicate termination attempts. -- [x] Add automated regression tests for EOF callback invocation and graceful-vs-force shutdown behavior. - -#### Related Items -- **BUG-T6** ✅ — Port collision handling (warns but doesn't fix orphans). -- **FU-BUG-T6-1** ✅ — Documents manual stale-process cleanup; BUG-T9 would make that unnecessary. -- **BUG-T4** — Repeated Xcode permission prompts; same root cause (no shared long-lived process). -- **Phase 13** — Persistent broker would eliminate the orphan problem by design. - ---- - -### BUG-T10: Tool chart colors change on update of tool type count -- **Type:** Bug / Web UI / Data Stability -- **Status:** ✅ Fixed (2026-02-20) -- **Priority:** P1 -- **Discovered:** 2026-02-16 -- **Component:** Web UI Dashboard (`webui/static/`, metrics visualization) -- **Affected Clients:** All clients using Web UI dashboard -- **Affected Surface:** Web UI tool usage charts (bar, pie, timeline visualizations) - -#### Description -The colors of tools on charts change when the count of tool types is updated. For example, a red tool becomes blue when a new tool type is added or removed. This is unstable and misleading behavior that undermines user trust in the dashboard data. Color associations must be stable between user sessions and stable across tool type population changes. - -#### Symptoms -``` -Initial state: BuildProject (red), XcodeListWindows (blue), XcodeExecuteCommand (green) -After adding new tool: -New state: BuildProject (blue), XcodeListWindows (green), XcodeExecuteCommand (red) -User sees: Colors have changed, appears to be different tools or corrupted data -``` - -#### Root Cause Analysis -Chart color assignment likely relies on tool index position in the data array or unsorted dictionary iteration, creating non-deterministic color mapping. When new tools are added/removed, array indices shift, causing existing tools to be assigned different colors. This is compounded by lack of persistent tool color mapping across sessions. - -#### Workaround -Refresh the browser page to reset the color assignment, but this persists only for the current session and does not solve the core issue. - -#### Resolution Path -- [ ] Implement stable tool color mapping using a deterministic function (e.g. hash-based assignment or fixed color palette keyed by tool name) -- [ ] Persist tool-to-color mappings in user configuration or database so colors remain stable across sessions -- [ ] Ensure chart rendering uses the stable color mapping instead of array-index-based assignment -- [ ] Add tests to verify tool colors remain consistent when tool type count changes -- [ ] Add tests to verify tool colors persist across dashboard page reloads -- [ ] Validate user experience with stable color scheme across multiple sessions - -#### Related Items -- **P10-T1** — Web UI Control & Audit Dashboard; the metrics and chart visualization system is the direct component affected -- **P10-T1.3** — Frontend dashboard with Chart.js visualizations; color management is part of this sub-task - ---- - -### BUG-T11: Chart Request Timeline never shows actual events -- **Type:** Bug / Web UI / Chart Data -- **Status:** ✅ Fixed (2026-02-25) -- **Priority:** P1 -- **Discovered:** 2026-02-16 -- **Completed:** 2026-02-25 -- **Component:** Web UI Dashboard (`webui/static/`, request timeline chart) -- **Affected Clients:** All clients using Web UI dashboard -- **Affected Surface:** Request Timeline chart on the Web UI dashboard - -#### Description -The "Request Timeline" chart never displays the actual events. Regardless of how many requests are made or how many errors occur, the chart always shows a static display of 1 request and 0 errors. The chart does not reflect real activity and is effectively non-functional as a monitoring tool. - -#### Symptoms -``` -Actual traffic: 50 requests, 3 errors over 10 minutes -Chart displays: 1 request, 0 errors (static, never changes) -Expected: Chart should update in real-time to show 50 requests and 3 errors -``` - -#### Root Cause Analysis -The request timeline chart is likely not consuming live metrics data correctly. Possible causes include: -- The chart data source is reading a snapshot or default value instead of the accumulated timeseries data from `SharedMetricsStore` -- The WebSocket `metrics_update` payload may not include the timeseries buckets needed by the timeline chart, causing it to fall back to a static default -- The frontend chart update logic may be replacing the dataset with a single-point summary instead of appending new data points over time -- Related to the earlier timeseries format mismatch (see FU-P10-T1-BUG-1) — the fix may not have fully resolved the issue for the request timeline specifically - -#### Workaround -None. The chart is non-functional for monitoring purposes. Users must rely on the raw counters or audit log to observe request activity. - -#### Resolution Path -- [x] Trace the data flow from `SharedMetricsStore.get_timeseries()` through the WebSocket payload to the frontend chart rendering for the request timeline -- [x] Verify that the timeseries data returned by the API contains correct per-bucket request and error counts -- [x] Verify that the frontend chart update function appends new data points rather than replacing the entire dataset with a summary -- [x] Ensure the chart x-axis (time) and y-axis (counts) are correctly bound to the timeseries data -- [x] Add integration test that simulates multiple requests and asserts the timeline chart data reflects actual counts -- [x] Validate fix with live traffic to confirm the chart updates in real-time - -#### Related Items -- **P10-T1** — Web UI Control & Audit Dashboard; the request timeline chart is part of this component -- **FU-P10-T1-BUG-1** — Earlier timeseries format mismatch bug; may share the same root cause -- **BUG-T10** — Chart color instability; related chart rendering issue - ---- - -### BUG-T12: Audit Log does not show new calls -- **Type:** Bug / Web UI / Audit Log -- **Status:** ✅ Fixed (2026-02-20) -- **Priority:** P1 -- **Discovered:** 2026-02-18 -- **Component:** Web UI Dashboard (`webui/static/`, audit log table) -- **Affected Clients:** All clients using Web UI dashboard -- **Affected Surface:** Audit Log section on the Web UI dashboard - -#### Description -New MCP tool calls are not appearing in the Audit Log table on the dashboard. The table remains static after the initial page load and does not reflect tool calls that occur while the dashboard is open. - -#### Symptoms -``` -User makes tool calls via MCP client while dashboard is open -Audit Log table: does not update, shows only entries from before page load (or stays empty) -Expected: new rows should appear in real-time (or on each refresh cycle) -``` - -#### Root Cause Analysis -Possible causes: -- The audit log WebSocket/polling update path is not delivering new entries to the frontend -- The frontend audit table rendering is not appending new rows on update (may be re-rendering from scratch and losing entries, or not re-rendering at all) -- `AuditLogger` is writing to disk but the in-memory ring buffer that feeds the `/api/audit` endpoint is not being populated - -#### Workaround -Export audit log via `/api/audit/export/json` or `/api/audit/export/csv` for a snapshot of recorded entries. - -#### Resolution Path -- [ ] Confirm that `AuditLogger` is writing entries to the in-memory ring buffer on each tool call -- [ ] Confirm that `/api/audit` returns new entries after tool calls complete -- [ ] Trace how the frontend polls or subscribes to audit updates and verify new entries are rendered -- [ ] Add a test that records a tool call and asserts the audit API returns the new entry - -#### Related Items -- **BUG-T8** ✅ — Cross-process audit log visibility; earlier fix ensured entries are shared across processes - ---- - -### BUG-T13: Per-Tool Latency Statistics does not show params when `capture_params` is false -- **Type:** Bug / Web UI / Configuration -- **Status:** ✅ Fixed (2026-02-25) -- **Priority:** P2 -- **Discovered:** 2026-02-18 -- **Completed:** 2026-02-25 -- **Component:** Web UI Dashboard (`webui/static/`, per-tool latency table), `webui/config.py` -- **Affected Clients:** All clients using Web UI dashboard with default config -- **Affected Surface:** Per-Tool Latency Statistics table - -#### Description -With `metrics.capture_params` set to `false` (the default), the Per-Tool Latency Statistics table shows no parameter information. There is no UI indication that this feature is disabled, leaving users unaware that enabling `capture_params: true` via `--web-ui-config` would unlock parameter-level analysis. - -#### Observed Config -```json -{ - "metrics": { - "capture_params": false - } -} -``` - -#### Symptoms -``` -Tool calls are made; Per-Tool Latency Statistics table shows call counts and latency. -No parameter key data is shown anywhere in the table. -No tooltip, label, or hint explains that capture_params must be enabled. -``` - -#### Root Cause Analysis -`capture_params: false` is correct by design — parameter capture is opt-in for privacy. The bug is a UX gap: the dashboard silently omits the params column/section without explaining why or how to enable it. - -#### Workaround -Enable parameter capture by passing `--web-ui-config` with `metrics.capture_params: true`. See [Web UI Dashboard docs](docs/webui-setup.md#using---web-ui-config-in-mcpjson). - -#### Resolution Path -- [x] Add a disabled-state hint in the Per-Tool Latency Statistics table when `capture_params` is false (e.g. greyed-out column with tooltip "Enable capture_params in webui config to see parameter data") -- [x] Expose the current value of `capture_params` in the `/api/config` response (already done) and have the frontend read it to conditionally render the hint -- [x] Add a test asserting the hint is present when `capture_params` is false - -#### Related Items -- **P12-T2** ✅ — Add Tool Parameter Frequency Analysis; the feature this bug surfaces - ---- - -### BUG-T14: Rows in Per-Tool Latency Statistics fold automatically immediately after unfolding -- **Type:** Bug / Web UI / UI Stability -- **Status:** ✅ Fixed (2026-02-20) -- **Priority:** P1 -- **Discovered:** 2026-02-18 -- **Completed:** 2026-02-20 -- **Component:** Web UI Dashboard (`webui/static/`, per-tool latency table) -- **Affected Clients:** All clients using Web UI dashboard -- **Affected Surface:** Per-Tool Latency Statistics table - -#### Description -Expanded or highlighted rows in the Per-Tool Latency Statistics table fold/collapse automatically immediately (or within about 1 second) after the user unfolds them. This coincides with the dashboard's default WebSocket refresh interval (`dashboard.refresh_interval_ms: 1000`), suggesting the table is being fully re-rendered on each update, discarding user interaction state (expanded rows, hover highlights, selected rows). - -#### Symptoms -``` -User expands a row in the Per-Tool Latency Statistics table to inspect details. -Immediately (or after ~1 second) the row collapses back to its default state. -Behaviour repeats on every subsequent refresh cycle. -``` - -#### Root Cause Analysis -The frontend table update logic likely replaces the entire table DOM on each WebSocket message rather than performing a targeted data update (e.g. diffing rows by tool name). This causes all interactive state to be lost on every refresh. - -#### Workaround -Increase `dashboard.refresh_interval_ms` in the webui config to a higher value (e.g. `10000`) to reduce the frequency of resets. - -#### Resolution Path -- [x] Refactor the per-tool latency table update to preserve row state during periodic updates -- [x] Preserve expanded/selected row state across updates by tracking it in frontend JS state -- [x] Add a UI test (or manual test checklist) that confirms row state survives a refresh cycle - -#### Related Items -- **BUG-T10** — Chart color changes on update; same root cause (full re-render on refresh) -- **BUG-T11** — Request Timeline never updates; related dashboard refresh issues - ---- - -### ✅ BUG-T15: Web UI fails to come up in MCP client runs when `--web-ui-port` and `--web-ui-config` are combined -- **Type:** Bug / Web UI / MCP Client Integration -- **Status:** ✅ Fixed (2026-02-20) -- **Priority:** P1 -- **Discovered:** 2026-02-20 -- **Component:** CLI arg handling in `__main__.py`, Web UI docs/examples -- **Affected Clients:** Cursor (reported), potentially Claude Code/Codex CLI/Zed -- **Affected Surface:** Dashboard startup and discoverability (`http://localhost:8080`) - -#### Description -In MCP client configuration, supplying both `--web-ui-port 8080` and `--web-ui-config /path/to/webui.json` can result in the dashboard being unreachable, while using `--web-ui` with only `--web-ui-config` works in the same environment. - -#### Reproduction Steps -1. Configure MCP with: - - `--web-ui --web-ui-port 8080 --web-ui-config /Users/egor/.config/xcodemcpwrapper/webui.json` -2. Start/restart MCP server from Cursor. -3. Open `http://localhost:8080`. -4. Observe browser cannot connect. -5. Reconfigure MCP to: - - `--web-ui --web-ui-config /Users/egor/.config/xcodemcpwrapper/webui.json` -6. Restart MCP server and verify dashboard becomes reachable. - -#### Root Cause Analysis -- `--web-ui-port` explicitly overrides the config file port, which may force an unavailable/incorrect bind target for that process lifecycle. -- Existing docs include combined examples that may be correct in isolation but fragile in real MCP multi-process launches. -- Port-collision handling may degrade into "dashboard skipped" behavior that users experience as "Web UI broken." - -#### Workaround -Use `--web-ui` with `--web-ui-config` only, and set the port in the config file. - -#### Resolution Path -- [x] Reproduce in automated/integration flow for MCP-launched process with both flags. -- [x] Capture startup stderr logs and confirm exact failure mode (`address already in use`, bind host mismatch, or other). -- [x] Decide and implement one behavior: - - Prefer config file port when both are supplied, or - - Keep CLI override but improve diagnostics and docs with explicit precedence and failure guidance. -- [x] Update docs/examples to avoid misleading combined-flag configuration for MCP clients. -- [x] Add regression test(s) covering argument precedence and dashboard startup behavior. - -#### Related Items -- **BUG-T6** ✅ — Web UI port collision behavior; likely same user-visible failure path. -- **FU-BUG-T6-1** ✅ — Current mitigation is documentation-only cleanup. -- **docs/webui-setup.md** — Contains combined `--web-ui-port` + `--web-ui-config` `mcp.json` example. - ---- - -### BUG-T16: Tool Distribution (Pie) widget is cropped at medium widths -- **Type:** Bug / Web UI / Responsive Layout -- **Status:** ✅ Fixed (2026-02-20) -- **Priority:** P1 -- **Discovered:** 2026-02-20 -- **Completed:** 2026-02-20 -- **Component:** Web UI Dashboard (`webui/static/`, chart layout CSS) -- **Affected Clients:** All clients using Web UI dashboard -- **Affected Surface:** Tool Distribution (Pie) widget - -#### Description -The "Tool Distribution (Pie)" widget does not flow correctly when the dashboard width is reduced to medium breakpoints. At around `1450px` layout is fine, around `1200px` the pie widget gets cropped, and below `768px` it becomes okay again. - -#### Symptoms -```text -~1450px viewport: widget displays correctly -~1200px viewport: Tool Distribution (Pie) widget is cropped -<768px viewport: widget displays correctly again -``` - -#### Root Cause Analysis -Likely a responsive breakpoint/layout gap between desktop and mobile rules where the chart container keeps a width/min-width/flex basis that overflows or clips at mid-range viewport sizes. - -#### Workaround -Resize the browser to either wide desktop (`~1450px+`) or narrow mobile (`<768px`) widths where the widget renders correctly. - -#### Resolution Path -- [x] Reproduce and capture exact breakpoint range where cropping begins/ends -- [x] Inspect chart container/grid/flex CSS rules for mid-width breakpoints (especially min-width, fixed widths, and overflow settings) -- [x] Update responsive CSS so the pie widget reflows without clipping at medium widths -- [x] Add/extend frontend responsiveness test or manual regression checklist for `1200px` range -- [x] Validate layout at `1450px`, `1200px`, `1024px`, `768px`, and mobile widths - -#### Related Items -- **P10-T1** ✅ — Web UI Dashboard implementation (chart layout subsystem) -- **BUG-T10** — Tool chart rendering instability; related chart UX surface - ---- - -### BUG-T17: Rows in Audit Log table automatically fold after user unfolds them -- **Type:** Bug / Web UI / UI Stability -- **Status:** ✅ Fixed (2026-02-20) -- **Priority:** P1 -- **Discovered:** 2026-02-20 -- **Completed:** 2026-02-20 -- **Component:** Web UI Dashboard (`webui/static/`, audit log table rendering) -- **Affected Clients:** All clients using Web UI dashboard -- **Affected Surface:** Audit Log table row expand/collapse behavior - -#### Description -Rows in the Audit Log table automatically fold/collapse several seconds after a user unfolds them. This interrupts inspection workflows and suggests expanded row UI state is being reset during periodic dashboard updates. - -#### Symptoms -```text -User unfolds an Audit Log row to inspect details. -After several seconds, the row folds automatically without user action. -``` - -#### Root Cause Analysis -Likely caused by full table re-render on refresh/WebSocket updates without preserving expanded-row state, similar to other dashboard state-reset issues. - -#### Workaround -Temporarily increase dashboard refresh interval via config to reduce frequency of auto-fold behavior. - -#### Resolution Path -- [x] Reproduce with default refresh interval and identify the exact trigger (WebSocket update vs polling refresh) -- [x] Refactor Audit Log table updates to patch rows by stable entry ID instead of full DOM replacement -- [x] Persist expanded/collapsed row state across refresh cycles -- [x] Add regression test (or manual checklist) confirming unfolded rows stay unfolded across updates -- [x] Validate behavior under active tool-call traffic - -#### Related Items -- **BUG-T12** — Audit Log update path not showing new calls; same component/surface -- **BUG-T14** ✅ — Per-Tool Latency row state now preserved across refresh - ---- - -### ✅ BUG-T18: Error Breakdown widget must be full width streatched -- **Type:** Bug / Web UI / Layout -- **Status:** ✅ Fixed (2026-02-26) -- **Priority:** P2 -- **Discovered:** 2026-02-20 -- **Completed:** 2026-02-26 -- **Component:** Web UI Dashboard (`webui/static/index.html`, `webui/static/dashboard.css`) -- **Affected Clients:** All clients using Web UI dashboard -- **Affected Surface:** Error Breakdown widget - -#### Description -The "Error Breakdown" widget must be displayed as a full-width stretched widget in the dashboard layout. - -#### Symptoms -```text -Error Breakdown appears as a regular half-width card in the chart grid. -Expected: Error Breakdown spans the full row width. -``` - -#### Root Cause Analysis -Likely the widget container is using the default chart card class and is not marked to span all grid columns. - -#### Workaround -None. - -#### Resolution Path -- [x] Reproduce on current dashboard layout and confirm non-full-width rendering -- [x] Update chart container/layout rules so Error Breakdown spans full width -- [x] Validate responsive behavior at desktop/tablet/mobile breakpoints -- [x] Add regression coverage for full-width Error Breakdown layout - -#### Related Items -- **P10-T1** ✅ — Web UI dashboard chart layout baseline -- **BUG-T16** ✅ — Another chart layout responsiveness fix - ---- - -### BUG-T19: Audit Log and Session Timeline are inconsistent with tool charts in multi-process runs -- **Type:** Bug / Web UI / Data Consistency -- **Status:** ✅ Fixed (2026-02-25) -- **Priority:** P1 -- **Discovered:** 2026-02-20 -- **Completed:** 2026-02-25 -- **Component:** Web UI backend (`webui/server.py`, `webui/audit.py`, `webui/shared_metrics.py`) -- **Affected Clients:** Cursor and other short-lived multi-process MCP clients -- **Affected Surface:** Audit Log table, Session Timeline, Tool usage charts - -#### Description -Dashboard surfaces are inconsistent: tool charts show fresh activity while Audit Log and Session Timeline remain stale (or show only old rows such as an earlier `initialize`), especially after reconnecting Cursor. - -#### Symptoms -```text -New tool usage appears in chart widgets. -Audit Log does not add a new row for reconnect/initialize. -Session Timeline still shows older last event. -``` - -#### Root Cause Analysis -Likely split data source model: -- charts/KPIs use `SharedMetricsStore` (cross-process SQLite), -- audit/sessions use process-local `AuditLogger` in-memory entries loaded at startup. -When a different wrapper process receives new events, chart data advances but local audit/session views can lag. - -#### Workaround -Use export endpoints (`/api/audit/export/json` or `/api/audit/export/csv`) for a broader snapshot, but real-time consistency remains unreliable. - -#### Resolution Path -- [x] Reproduce with repeated Cursor reconnects in a multi-process setup and capture API deltas between `/api/metrics`, `/api/audit`, and `/api/sessions` -- [x] Choose and implement a single shared source of truth for audit/session data across processes (SQLite-backed audit store or equivalent) -- [x] Ensure `/api/audit` reflects newly recorded entries regardless of which wrapper process logged them -- [x] Ensure `/api/sessions` is computed from the same shared data source as Audit Log -- [x] Add integration regression test covering reconnect + new initialize row visibility in Audit Log and Session Timeline -- [x] Document consistency guarantees and limitations in `docs/webui-setup.md` and troubleshooting guide - -#### Related Items -- **BUG-T12** ✅ — Audit Log live refresh path improved but did not fully solve cross-process consistency -- **BUG-T8** ✅ — Cross-process audit visibility baseline; likely related implementation surface -- **P10-T2** ✅ — Shared metrics store pattern reference - ---- - -### BUG-T20: Session Timeline can show negative duration due to incorrect entry ordering -- **Type:** Bug / Web UI / Session Analytics -- **Status:** ✅ Fixed (2026-02-25) -- **Priority:** P1 -- **Discovered:** 2026-02-20 -- **Completed:** 2026-02-25 -- **Component:** Session detection path (`webui/server.py`, `webui/sessions.py`) -- **Affected Clients:** All clients using Session Timeline -- **Affected Surface:** Session Timeline duration and ordering - -#### Description -Session Timeline can display impossible negative durations (for example, `-174224s`) and stale-looking last events. - -#### Symptoms -```text -Session shows negative duration. -Session start/end ordering appears inverted. -``` - -#### Root Cause Analysis -`detect_sessions()` expects entries sorted by timestamp ascending, but callers pass most-recent-first audit entries, causing inverted session boundaries and invalid duration math. - -#### Workaround -None. - -#### Resolution Path -- [x] Normalize session input ordering to ascending timestamps before calling `detect_sessions()` -- [x] Add defensive sorting (or contract enforcement) in session computation path -- [x] Add regression test asserting non-negative session duration for mixed/newest-first inputs -- [x] Validate timeline rendering shows monotonic ordering and correct latest event after reconnect/activity - -#### Related Items -- **BUG-T19** — Shared audit/session consistency issue may amplify session timeline staleness -- **P11-T2** ✅ — Session Timeline feature implementation - ---- - -### Phase 10: Web UI Control & Audit Dashboard - -**Intent:** Create a web-based dashboard for real-time monitoring, control, and audit logging of the XcodeMCPWrapper. Provides visibility into MCP tool usage, performance metrics, and operational control. - -#### ✅ P10-T1: Implement Web UI Control & Audit Dashboard - -**Description:** -Create a comprehensive web dashboard for monitoring and controlling the XcodeMCPWrapper. The dashboard will provide real-time metrics (RPS, latency, error rates), tool usage analytics with visualizations, request/response inspector for debugging, persistent audit logging, and service control interface. Implement using FastAPI for the backend with WebSocket support for live updates, and a modern HTML/CSS/JS frontend with Chart.js visualizations. Include configurable authentication, log rotation, and export capabilities. - -**Priority:** P1 - -**Dependencies:** P9-T1 - -**Parallelizable:** no - -**Outputs/Artifacts:** -- `src/mcpbridge_wrapper/webui/` package with: - - `server.py` - FastAPI web server with REST API and WebSocket - - `metrics.py` - Thread-safe metrics collection system - - `audit.py` - Structured audit logging with rotation - - `config.py` - Web UI configuration management - - `static/` - Frontend dashboard assets (HTML, CSS, JS) -- `config/webui.json` - Configuration template -- Updated `src/mcpbridge_wrapper/cli.py` - Add `--web-ui` flag -- Updated `pyproject.toml` - Optional webui dependencies -- Tests in `tests/unit/webui/` and `tests/integration/webui/` -- Documentation in `docs/webui-setup.md` - -**Acceptance Criteria:** -- [ ] Dashboard accessible at `http://localhost:8080` when `--web-ui` flag is used -- [ ] Real-time metrics update via WebSocket every second -- [ ] Tool usage charts (bar, pie, timeline) display accurate data -- [ ] Audit logs capture all MCP tool calls with timestamps -- [ ] Log export produces valid JSON/CSV files -- [ ] Web UI has < 1% performance impact on wrapper core -- [ ] All existing tests pass with Web UI enabled -- [ ] New unit tests achieve > 90% coverage for webui module -- [ ] Documentation includes setup and troubleshooting guide -- [ ] Optional authentication works correctly -- [ ] Log rotation prevents unbounded disk usage - -**Sub-tasks:** -1. P10-T1.1: Create webui package structure and metrics collection hooks -2. P10-T1.2: Implement FastAPI server with REST endpoints and WebSocket -3. P10-T1.3: Build frontend dashboard with Chart.js visualizations -4. P10-T1.4: Implement audit logging with rotation -5. P10-T1.5: Add CLI integration and configuration -6. P10-T1.6: Write tests and documentation - ---- - -#### ✅ P10-T2: Fix Web UI timeseries charts showing no data - -**Description:** -The Web UI dashboard shows "Connected" and counters work correctly, but the timeseries charts ("Request timeline" and "Latency") show no data. The issue is that `SharedMetricsStore.get_timeseries()` returns data in a different format than the frontend expects: - -- **Current (wrong):** `{"data": [{"timestamp": "...", "requests": N, "errors": N, "latency_ms": N}]}` -- **Expected by frontend:** `{"requests": [{"t": seconds_ago, "v": count}], "errors": [...], "latencies": [{"t": seconds_ago, "v": latency}]}` - -The frontend JavaScript expects arrays of `{t, v}` objects for each metric type, with time as "seconds ago" relative to now. The SharedMetricsStore currently returns minute-bucketed data with string timestamps. - -**Root Cause:** -When migrating from in-memory `MetricsCollector` (which had the correct format) to `SharedMetricsStore` (SQLite-based for multi-process support), the `get_timeseries()` method was implemented with a different return format that doesn't match the frontend expectations. - -**Priority:** P1 - -**Dependencies:** P10-T1 - -**Parallelizable:** no - -**Outputs/Artifacts:** -- Fixed `src/mcpbridge_wrapper/webui/shared_metrics.py` - Update `get_timeseries()` to return format matching frontend expectations -- Updated tests in `tests/unit/webui/test_shared_metrics.py` - Verify timeseries format -- Validation report confirming charts display data correctly - -**Acceptance Criteria:** -- [ ] `/api/metrics/timeseries` returns data in format `{"requests": [...], "errors": [...], "latencies": [...]}` -- [ ] Each array contains objects with `t` (seconds ago) and `v` (value) properties -- [ ] Request timeline chart displays data points -- [ ] Latency chart displays data points -- [ ] Charts update in real-time via WebSocket -- [ ] All existing tests pass -- [ ] New tests verify timeseries format matches frontend expectations - ---- - -#### ✅ P10-T3: Recover main branch after accidental Web UI merge - -**Description:** -Main branch is currently unstable after an accidental merge of the Phase 10 Web UI branch. Diagnose regressions introduced by that merge and restore main to a releasable state without discarding intended Web UI functionality. - -**Priority:** P0 - -**Dependencies:** P10-T2 - -**Parallelizable:** no - -**Outputs/Artifacts:** -- Regression report listing failures introduced by the accidental merge -- Corrective patch set (revert and/or forward-fix) to restore stability -- Updated tests/docs where behavior changed during stabilization - -**Acceptance Criteria:** -- [ ] `pytest` passes on the recovery branch -- [ ] `ruff check src/` and `mypy src/` pass -- [ ] Web UI functionality from P10 remains operational after stabilization -- [ ] No known merge-regression failures remain on the branch proposed for `main` - ---- - -### Phase 9: Release Management - -**Intent:** Manage version releases, including version bumps, changelog updates, and automated publishing. - -#### ✅ P9-T2: Update Documentation with uvx Installation Method -- **Description:** Update all documentation to include uvx as the recommended installation method. The package is now published to PyPI and MCP Registry, and uvx provides the easiest way to install without cloning the repository or manually setting up paths. Update README.md, all docs/*.md files, AGENTS.md, and config templates with uvx instructions. -- **Priority:** P1 -- **Dependencies:** P9-T1 -- **Parallelizable:** no -- **Outputs/Artifacts:** - - Updated `README.md` - Primary uvx method documented, manual install as alternative - - Updated `docs/installation.md` - uvx installation section - - Updated `docs/cursor-setup.md` - uvx configuration examples - - Updated `docs/claude-setup.md` - uvx configuration examples - - Updated `docs/codex-setup.md` - uvx configuration examples - - Updated `AGENTS.md` - uvx method in Quick Start - - Updated `config/cursor-mcp.json` - uvx template option - - Updated `config/claude-code.txt` - uvx command option - - Updated `config/codex-cli.txt` - uvx command option -- **Acceptance Criteria:** - - All documentation shows uvx as the primary/recommended installation method - - Manual installation is documented as an alternative for development - - All config templates include uvx options - - uvx installation verified working (already tested by user) - - No breaking changes to existing manual installation paths - ---- - -#### ✅ P9-T1: Release version 0.2.0 -- **Description:** Bump version to 0.2.0, update CHANGELOG, create git tag, and trigger automated publishing to PyPI and MCP Registry -- **Priority:** P1 -- **Dependencies:** P8-T2 -- **Parallelizable:** no -- **Outputs/Artifacts:** - - Version updated in `pyproject.toml` (0.1.7 → 0.2.0) - - Version updated in `server.json` (0.1.7 → 0.2.0) - - CHANGELOG.md entry for v0.2.0 - - Git tag `v0.2.0` pushed to origin - - GitHub Release created automatically -- **Acceptance Criteria:** - - `pyproject.toml` shows version 0.2.0 - - `server.json` shows version 0.2.0 - - CHANGELOG has entry for [0.2.0] with release date - - Git tag `v0.2.0` exists on GitHub - - GitHub Actions workflow publishes to PyPI successfully - - MCP Registry receives the new version -- **Release Checklist:** - - [ ] Update version in `pyproject.toml` - - [ ] Update version in `server.json` - - [ ] Add CHANGELOG entry for 0.2.0 - - [ ] Commit changes: "Bump version to 0.2.0" - - [ ] Create git tag: `git tag v0.2.0` - - [ ] Push tag: `git push origin v0.2.0` - - [ ] Verify GitHub Actions workflow completes - - [ ] Verify PyPI package updated - - [ ] Verify MCP Registry updated - ---- - -#### ✅ P9-T3: Release version 0.3.0 (Web UI Feature Release) -- **Description:** Prepare and publish version 0.3.0 as the Web UI release. Include final version bumps, release notes for the new dashboard feature set, and tagged publication to PyPI and MCP Registry. -- **Priority:** P1 -- **Dependencies:** P10-T3, FU-REBUILD-P10-T1-6 -- **Parallelizable:** no -- **Outputs/Artifacts:** - - Version updated in `pyproject.toml` (0.2.0 -> 0.3.0) - - Version updated in `server.json` (0.2.0 -> 0.3.0) - - CHANGELOG.md entry for v0.3.0 with Web UI highlights - - Git tag `v0.3.0` pushed to origin - - GitHub Release created automatically -- **Acceptance Criteria:** - - `pyproject.toml` shows version 0.3.0 - - `server.json` shows version 0.3.0 - - CHANGELOG has entry for [0.3.0] with release date and Web UI feature summary - - Git tag `v0.3.0` exists on GitHub - - GitHub Actions workflow publishes to PyPI successfully - - MCP Registry receives version 0.3.0 -- **Release Checklist:** - - [x] Update version in `pyproject.toml` - - [x] Update version in `server.json` - - [x] Add CHANGELOG entry for 0.3.0 (Web UI release) - - [x] Commit changes: "Bump version to 0.3.0" - - [x] Create git tag: `git tag v0.3.0` - - [x] Push tag: `git push origin v0.3.0` - - [x] Verify GitHub Actions workflow completes - - [x] Verify PyPI package updated - - [x] Verify MCP Registry updated - ---- - -#### ✅ P9-T4: Create the publishing helper -- **Description:** Create a helper script to streamline release version updates for publishing. The script should update all required version fields in one run (at minimum `pyproject.toml` and `server.json`), validate the provided semantic version, and guide the user through the remaining publish steps documented in `PUBLISHING.md`. -- **Priority:** P1 -- **Dependencies:** P9-T3 -- **Parallelizable:** yes -- **Outputs/Artifacts:** - - New helper script at `scripts/publish_helper.py` (or equivalent script under `scripts/`) - - Documentation update in `PUBLISHING.md` showing how to run the helper - - Optional `Makefile` convenience target for version bumping -- **Acceptance Criteria:** - - Running the helper with a target version updates `pyproject.toml` and `server.json` to the exact same version value - - The helper rejects invalid version formats with a clear error message - - The helper supports a dry-run mode that prints planned changes without modifying files - - The helper prints next release commands (commit, tag, push) aligned with `PUBLISHING.md` - - Existing tests and quality gates continue to pass after integrating the helper - ---- - -Phase 9 Follow-up Backlog -- [x] FU-P9-T2-1: Fix uvx Web UI examples to include `webui` extras (P1) -- [x] FU-P9-T4-1: Align publish_helper output with protected main branch workflow (P1) -- [x] FU-P9-T2-2: Add troubleshooting guidance for stale uvx cache/process versions (P1) - -#### ✅ FU-P9-T2-1: Fix uvx Web UI examples to include `webui` extras -- **Description:** Resolve documentation/config mismatch where examples use `uvx --from mcpbridge-wrapper ... --web-ui` without optional dependencies. Update all uvx Web UI examples to install extras via `--from mcpbridge-wrapper[webui]`, and align troubleshooting/runtime guidance with the correct uvx command. -- **Priority:** P1 -- **Dependencies:** P9-T2 -- **Parallelizable:** yes -- **Outputs/Artifacts:** - - Updated `README.md` uvx + Web UI snippets use `mcpbridge-wrapper[webui]` - - Updated `docs/cursor-setup.md`, `docs/claude-setup.md`, `docs/codex-setup.md` uvx + Web UI commands - - Updated config templates: `config/cursor-mcp.json`, `config/zed-agent.json`, `config/claude-code.txt`, `config/codex-cli.txt` - - Updated troubleshooting guidance to include uvx extras fix path - - Optional: improved runtime error message when `--web-ui` is used without extras -- **Acceptance Criteria:** - - No remaining documented command/config combines `--web-ui` with `uvx --from mcpbridge-wrapper` (base-only) - - All uvx Web UI examples consistently use `uvx --from mcpbridge-wrapper[webui] mcpbridge-wrapper` - - A user can copy/paste the documented Cursor JSON Web UI config and connect without `ModuleNotFoundError: uvicorn` - - Troubleshooting docs include both solutions: - - use `mcpbridge-wrapper[webui]` for uvx - - remove `--web-ui` args when dashboard is not needed - ---- - -#### ✅ FU-P9-T4-1: Align publish_helper output with protected main branch workflow -- **Description:** Update `scripts/publish_helper.py` release guidance so it does not instruct direct commits/tags from `main` in repositories where `main` is protected. Guidance should explicitly recommend creating a release branch, pushing branch commits, opening a PR into `main`, and only tagging after merge. -- **Priority:** P1 -- **Dependencies:** P9-T4 -- **Parallelizable:** yes -- **Outputs/Artifacts:** - - Updated `scripts/publish_helper.py` summary text and command block - - Updated tests in `tests/unit/test_publish_helper.py` validating protected-branch-safe guidance - - Optional alignment update in `PUBLISHING.md` if command sequence is duplicated there -- **Acceptance Criteria:** - - [x] Running `python scripts/publish_helper.py ` no longer suggests direct push-to-main flow - - [x] Printed commands include branch creation + push + PR-to-main step before tagging - - [x] Guidance still includes tag creation/push after merge so GitHub publish workflow is triggered - - [x] `pytest tests/unit/test_publish_helper.py` passes - ---- - -#### ✅ FU-P9-T2-2: Add troubleshooting guidance for stale uvx cache/process versions -- **Description:** Document the failure mode where Cursor or manual Web UI sessions keep running an older `uvx` environment (for example `mcpbridge-wrapper==0.3.2`) after a fix is released, causing stale behavior such as uptime stuck at `1h 0m 0s`. Add explicit verification and recovery steps using port/PID inspection, version checks, process restart, and `uvx --refresh`. -- **Priority:** P1 -- **Dependencies:** P9-T2, FU-P9-T2-1 -- **Parallelizable:** yes -- **Outputs/Artifacts:** - - Updated `docs/troubleshooting.md` with a dedicated section for stale `uvx` cache/process diagnosis - - Updated setup docs (at minimum `docs/cursor-setup.md`) with a one-time `--refresh` guidance note after new releases - - Optional quick-check snippet in `README.md` for confirming active runtime package version on a dashboard port -- **Acceptance Criteria:** - - [x] Troubleshooting docs include symptom/cause/fix for: uptime or behavior unchanged after upgrade - - Docs provide concrete commands to: - - [x] identify process listening on Web UI port - - [x] inspect active runtime version from that process - - [x] restart with `uvx --refresh --from mcpbridge-wrapper[webui] ...` - - [x] Recovery steps explicitly mention that multiple wrapper processes can coexist and mask upgrades - - [x] Guidance is validated against a local repro where an old process serves stale behavior and a refreshed process resolves it - ---- - -### Phase 11: Web UI UX Improvements - -**Intent:** Enhance the dashboard with better debugging tools, session awareness, theming, and keyboard-driven workflows. - -#### ✅ P11-T1: Add Tool Call Detail Inspector (Request/Response Viewer) -- **Description:** Add a clickable row expansion or slide-out panel in the audit table that displays the full JSON-RPC request and response payloads. Payloads are syntax-highlighted and collapsible. Backend stores truncated payloads in a bounded ring buffer (last 500, max 64KB each) behind an optional `capture_payload` config flag (default off for privacy). New API: `GET /api/audit/{request_id}/detail` returns `{request: {...}, response: {...}}`. Frontend: click audit row to expand inline or open side panel with pretty-printed JSON. -- **Priority:** P1 -- **Dependencies:** P10-T1 -- **Parallelizable:** yes -- **Outputs/Artifacts:** - - Updated `src/mcpbridge_wrapper/webui/audit.py` - payload capture and storage - - New SQLite table or column for request/response payloads - - New API endpoint in `src/mcpbridge_wrapper/webui/server.py` - - Updated `src/mcpbridge_wrapper/webui/static/dashboard.js` - row expansion UI - - Updated `src/mcpbridge_wrapper/webui/static/dashboard.css` - detail panel styling - - Updated `src/mcpbridge_wrapper/webui/config.py` - `capture_payload` flag - - Tests in `tests/unit/webui/test_audit.py` and `tests/unit/webui/test_server.py` -- **Status:** ✅ DONE (2026-02-15) -- **Acceptance Criteria:** - - [x] `capture_payload: true` in config enables payload storage - - [x] `GET /api/audit/{request_id}/detail` returns full request/response JSON - - [x] Clicking an audit row in the dashboard expands to show payload detail - - [x] Payloads are truncated at 64KB to bound storage - - [x] Ring buffer retains last 500 payloads and evicts oldest - - [x] Default behavior (flag off) is unchanged — no payload capture overhead - - [x] Tests cover payload capture, retrieval, truncation, and ring buffer eviction - ---- - -#### ✅ P11-T2: Add Session Timeline View -- **Description:** Add a vertical timeline view that groups tool calls into sessions detected by configurable idle gaps (default 5 min). Each session shows a compact sequence of tool calls with icons, durations, and error badges. New API: `GET /api/sessions` returns `[{id, start, end, tool_count, error_count, tools: [...]}]`. Frontend: new tab/view with vertical timeline using CSS. Each node is a tool call; hover shows summary; click opens detail inspector (P11-T1). -- **Priority:** P1 -- **Dependencies:** P11-T1 -- **Parallelizable:** no -- **Outputs/Artifacts:** - - New module `src/mcpbridge_wrapper/webui/sessions.py` - session detection logic - - New API endpoint `GET /api/sessions` in `src/mcpbridge_wrapper/webui/server.py` - - Updated `src/mcpbridge_wrapper/webui/static/index.html` - timeline tab - - Updated `src/mcpbridge_wrapper/webui/static/dashboard.js` - timeline rendering - - Updated `src/mcpbridge_wrapper/webui/static/dashboard.css` - timeline styling - - Updated `src/mcpbridge_wrapper/webui/config.py` - `session_gap_seconds` setting - - Tests in `tests/unit/webui/test_sessions.py` -- **Acceptance Criteria:** - - [x] Sessions are detected by idle gap (configurable, default 300s) - - [x] `GET /api/sessions` returns session list with tool call summaries - - [x] Dashboard displays vertical timeline with tool call nodes - - [x] Hover on node shows tool name, latency, error status - - [x] Click on node opens detail inspector (if P11-T1 payload capture enabled) - - [x] Sessions update via periodic poll (15s) + manual Refresh - - [x] Tests cover session boundary detection, edge cases (single-call sessions, zero-gap) - ---- - -#### ✅ FU-P11-T2-1: Push session data via WebSocket for real-time timeline updates -- **Description:** Extend the WebSocket `metrics_update` message in `server.py` to include current session data from `detect_sessions()`. Update `dashboard.js` to refresh the timeline on every WebSocket message instead of using the 15s poll. This fulfills the original P11-T2 acceptance criterion of "real-time via WebSocket." -- **Priority:** P3 -- **Dependencies:** P11-T2 -- **Parallelizable:** yes -- **Outputs/Artifacts:** - - Updated `src/mcpbridge_wrapper/webui/server.py` — include sessions in WebSocket payload - - Updated `src/mcpbridge_wrapper/webui/static/dashboard.js` — consume sessions from WS message -- **Acceptance Criteria:** - - [ ] WebSocket `metrics_update` message includes `sessions` key - - [ ] Dashboard timeline updates immediately on each WebSocket push - - [ ] 15s poll fallback removed or made redundant - ---- - -#### ✅ FU-P11-T2-2: Add `limit` query param to `GET /api/sessions` -- **Description:** Add an optional `limit` query parameter (default: all, max: 10000) to `GET /api/sessions` that caps the number of audit entries fed to `detect_sessions()`. This prevents slow responses for large audit logs. -- **Priority:** P3 -- **Dependencies:** P11-T2 -- **Parallelizable:** yes -- **Outputs/Artifacts:** - - Updated `src/mcpbridge_wrapper/webui/server.py` — `limit` query param on sessions endpoint -- **Acceptance Criteria:** - - [ ] `GET /api/sessions?limit=500` fetches at most 500 most-recent entries before session grouping - - [ ] Default (no `limit`) retains current behavior (up to 10,000 entries) - - [ ] Tests updated to cover limit parameter behavior - ---- - -#### ✅ FU-P11-T2-3: Reorder sessions from the last to the first -- **Description:** Fix session ordering so the Session Timeline shows the most recent session first (newest-to-oldest). Current behavior shows the oldest session first, which makes fresh activity harder to find. -- **Priority:** P2 -- **Dependencies:** P11-T2 -- **Parallelizable:** yes -- **Outputs/Artifacts:** - - Updated `src/mcpbridge_wrapper/webui/sessions.py` — enforce newest-first session ordering - - Updated `src/mcpbridge_wrapper/webui/server.py` and/or `src/mcpbridge_wrapper/webui/static/dashboard.js` — preserve newest-first ordering in API/UI rendering - - Updated tests in `tests/unit/webui/test_sessions.py` and/or `tests/unit/webui/test_server.py` -- **Acceptance Criteria:** - - [ ] `GET /api/sessions` returns sessions ordered by latest start time first - - [ ] Timeline labels show the newest group as `SESSION 1` - - [ ] Refresh and live updates keep the same newest-first ordering - - [ ] Tests cover ordering with at least two sessions at different timestamps - ---- - -#### ✅ FU-P11-T2-4: Add one-command Web UI restart workflow -- **Description:** Add a simple restart workflow for developers and users that reliably frees the configured Web UI port and starts a fresh dashboard process after updates. -- **Priority:** P2 -- **Dependencies:** P11-T2 -- **Parallelizable:** yes -- **Outputs/Artifacts:** - - Updated `src/mcpbridge_wrapper/__main__.py` and/or helper script — support restart semantics (`stop stale listener on port`, then `start`) - - Updated `Makefile` — add a `webui-restart` target - - Updated `docs/troubleshooting.md` and `Sources/XcodeMCPWrapper/Documentation.docc/Troubleshooting.md` — document one-step restart command -- **Acceptance Criteria:** - - [ ] A single documented command restarts Web UI on a chosen port without manual PID hunting - - [ ] Restart flow attempts graceful stop first, then force-kill only if needed - - [ ] Works for both local/dev install and uvx usage - - [ ] Tests cover restart behavior and port-occupied edge case(s) where practical - ---- - -#### ✅ P11-T3: Add Dashboard Theme Toggle (Dark/Light) -- **Description:** Implement CSS-variable-based theme system with a toggle button in the header. Refactor all hardcoded colors in `dashboard.css` to CSS custom properties on `:root`. Add `[data-theme="light"]` overrides. Store user preference in `localStorage`. Update Chart.js color defaults on theme toggle. -- **Priority:** P2 -- **Dependencies:** P10-T1 -- **Parallelizable:** yes -- **Outputs/Artifacts:** - - Updated `src/mcpbridge_wrapper/webui/static/dashboard.css` - CSS variable refactor + light theme - - Updated `src/mcpbridge_wrapper/webui/static/dashboard.js` - theme toggle logic and Chart.js color sync - - Updated `src/mcpbridge_wrapper/webui/static/index.html` - theme toggle button in header -- **Acceptance Criteria:** - - [ ] All colors in CSS use custom properties (no hardcoded hex in selectors) - - [ ] Toggle button switches between dark and light themes - - [ ] Chart.js chart colors update on theme change without page reload - - [ ] Theme preference persists across page reloads via `localStorage` - - [ ] Default theme matches current dark theme (no visual regression) - ---- - -#### ✅ P11-T4: Add Keyboard Shortcuts & Command Palette -- **Description:** Add lightweight keyboard shortcuts for dashboard navigation. `1-4` to focus chart sections, `a` to jump to audit log, `r` to reset metrics (with confirmation), `e` to export JSON, `?` to show shortcut help overlay. Pure JS `keydown` listener with a shortcut map. Small modal overlay for `?` help. No library needed. -- **Priority:** P3 -- **Dependencies:** P10-T1 -- **Parallelizable:** yes -- **Outputs/Artifacts:** - - Updated `src/mcpbridge_wrapper/webui/static/dashboard.js` - shortcut handler - - Updated `src/mcpbridge_wrapper/webui/static/dashboard.css` - help overlay styling - - Updated `src/mcpbridge_wrapper/webui/static/index.html` - help overlay markup -- **Acceptance Criteria:** - - [x] `?` key opens/closes shortcut help overlay - - [x] Number keys `1-4` scroll to corresponding chart section - - [x] `a` key scrolls to audit log section - - [x] `r` key triggers reset metrics with confirmation dialog - - [x] `e` key triggers JSON export download - - [x] Shortcuts are disabled when focus is in an input field (audit filter) - - [x] Help overlay lists all available shortcuts with descriptions - ---- - -### Phase 12: Data Collection Enhancements - -**Intent:** Enrich collected telemetry with client identity, parameter patterns, and structured error classification for deeper operational insight. - -#### ✅ P12-T1: Add MCP Client Identification -- **Description:** Detect the calling MCP client from the `initialize` handshake. The `clientInfo` field in the initialize request contains `{name, version}`. Capture this and tag all subsequent metrics with the client identity. Add `client` column to shared metrics SQLite schema. Dashboard: new KPI card "Active Client" showing the connected client name and version. Charts: optional client-based breakdown in tool usage. -- **Priority:** P0 -- **Dependencies:** P10-T1 -- **Parallelizable:** yes -- **Outputs/Artifacts:** - - Updated `src/mcpbridge_wrapper/__main__.py` - extract `clientInfo` from initialize request - - Updated `src/mcpbridge_wrapper/schemas.py` - add `MCPInitializeParams` model with `clientInfo` - - Updated `src/mcpbridge_wrapper/webui/metrics.py` - `client_name` field in metrics - - Updated `src/mcpbridge_wrapper/webui/shared_metrics.py` - `client` column in SQLite schema - - Updated `src/mcpbridge_wrapper/webui/server.py` - expose client info in metrics summary - - Updated `src/mcpbridge_wrapper/webui/static/index.html` - "Active Client" KPI card - - Updated `src/mcpbridge_wrapper/webui/static/dashboard.js` - render client KPI - - Tests in `tests/unit/test_main.py` and `tests/unit/webui/test_metrics.py` -- **Acceptance Criteria:** - - [ ] `initialize` request `clientInfo.name` and `clientInfo.version` are captured - - [ ] Metrics summary includes `client_name` and `client_version` fields - - [ ] Dashboard displays "Active Client" KPI card (e.g. "Cursor 1.2.3") - - [ ] Metrics reset clears client info - - [ ] If `initialize` has no `clientInfo`, fields default to "unknown" - - [ ] SQLite schema migration is backward-compatible (nullable column) - - [ ] Tests cover initialize parsing, missing clientInfo, and metric tagging - ---- - -#### ✅ P12-T2: Add Tool Parameter Frequency Analysis -- **Description:** Optionally capture and aggregate tool call parameter keys (not values by default) for pattern analysis. Config flag `capture_params: bool` (default off). On request capture, extract `params.arguments` key names. Store parameter key signatures per tool (e.g. `XcodeGrep(pattern, path, tabIdentifier)`). New API: `GET /api/analytics/param-patterns?tool=` returns top-N parameter combinations. Dashboard: expandable section in latency table showing common param combos. -- **Priority:** P3 -- **Dependencies:** P12-T1 -- **Parallelizable:** yes -- **Outputs/Artifacts:** - - Updated `src/mcpbridge_wrapper/__main__.py` - extract argument keys from tool call params - - New module or section in `src/mcpbridge_wrapper/webui/metrics.py` - param pattern aggregation - - Updated `src/mcpbridge_wrapper/webui/shared_metrics.py` - `param_keys` column in requests table - - New API endpoint `GET /api/analytics/param-patterns` in `src/mcpbridge_wrapper/webui/server.py` - - Updated `src/mcpbridge_wrapper/webui/config.py` - `capture_params` flag - - Updated `src/mcpbridge_wrapper/webui/static/dashboard.js` - param pattern display - - Tests in `tests/unit/webui/test_metrics.py` -- **Acceptance Criteria:** - - [ ] `capture_params: true` enables parameter key capture - - [ ] Only argument key names are stored (not values) by default - - [ ] `GET /api/analytics/param-patterns?tool=XcodeGrep` returns ranked param combos - - [ ] Dashboard shows expandable param pattern info per tool - - [ ] Default behavior (flag off) is unchanged — no extra capture overhead - - [ ] Tests cover param extraction, aggregation, and API response format - ---- - -#### ✅ P12-T3: Add Error Classification & Categorization -- **Description:** Parse JSON-RPC error codes and messages from responses. Categorize into buckets: protocol errors (-326xx), tool execution errors (Xcode-side failures), timeout errors, connection errors. Extend `record_response` to accept `error_code: Optional[int]` and `error_message: Optional[str]`. New metrics: `error_counts_by_code: Dict[int, int]`. Dashboard: replace single "Total Errors" KPI with error breakdown doughnut chart. Audit table: color-code error column by severity. -- **Priority:** P1 -- **Dependencies:** P10-T1 -- **Parallelizable:** yes -- **Outputs/Artifacts:** - - Updated `src/mcpbridge_wrapper/__main__.py` - extract error code and message from responses - - Updated `src/mcpbridge_wrapper/schemas.py` - expose `error.code` and `error.message` accessors - - Updated `src/mcpbridge_wrapper/webui/metrics.py` - `error_counts_by_code` tracking - - Updated `src/mcpbridge_wrapper/webui/shared_metrics.py` - `error_code` and `error_message` columns - - Updated `src/mcpbridge_wrapper/webui/server.py` - expose error breakdown in metrics summary - - Updated `src/mcpbridge_wrapper/webui/static/index.html` - error breakdown chart container - - Updated `src/mcpbridge_wrapper/webui/static/dashboard.js` - error doughnut chart + audit color-coding - - Updated `src/mcpbridge_wrapper/webui/static/dashboard.css` - error severity color classes - - Tests in `tests/unit/webui/test_metrics.py` and `tests/unit/webui/test_shared_metrics.py` -- **Acceptance Criteria:** - - [ ] JSON-RPC error code and message are extracted from error responses - - [ ] Metrics summary includes `error_counts_by_code` map (e.g. `{-32600: 5, -32601: 2}`) - - [ ] Dashboard displays error breakdown doughnut chart alongside or replacing "Total Errors" KPI - - [ ] Audit table error column is color-coded by severity (red for protocol, orange for tool, yellow for timeout) - - [ ] Error categories are defined: protocol (-326xx), tool (positive codes), timeout, unknown - - [ ] Non-error responses leave error code/message as null - - [ ] Tests cover code extraction, categorization, and metric aggregation - ---- - -#### ✅ P12-T4: Add documentation about data storage -- **Description:** Document the structure of all data storage containers used by mcpbridge-wrapper, including the SQLite database schema (`shared_metrics.db`), the in-memory metrics structures (`MetricsCollector`, `SharedMetricsCollector`), audit log format, and any other persistent or transient data containers. This documentation should explain table schemas, column semantics, retention policies, and how data flows between components (e.g. from `__main__.py` capture → `metrics.py` aggregation → `shared_metrics.py` persistence → Web UI API). -- **Priority:** P2 -- **Dependencies:** P12-T1, P12-T3 -- **Parallelizable:** yes -- **Outputs/Artifacts:** - - New `docs/data-storage.md` - comprehensive reference for all data containers - - Updated docstrings in `src/mcpbridge_wrapper/webui/shared_metrics.py` describing each table/column - - Updated docstrings in `src/mcpbridge_wrapper/webui/metrics.py` describing in-memory structures - - Optional: ER diagram or table relationship overview in `docs/data-storage.md` -- **Acceptance Criteria:** - - [ ] SQLite schema documented with all tables, columns, types, and nullability - - [ ] In-memory `MetricsCollector` and `SharedMetricsCollector` fields documented - - [ ] Audit log format (CSV export columns and semantics) documented - - [ ] Data flow from capture to storage to API explained - - [ ] Retention/reset behavior documented (e.g. what resets on metrics clear) - - [ ] Document is discoverable from `README.md` or `docs/` index - ---- - -### Phase 13: Persistent Broker & Shared Xcode Session - -**Intent:** Introduce a long-lived broker process that owns the Xcode bridge connection and multiplexes multiple MCP clients through one upstream session. - -#### ✅ P13-T1: Design persistent broker architecture and protocol contract -- **Status:** ✅ Completed (2026-02-16) -- **Description:** Define daemon lifecycle, client transport choice (Unix domain socket first), request/response correlation strategy, reconnect behavior, and failure boundaries between broker, upstream bridge, and client proxies. -- **Priority:** P0 -- **Dependencies:** P2-T6, P3-T10 -- **Parallelizable:** no -- **Outputs/Artifacts:** - - Broker architecture spec (sequence diagrams and lifecycle states) - - ADR documenting transport and security choices - - Initial module scaffold under `src/mcpbridge_wrapper/broker/` -- **Acceptance Criteria:** - - [ ] Architecture covers startup, shutdown, reconnect, and stale-socket recovery - - [ ] Correlation strategy for concurrent JSON-RPC requests is specified - - [ ] Security boundary for local clients is documented (socket permissions/token) - - [ ] Design is reviewed and approved for implementation - ---- - -#### ✅ P13-T2: Implement persistent broker daemon with single upstream Xcode bridge -- **Description:** Add daemon mode that launches and owns one `xcrun mcpbridge` subprocess, keeps it alive, and exposes broker readiness state to clients. -- **Priority:** P0 -- **Dependencies:** P13-T1 -- **Parallelizable:** no -- **Outputs/Artifacts:** - - `src/mcpbridge_wrapper/broker/daemon.py` - - PID/lock handling + stale lock cleanup - - Health endpoint or status command (`broker status`) -- **Acceptance Criteria:** - - [ ] Starting broker twice does not spawn duplicate upstream bridge instances - - [ ] Broker survives client disconnects without restarting upstream bridge - - [ ] Graceful shutdown terminates upstream process and cleans lock/socket files - - [ ] Crash recovery path is covered by tests - ---- - -#### ✅ FU-P13-T2-1: Replace run_forever() polling loop with asyncio.Event-based wait -- **Status:** ✅ Completed (2026-02-18) -- **Type:** Enhancement -- **Priority:** P3 -- **Discovered:** 2026-02-17 (REVIEW_P13-T2) -- **Component:** BrokerDaemon.run_forever() -- **Description:** Current implementation uses `asyncio.sleep(0.1)` polling which introduces up to 100ms stop-signal latency. Replace with `asyncio.Event.wait()` for idiomatic zero-latency shutdown. -- **Acceptance Criteria:** - - [x] `run_forever()` responds to stop signal within one event loop tick - - [x] Existing `test_run_forever_starts_and_stops` passes without change - ---- - -#### ✅ FU-P13-T2-2: Move PID file write to after successful upstream launch -- **Status:** ✅ Completed (2026-02-18) -- **Type:** Robustness -- **Priority:** P3 -- **Discovered:** 2026-02-17 (REVIEW_P13-T2) -- **Component:** BrokerDaemon.start() -- **Description:** PID file is currently written before upstream subprocess is launched. A crash between write and launch leaves a live-PID lock that blocks future starts until the owning process dies. Move the write to after successful launch. -- **Acceptance Criteria:** - - [x] PID file is written only after `_launch_upstream()` succeeds - - [x] Stale-lock tests continue to pass - ---- - -#### ✅ P13-T3: Implement multi-client transport and JSON-RPC multiplexing -- **Description:** Add local transport server (Unix socket) that accepts multiple clients and multiplexes JSON-RPC traffic to/from the single upstream bridge while preserving per-client response routing. -- **Priority:** P0 -- **Dependencies:** P13-T2 -- **Parallelizable:** no -- **Outputs/Artifacts:** - - `src/mcpbridge_wrapper/broker/transport.py` - - Client session manager and request ID routing map - - Backpressure/queue limits and timeout handling -- **Status:** ✅ Completed 2026-02-18 -- **Acceptance Criteria:** - - [x] At least two concurrent clients can perform tool calls successfully - - [x] Responses are routed back to the correct client/request - - [x] Broker handles malformed client payloads without affecting other clients - - [x] Queue/timeout behavior is tested and deterministic - ---- - -#### ✅ P13-T4: Add stdio proxy mode for compatibility with existing MCP clients -- **Description:** Implement a proxy mode where standard MCP clients still use stdio, but the wrapper process forwards traffic to the persistent local broker instead of spawning a new upstream bridge. -- **Priority:** P1 -- **Dependencies:** P13-T3 -- **Parallelizable:** yes -- **Outputs/Artifacts:** - - CLI flags for broker usage (e.g., `--broker-connect`, `--broker-spawn`) - - Proxy adapter module under `src/mcpbridge_wrapper/broker/proxy.py` - - Backward-compatible default behavior toggle strategy -- **Acceptance Criteria:** - - [x] Existing MCP client configs can opt into broker mode with minimal changes - - [x] Proxy process exit does not terminate broker daemon - - [x] Legacy direct mode remains available for fallback - - [x] Unit tests cover proxy connect/disconnect and reconnect behavior - ---- - -#### ✅ 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:** - - [x] All `asyncio.get_event_loop()` calls in proxy.py replaced with `asyncio.get_running_loop()` - - [x] Tests still pass - ---- - -#### ✅ FU-P13-T4-2: Implement or remove reconnect parameter in BrokerProxy -- **Status:** ✅ Completed (2026-02-18) -- **Description:** The `reconnect: bool` parameter is stored but never used in `_run_bridge`. Either implement the reconnect loop (retry once on broken socket before stdin EOF) or remove the parameter entirely and add a comment referencing P13-T5. -- **Priority:** P2 -- **Dependencies:** P13-T4 -- **Parallelizable:** yes -- **Outputs/Artifacts:** - - Updated `src/mcpbridge_wrapper/broker/proxy.py` - - Updated `tests/unit/test_broker_proxy.py` -- **Acceptance Criteria:** - - [x] `reconnect=True` either reconnects on broken socket or the parameter is removed - - [x] No dead/unused code remains - - [x] Tests pass - ---- - -#### ✅ P13-T5: Validate prompt reduction and multi-client stability -- **Status:** ❌ FAIL (2026-02-19, broker-mode validation blocked by UID mismatch rejection) -- **Description:** Add integration and manual verification that repeated short-lived client sessions can reuse the broker session without repeated upstream churn, plus load tests for concurrent calls. -- **Priority:** P1 -- **Dependencies:** P13-T4 -- **Parallelizable:** no -- **Outputs/Artifacts:** - - `tests/integration/test_broker_multi_client.py` - - Manual validation report for Xcode permission prompt behavior - - Metrics comparison (direct mode vs broker mode process churn) -- **Acceptance Criteria:** - - [x] Sequential short-lived clients reuse one broker-owned upstream bridge process - - [x] Concurrent client tool calls remain stable under load - - [x] Manual prompt criterion resolved in FU-P13-T14 (**FAIL**: broker-mode proxy sessions rejected with `-32003 UID mismatch`) - - [x] Regression suite passes with broker mode enabled - ---- - -#### ✅ P13-T6: Document broker mode configuration, migration, and rollback -- **Status:** ✅ PASS (2026-02-18) -- **Description:** Update setup and troubleshooting docs for broker mode adoption, including client config examples, operational commands, limitations, and rollback to direct mode. -- **Priority:** P1 -- **Dependencies:** P13-T4 -- **Parallelizable:** yes -- **Outputs/Artifacts:** - - Updated `README.md`, `docs/cursor-setup.md`, `docs/claude-setup.md`, `docs/codex-setup.md`, `docs/troubleshooting.md` - - Added `docs/broker-mode.md` deep-dive - - Config templates with broker-mode variants -- **Acceptance Criteria:** - - [x] Docs include one-command start/stop/status flows for broker mode - - [x] Client examples are provided for Codex/Cursor/Claude - - [x] Troubleshooting includes socket/lock and stale-broker recovery - - [x] Rollback steps to direct mode are explicit and tested - ---- - -#### ✅ FU-P13-T7: Enforce strict `structuredContent` compliance for empty-content tool results -- **Description:** Fix transformation logic so strict MCP clients no longer fail when a tool response includes `result.content: []` without `result.structuredContent`. Add a fallback injection strategy for transformable tool results with empty content. -- **Priority:** P0 -- **Dependencies:** P3-T3, P4-T1, P5-T6 -- **Parallelizable:** yes -- **Outputs/Artifacts:** - - Updated `src/mcpbridge_wrapper/transform.py` transformation conditions for empty-content results - - Updated `tests/unit/test_transform.py` coverage for strict empty-content compliance - - Updated troubleshooting/docs note clarifying strict-client behavior -- **Acceptance Criteria:** - - [ ] For tool responses missing `structuredContent`, empty `content` results are normalized to include `structuredContent` fallback - - [ ] Existing already-compliant responses remain unchanged - - [ ] Non-tool JSON-RPC notifications and unrelated payloads are not regressed - - [ ] New unit tests fail before fix and pass after fix - ---- - -#### ✅ FU-P13-T8: Prevent Web UI port collision from destabilizing MCP sessions -- **Description:** Harden startup behavior when `--web-ui` port is already occupied (common with stale/orphan wrapper processes). Ensure collision handling is deterministic and does not silently degrade MCP client stability. -- **Priority:** P0 -- **Dependencies:** P10-T1 -- **Parallelizable:** yes -- **Status:** ✅ Implemented (2026-02-16) -- **Outputs/Artifacts:** - - `run_server()` catches `SystemExit` from uvicorn bind failure (TOCTOU window) - - TOCTOU regression test in `tests/unit/test_main_webui.py` - - Stale-process troubleshooting docs in `docs/troubleshooting.md` (FU-BUG-T6-1) -- **Acceptance Criteria:** - - [x] When requested Web UI port is occupied, wrapper behavior is explicit and deterministic (clear error or safe fallback) - - [x] MCP stdio protocol output remains valid JSON-RPC only on stdout - - [x] Repeated client startups no longer accumulate conflicting Web UI listeners on the same port - - [x] Tests cover occupied-port and restart scenarios - ---- - -#### ✅ FU-P13-T9: Normalize unsupported `resources/*` method failures to standard JSON-RPC errors -- **Description:** Add protocol normalization for non-tool method failures where upstream returns tool-style `result.isError/content` payloads. Convert these into standard JSON-RPC `error` envelopes for strict MCP clients. -- **Priority:** P0 -- **Status:** ✅ Implemented in BUG-T7 (2026-02-14) -- **Dependencies:** P3-T10 -- **Parallelizable:** yes -- **Outputs/Artifacts:** - - Updated response normalization logic in `src/mcpbridge_wrapper/__main__.py` and/or `src/mcpbridge_wrapper/transform.py` - - Request/response correlation support for method-aware normalization - - Regression tests for `resources/list` and `resources/templates/list` compatibility -- **Acceptance Criteria:** - - [x] Unsupported non-tool methods return JSON-RPC `error` responses with stable code/message shape - - [x] Codex/Cursor strict MCP paths no longer report "Unexpected response type" for normalized unsupported methods - - [x] Tool-call success/error behavior remains backward compatible - - [x] Integration tests cover normalization without false positives on valid tool results - ---- - -#### ✅ FU-P13-T10: Implement explicit broker daemon entrypoint and operational CLI flows -- **Description:** Make broker host mode first-class by implementing a real daemon entrypoint (`--broker-daemon` or equivalent broker subcommand) in `__main__.py`, ensuring `--broker-spawn` can reliably auto-start and connect. Replace doc-only one-liner operational flows with supported CLI commands for start/status/stop. -- **Priority:** P0 -- **Dependencies:** P13-T2, P13-T4 -- **Parallelizable:** no -- **Outputs/Artifacts:** - - Updated `src/mcpbridge_wrapper/__main__.py` broker daemon branch and command parsing - - Updated `src/mcpbridge_wrapper/broker/proxy.py` spawn target (if needed) - - Integration test covering `--broker-spawn` end-to-end readiness - - Updated `docs/broker-mode.md` and setup docs with first-class broker host commands -- **Acceptance Criteria:** - - [ ] Running `mcpbridge-wrapper --broker-daemon` starts broker host mode and creates live PID/socket state - - [ ] `--broker-spawn` successfully auto-starts broker and connects without manual bootstrap - - [ ] No broker-only flags are accidentally forwarded to `xcrun mcpbridge` - - [ ] Start/status/stop commands are documented as supported CLI flows (not private inline Python snippets) - ---- - -#### ✅ FU-P13-T11: Preserve JSON-RPC numeric request ID fidelity in broker transport -- **Status:** ✅ Completed (2026-02-19) -- **Description:** Replaced lossy 20-bit integer ID bitmask with a reversible per-session counter (`_alloc_local_id`) and reverse map (`id_restore`). Large, negative, and concurrent integer IDs now round-trip exactly. O(1) restore replaces previous O(n) scan. -- **Priority:** P1 -- **Dependencies:** P13-T3 -- **Parallelizable:** yes -- **Outputs/Artifacts:** - - Updated `src/mcpbridge_wrapper/broker/transport.py` ID remap/restore strategy - - Updated `src/mcpbridge_wrapper/broker/types.py` session mapping fields - - New/updated unit tests for large, negative, and concurrent numeric IDs -- **Acceptance Criteria:** - - [x] Integer IDs (including negative and > 20-bit) are returned unchanged to clients - - [x] Distinct concurrent numeric IDs cannot collide within a session - - [x] Existing string-ID routing behavior remains backward compatible - - [x] Broker transport tests cover ID round-trip fidelity for int and string IDs - ---- - -#### ✅ FU-P13-T12: Enforce local Unix-socket security boundary for broker clients -- **Status:** ✅ Completed (2026-02-19) -- **Description:** Implement same-UID peer credential verification for broker socket clients and enforce owner-only socket permissions, aligning runtime behavior with P13-T1 ADR security decisions. -- **Priority:** P1 -- **Dependencies:** P13-T1, P13-T3 -- **Parallelizable:** yes -- **Outputs/Artifacts:** - - Updated `src/mcpbridge_wrapper/broker/transport.py` peer credential checks and rejection path - - Updated broker socket creation flow to enforce `0600` permissions - - Unit tests for accepted/rejected client credential cases - - Documentation update in `docs/broker-mode.md` and/or `docs/troubleshooting.md` -- **Acceptance Criteria:** - - [x] Broker accepts only same-UID local clients - - [x] Connections failing UID verification are rejected without affecting active sessions - - [x] Broker socket file is owner-readable/writable only (`0600`) - - [x] Security-boundary behavior is documented and test-covered - ---- - -#### ✅ FU-P13-T13: Make broker startup transactional when transport bind/start fails — Completed (2026-02-19) -- **Description:** Harden `BrokerDaemon.start()` so partial startup failures (for example socket bind errors after upstream launch) perform full rollback, leaving no orphaned upstream process or stale PID/socket files. -- **Priority:** P1 -- **Dependencies:** P13-T2, P13-T3 -- **Parallelizable:** yes -- **Outputs/Artifacts:** - - Updated `src/mcpbridge_wrapper/broker/daemon.py` startup failure rollback path - - Regression tests for transport-start failure after upstream launch - - Troubleshooting note for deterministic failure behavior -- **Acceptance Criteria:** - - [x] If transport startup fails, upstream subprocess is terminated and waited - - [x] PID/socket files are cleaned up on startup failure - - [x] Broker state returns to a safe non-ready state after rollback - - [x] Unit tests cover rollback behavior and prevent regression - ---- - -#### ✅ FU-P13-T13-FU-1: Set _stopped_event and _stop_event in _rollback_startup for defensive consistency — Completed (2026-02-19, PASS) -- **Description:** After `_rollback_startup()` sets state to STOPPED, also call `self._stopped_event.set()` and `self._stop_event.set()` so all event states are consistent with the STOPPED contract. These paths are currently unreachable by callers but the defensive fix ensures correctness if future callers are added. -- **Priority:** P3 (Low — optional defensibility improvement) -- **Dependencies:** FU-P13-T13 (✅) -- **Parallelizable:** yes -- **Outputs/Artifacts:** - - Updated `src/mcpbridge_wrapper/broker/daemon.py` `_rollback_startup()` method - - Updated tests asserting event state after rollback -- **Acceptance Criteria:** - - [x] `_stopped_event.set()` called in `_rollback_startup()` - - [x] `_stop_event.set()` called in `_rollback_startup()` - - [x] Tests verify event states are set after a failed startup - ---- - -#### ✅ FU-P13-T14: Complete interactive Xcode prompt verification and close P13-T5 — Completed (2026-02-19, FAIL) -- **Description:** Execute and document the remaining human-run interactive validation for Xcode permission prompts in direct mode vs broker mode, then update P13-T5 verdict and linked acceptance states. -- **Priority:** P1 -- **Dependencies:** P13-T5, P13-T6 -- **Parallelizable:** no -- **Outputs/Artifacts:** - - Updated `SPECS/ARCHIVE/P13-T5_Validate_prompt_reduction_and_multi_client_stability/P13-T5_manual_prompt_validation.md` - - Updated `SPECS/ARCHIVE/P13-T5_Validate_prompt_reduction_and_multi_client_stability/P13-T5_Validation_Report.md` - - Workplan status update for P13-T5 acceptance line items -- **Acceptance Criteria:** - - [x] Interactive desktop run confirms observed prompt behavior for repeated short-lived sessions - - [x] P13-T5 manual prompt criterion is resolved to PASS or FAIL with concrete evidence (resolved to **FAIL**) - - [x] Any discovered deviations are captured in troubleshooting and/or follow-up bug tasks (`FU-P13-T15`) - - [x] BUG-T4 related resolution path is reconciled with the final validation outcome - ---- - -#### ✅ FU-P13-T15: Restore broker same-UID client acceptance when peer credential APIs are unavailable — Completed (2026-02-19, PASS) -- **Description:** Broker mode currently rejects same-user local clients with `-32003 UID mismatch` when peer credential lookup returns `Errno 42 (Protocol not available)`. Implement a platform-safe credential verification fallback that preserves local security boundaries while allowing same-UID clients to connect. -- **Priority:** P1 -- **Dependencies:** FU-P13-T12, FU-P13-T14 -- **Parallelizable:** no -- **Outputs/Artifacts:** - - Updated `src/mcpbridge_wrapper/broker/transport.py` peer credential verification path and fallback handling - - Added/updated tests covering `Errno 42`/unsupported credential API behavior -- **Acceptance Criteria:** - - [x] Same-user local broker clients connect successfully on environments where current credential path returns `Errno 42` - - [x] Cross-UID or unverifiable peers are still rejected with deterministic security errors - - [x] Integration tests for broker multi-client flows pass in supported local environments - ---- - -#### ✅ FU-P13-T16: Document multi-agent MCP usage and single Web UI host -- **Status:** ✅ Completed (2026-02-28, PASS) -- **Description:** Updated multi-agent documentation to clarify Web UI ownership, explain why MCP can remain healthy while the dashboard is unavailable, and document stable multi-agent topologies for both broker mode and direct mode. -- **Priority:** P1 -- **Dependencies:** P13-T6, FU-P13-T8, FU-P13-T10 -- **Parallelizable:** yes -- **Outputs/Artifacts:** - - Updated `README.md` with explicit multi-agent guidance and Web UI ownership notes - - Updated `docs/broker-mode.md` with multi-agent topology details and broker/Web UI behavior notes - - Updated `docs/webui-setup.md` with a dedicated multi-agent ownership model section - - Updated `docs/troubleshooting.md` with diagnostics for reachable MCP + unreachable dashboard states -- **Acceptance Criteria:** - - [x] Documentation states that only one process can bind a given Web UI `host:port` - - [x] Documentation explains why MCP can be healthy while Web UI is unavailable - - [x] Documentation provides a dedicated broker host + `--broker-connect` client pattern - - [x] Troubleshooting includes concrete checks for listener ownership and port conflicts - ---- - -#### ✅ FU-P13-T17: Enable broker-hosted Web UI with shared multi-client telemetry — Completed (2026-02-28, PASS) -- **Status:** ✅ Completed (2026-02-28, PASS) -- **Description:** Implement a broker-mode runtime path where `--broker-daemon --web-ui` starts both the persistent broker and dashboard in one host process, and broker-side request/response telemetry feeds one shared Web UI view for all connected agents. -- **Priority:** P0 -- **Dependencies:** P13-T10, FU-P13-T8 -- **Parallelizable:** no -- **Outputs/Artifacts:** - - Updated `src/mcpbridge_wrapper/__main__.py` to support broker-daemon + Web UI startup orchestration - - Updated `src/mcpbridge_wrapper/broker/transport.py` to record tool telemetry for broker-routed clients - - Updated `src/mcpbridge_wrapper/broker/proxy.py` to propagate Web UI spawn args in `--broker-spawn` flows - - Updated unit tests in `tests/unit/test_main.py`, `tests/unit/test_broker_proxy.py`, and `tests/unit/test_broker_transport.py` -- **Acceptance Criteria:** - - [x] `mcpbridge-wrapper --broker-daemon --web-ui --web-ui-config ` starts broker socket and dashboard without launching direct-mode bridge loop - - [x] `mcpbridge-wrapper --broker-spawn --web-ui --web-ui-config ` can auto-start a broker host with Web UI enabled - - [x] Tool calls from multiple broker-connected clients appear in one dashboard metrics/audit stream - - [x] Existing direct mode and broker-only behavior remain backward compatible - ---- - -#### ✅ FU-P13-T18: Document unified single-config setup for broker + Web UI multi-agent workflows — Completed (2026-02-28, PASS) -- **Status:** ✅ Completed (2026-02-28, PASS) -- **Description:** Update setup and troubleshooting docs so users can apply one MCP config across agents while reusing a shared broker host and one dashboard endpoint. -- **Priority:** P1 -- **Dependencies:** FU-P13-T17 -- **Parallelizable:** yes -- **Outputs/Artifacts:** - - Updated `README.md`, `docs/broker-mode.md`, `docs/webui-setup.md`, and `docs/troubleshooting.md` - - Updated mapped DocC files in `Sources/XcodeMCPWrapper/Documentation.docc/` -- **Acceptance Criteria:** - - [x] Docs include one-config examples for Zed/Cursor/Claude/Codex with broker + dashboard expectations - - [x] Docs clearly define dashboard ownership and fallback behavior - - [x] Troubleshooting includes broker-hosted Web UI diagnostics - ---- - -#### ✅ FU-P13-T19: Add integration coverage for broker-hosted Web UI observability — Completed (2026-02-28, PASS) -- **Status:** ✅ Completed (2026-02-28, PASS) -- **Description:** Added integration coverage validating broker-hosted Web UI observability by exercising multi-client broker traffic and asserting aggregated/error telemetry via `/api/metrics` and `/api/audit`. -- **Priority:** P1 -- **Dependencies:** FU-P13-T17 -- **Parallelizable:** yes -- **Outputs/Artifacts:** - - Added `tests/integration/webui/test_broker_observability.py` - - Added `SPECS/ARCHIVE/FU-P13-T19_Add_integration_coverage_for_broker-hosted_Web_UI_observability/FU-P13-T19_Validation_Report.md` -- **Acceptance Criteria:** - - [x] Tests demonstrate aggregated metrics visibility for broker-connected clients - - [x] Tests cover at least one error-path request and verify error reporting in metrics/audit output - - [x] CI remains stable without flaky timing assumptions - ---- - -### Phase 14: Release 0.4.0 Readiness - -#### ✅ P14-T5: Stabilize broker Unix-socket permission test against path-length limits — Completed (2026-02-20, PASS) -- **Description:** Make the socket-permission regression test deterministic across local environments by avoiding Unix domain socket path overflows in pytest temporary directories, while preserving verification of `0600` permissions. -- **Priority:** P1 -- **Dependencies:** FU-P13-T12 -- **Parallelizable:** yes -- **Outputs/Artifacts:** - - Updated `tests/unit/test_broker_transport.py` socket-permission test setup to use a safe short socket path - - Validation evidence that `pytest -q` passes without requiring `--basetemp` -- **Acceptance Criteria:** - - [x] `tests/unit/test_broker_transport.py::TestSocketPermissions::test_socket_created_with_0600_permissions` passes on macOS with default pytest temp paths - - [x] Full `pytest -q` passes without `AF_UNIX path too long` - - [x] Test still verifies socket mode is exactly `0o600` - ---- - -#### ✅ FU-P14-T5-1: Add macOS CI execution for broker socket-path regression coverage — Completed (2026-02-20, PASS) -- **Description:** Extend GitHub Actions CI with a macOS test path (similar to dedicated workflow lanes such as DocC) so the AF_UNIX path-length-sensitive broker socket test is exercised on macOS runners during PR validation. -- **Priority:** P1 -- **Dependencies:** P14-T5 -- **Parallelizable:** yes -- **Outputs/Artifacts:** - - Updated `.github/workflows/ci.yml` (or a dedicated workflow) to run broker transport tests on `macos-latest` - - CI documentation note describing why macOS coverage is required for AF_UNIX path-length behavior -- **Acceptance Criteria:** - - [x] GitHub Actions runs the broker socket permission/path regression test on a macOS runner for pull requests - - [x] The macOS job status is visible in PR checks and gates merges on failure - - [x] Existing Linux test matrix behavior remains unchanged - ---- - -#### ✅ P14-T1: Bound per-session ID restore maps in broker transport — Completed (2026-02-20, PASS) -- **Description:** Prevent unbounded memory growth in long-lived broker sessions by pruning/removing alias/restore entries once responses are routed (and define safe behavior when local ID space wraps). -- **Priority:** P1 -- **Dependencies:** FU-P13-T11, FU-P13-T15 -- **Parallelizable:** no -- **Outputs/Artifacts:** - - Updated `src/mcpbridge_wrapper/broker/transport.py` map lifecycle management for `id_restore`, `string_id_map`, and `int_id_map` - - Regression tests in `tests/unit/test_broker_transport.py` for long-lived/large-ID request streams -- **Acceptance Criteria:** - - [x] Per-session restore/alias maps do not grow unbounded for completed requests - - [x] Existing ID round-trip fidelity guarantees remain intact for int and string IDs - - [x] Tests cover wrap/prune behavior and pass in CI - ---- - -#### ✅ P14-T3: Reconcile declared Python support with tested matrix — Completed (2026-02-20, PASS) -- **Description:** Resolve mismatch between declared Python compatibility and CI coverage by aligning `requires-python`/classifiers/docs with supported and continuously-tested interpreter versions. -- **Priority:** P1 -- **Dependencies:** none -- **Parallelizable:** yes -- **Outputs/Artifacts:** - - Updated compatibility declarations in `pyproject.toml` and documentation badges/text - - Updated `.github/workflows/ci.yml` matrix and/or version floor to match declared support -- **Acceptance Criteria:** - - [x] Declared Python support exactly matches tested CI versions - - [x] README and packaging metadata communicate the same minimum Python version - - [x] CI passes on the finalized support matrix - ---- - -#### ✅ P14-T4: Replace deprecated setuptools license metadata with SPDX format — Completed (2026-02-20, PASS) -- **Description:** Remove packaging deprecation warnings by migrating license metadata to modern SPDX-based fields and dropping deprecated classifiers/structures. -- **Priority:** P2 -- **Dependencies:** none -- **Parallelizable:** yes -- **Outputs/Artifacts:** - - Updated `pyproject.toml` license metadata (`project.license` / `license-files` / classifiers as needed) - - Validation evidence from `python -m build` showing deprecation warnings are resolved -- **Acceptance Criteria:** - - [x] Build output no longer emits setuptools license deprecation warnings - - [x] Package metadata remains valid for PyPI and MCP registry publication - - [x] Existing `make check` pipeline remains green - ---- - -#### ✅ P14-T2: Align release metadata and changelog for 0.4.0 — Completed (2026-02-20, PASS) -- **Description:** Prepare publishable 0.4.0 release metadata by updating package/registry versions and adding a complete changelog entry matching delivered functionality. -- **Priority:** P1 -- **Dependencies:** P14-T1, P14-T3, P14-T4 -- **Parallelizable:** no -- **Outputs/Artifacts:** - - Updated version fields in `pyproject.toml` and `server.json` - - New `0.4.0` entry in `CHANGELOG.md` with release date, key changes, and release link -- **Acceptance Criteria:** - - [x] `pyproject.toml`, `server.json`, and `CHANGELOG.md` all reference `0.4.0` consistently - - [x] Changelog includes accurate notes for broker and Web UI work shipped since `0.3.2` - - [x] Release metadata passes existing build/publish validation checks - ---- - - -#### ✅ 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 -- **Parallelizable:** yes -- **Outputs/Artifacts:** - - Updated `pending_methods` handling in `src/mcpbridge_wrapper/__main__.py` - - Unit test that exercises high-volume in-flight requests without responses -- **Acceptance Criteria:** - - [x] `pending_methods` does not grow beyond a capped size under any traffic pattern - - [x] Existing BUG-T7 normalization behavior is unaffected - ---- - -#### ✅ FU-P12-T1-1: Remove or document `MCPInitializeParams` in schemas -- **Status:** ✅ Completed (2026-02-18) -- **Description:** `MCPInitializeParams` was added to `schemas.py` during P12-T1 but is not used anywhere in the codebase — `MCPParams.clientInfo` covers the same purpose. Either remove it to reduce confusion, or add a usage (e.g., a helper or test) that justifies its existence as a public export. -- **Priority:** P3 -- **Dependencies:** P12-T1 -- **Parallelizable:** yes -- **Outputs/Artifacts:** - - Updated `src/mcpbridge_wrapper/schemas.py` — `MCPInitializeParams` removed or documented with usage -- **Acceptance Criteria:** - - [x] `MCPInitializeParams` is either removed or has a clear, tested usage - - [x] `pytest` suite remains green - ---- - -#### ✅ FU-P12-T1-2: Add code comment clarifying stdin-only client capture in `on_request` -- **Status:** ✅ Completed (2026-02-18) -- **Description:** In `__main__.py`'s `on_request()`, the `initialize` client info capture only fires for requests arriving on stdin (client→bridge). Add a brief comment clarifying this intentional scope to prevent future confusion about whether stdout initialize messages are also handled. -- **Priority:** P3 -- **Dependencies:** P12-T1 -- **Parallelizable:** yes -- **Outputs/Artifacts:** - - Updated `src/mcpbridge_wrapper/__main__.py` — comment added near client info capture block -- **Acceptance Criteria:** - - [x] Comment clearly states stdin-only capture direction - - [x] No functional changes - ---- - -#### ✅ FU-P12-T1-3: Show multi-client widgets in Web UI instead of single overwritten active client -- **Status:** ✅ Completed (2026-02-18) -- **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:** - - [x] Dashboard shows multiple clients simultaneously when more than one client connects - - [x] Existing single-client behavior remains correct when only one client is present - - [x] Client widgets update in real time with the same refresh cadence as other KPIs - - [x] `pytest` suite remains green - ---- - -#### ✅ FU-P12-T1-5: Cap `_clients` dict and prune `client_identities` to prevent unbounded growth -- **Status:** ✅ Completed (2026-02-19) -- **Description:** The in-memory `_clients` dict in `MetricsCollector` and the `client_identities` SQLite table in `SharedMetricsStore` grow without limit — every unique `(name, version)` pair adds an entry that is never evicted. Add a soft cap (e.g. 50 entries, evict oldest by `last_seen`) to `_clients`, and add a `WHERE last_seen > ?` pruning clause for `client_identities` on write. This aligns with the project pattern established by FU-BUG-T7-1 (`pending_methods` cap). -- **Priority:** P2 -- **Dependencies:** FU-P12-T1-3 -- **Parallelizable:** yes -- **Outputs/Artifacts:** - - Updated `src/mcpbridge_wrapper/webui/metrics.py` — soft cap on `_clients` dict - - Updated `src/mcpbridge_wrapper/webui/shared_metrics.py` — pruning old `client_identities` rows - - Updated tests covering eviction behavior -- **Acceptance Criteria:** - - [x] `_clients` dict never exceeds the configured cap - - [x] Stale `client_identities` rows are pruned on write - - [x] Existing multi-client dashboard behavior is preserved - - [x] `pytest` suite remains green - ---- - -#### ✅ FU-P12-T1-6: Uniform HTML escaping in `renderClientWidgets` -- **Status:** ✅ Completed (2026-02-19) -- **Description:** In `dashboard.js` `renderClientWidgets`, the `count` integer and `lastSeen` string are interpolated into innerHTML without `escapeHtml()`, while `name` and `version` are escaped. Although `count` is always a number and `lastSeen` already passes through `escapeHtml` inside `formatRelativeAge`, the asymmetric pattern makes security auditing harder. Apply `escapeHtml()` uniformly to all interpolated values for consistency. -- **Priority:** P3 -- **Dependencies:** FU-P12-T1-3 -- **Parallelizable:** yes -- **Outputs/Artifacts:** - - Updated `src/mcpbridge_wrapper/webui/static/dashboard.js` — uniform escaping in `renderClientWidgets` -- **Acceptance Criteria:** - - [x] All interpolated values in `renderClientWidgets` are passed through `escapeHtml()` - - [x] No visual regression in client widget rendering - - [x] `pytest` suite remains green - ---- - -#### ✅ FU-P12-T1-4: Make `IN FLIGHT` KPI reflect real in-flight requests in shared-metrics mode -- **Status:** ✅ Completed (2026-02-19) -- **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:** - - [x] `IN FLIGHT` KPI is greater than zero while requests are in progress and returns to zero after responses - - [x] Works correctly with multiple concurrent clients/processes using the shared metrics database - - [x] No regressions in existing dashboard metrics endpoints - - [x] `pytest` suite remains green - ---- - -#### ✅ FU-P12-T3-1: Document unused `error_message` parameter in `MetricsCollector.record_response` -- **Status:** ✅ Completed (2026-02-19) -- **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 -- **Dependencies:** P12-T3 -- **Parallelizable:** yes -- **Outputs/Artifacts:** - - Updated `src/mcpbridge_wrapper/webui/metrics.py` — docstring clarification on `error_message` parameter -- **Acceptance Criteria:** - - [x] Docstring clearly notes `error_message` is accepted for API symmetry but not stored in-memory - - [x] No functional changes - ---- - -#### ✅ FU-P12-T3-2: Add `error_code` column to audit CSV export -- **Status:** ✅ Completed (2026-02-19) -- **Description:** `AuditLogger.export_csv()` uses a fixed column list that does not include `error_code`. Audit entries with error codes will silently omit this field from CSV exports. Add `error_code` to the CSV column list so it is included when present. -- **Priority:** P3 -- **Dependencies:** P12-T3 -- **Parallelizable:** yes -- **Outputs/Artifacts:** - - Updated `src/mcpbridge_wrapper/webui/audit.py` — `error_code` added to CSV export columns - - Updated `tests/unit/webui/test_audit.py` — test verifying `error_code` in CSV output -- **Acceptance Criteria:** - - [x] CSV export includes `error_code` column - - [x] Entries without `error_code` show empty string for the column - - [x] Existing CSV tests still pass - ---- - -#### ✅ FU-P12-T2-1: Fix stacking click event listeners in `updateLatencyTable` -- **Description:** The `tbody.addEventListener("click", ...)` call inside `updateLatencyTable()` in `dashboard.js` is re-registered on every metrics refresh (every 1 second), causing listeners to accumulate. Move the click handler to a one-time setup function called once during dashboard initialization, or guard with a `data-listener-attached` sentinel attribute on the tbody element. -- **Priority:** P3 -- **Dependencies:** P12-T2 -- **Parallelizable:** yes -- **Outputs/Artifacts:** - - Updated `src/mcpbridge_wrapper/webui/static/dashboard.js` — click handler moved out of `updateLatencyTable` -- **Acceptance Criteria:** - - [ ] Click handler on latency tbody is registered exactly once per page load - - [ ] Param pattern expand/collapse still works correctly after fix - - [ ] No regression in existing dashboard behavior - ---- - -#### ✅ FU-P11-T1-1: Refactor `_FakeWebUIConfig` test stub to use `MagicMock(spec=WebUIConfig)` -- **Description:** The hand-rolled `_FakeWebUIConfig` class in `tests/unit/test_main.py` must be manually updated every time a new property is added to `WebUIConfig`. Refactor it to use `MagicMock(spec=WebUIConfig)` with only the properties needed by the test wired up, so the spec auto-enforces the real interface and future property additions do not break the test. -- **Priority:** P3 -- **Dependencies:** P11-T1 -- **Parallelizable:** yes -- **Outputs/Artifacts:** - - Updated `tests/unit/test_main.py` — `_FakeWebUIConfig` replaced with `MagicMock(spec=WebUIConfig)` -- **Acceptance Criteria:** - - [ ] No hand-rolled `_FakeWebUIConfig` class remains in `test_main.py` - - [ ] All existing `test_main.py` tests pass without modification when new `WebUIConfig` properties are added - - [ ] `pytest` suite remains green - ---- - -#### ✅ FU-BUG-T6-1: Document stale-process cleanup for Web UI port collisions -- **Description:** Add a troubleshooting entry explaining how to identify and kill stale wrapper/uvx processes occupying the Web UI port. Include diagnostic commands (e.g., `lsof -i :` or `ps aux | grep mcpbridge`) and cleanup steps. -- **Priority:** P2 -- **Dependencies:** BUG-T6 -- **Parallelizable:** yes -- **Outputs/Artifacts:** - - Updated `docs/troubleshooting.md` with stale-process cleanup section -- **Acceptance Criteria:** - - [x] Troubleshooting entry covers the "port already in use" warning message - - [x] Commands for identifying and killing stale processes are included - - [x] Relates the fix to the BUG-T6 warning text so users can cross-reference - ---- - -### Phase 15: Next Release Readiness Validation - -#### ✅ P15-T1: Validate project readiness for the next release — Completed (2026-02-28, PASS) -- **Status:** ✅ Completed (2026-02-28, PASS) -- **Description:** Executed release-readiness quality gates, packaging/installability checks, and metadata consistency validation; produced a GO recommendation with documented non-blocking risks. -- **Priority:** P1 -- **Dependencies:** none -- **Parallelizable:** no -- **Outputs/Artifacts:** - - Added `SPECS/ARCHIVE/P15-T1_Validate_project_readiness_for_the_next_release/P15-T1_Validation_Report.md` - - Added `SPECS/ARCHIVE/P15-T1_Validate_project_readiness_for_the_next_release/P15-T1_Validate_project_readiness_for_the_next_release.md` - - Updated `SPECS/ARCHIVE/INDEX.md` with archived task and archive log entries -- **Acceptance Criteria:** - - [x] `pytest`, `ruff check src/`, and `mypy src/` are executed and results are captured in the readiness report - - [x] Packaging preflight (`python -m build`) and install smoke tests (`uvx --from ...` and pip install path) are validated or blockers are documented - - [x] Release metadata consistency is verified across `pyproject.toml`, `server.json`, and `CHANGELOG.md` for the target version - - [x] Readiness report includes an explicit go/no-go recommendation and a concrete blocker list when not ready - ---- - -## 4. Dependency Graph - -``` -P1-T1 → P1-T2 → P1-T3 - │ │ │ - │ │ └────→ P1-T5 - │ │ - │ └────────────→ P1-T4 - │ │ - │ └────→ P5-T1 - │ - └────────────────────→ P1-T6 - -P2-T1 → P2-T2 → P2-T4 → P3-T10 - │ │ ▲ │ - │ │ │ ▼ - │ │ P2-T3 → P3-T1 → P3-T2 → P3-T3 → P3-T4 → P3-T5 → P3-T6 → P3-T7 - │ │ │ │ │ │ │ │ - │ │ │ │ │ │ │ │ - │ │ ▼ ▼ ▼ ▼ ▼ ▼ - │ └─────────────────────────────────────────────────────────────────→ P4-T1 - │ │ │ │ │ │ │ │ │ - │ ▼ ▼ ▼ ▼ ▼ ▼ ▼ ▼ - │ P4-T4 P4-T2 P4-T3 P4-T4 P4-T8 P4-T9 (done) (done) - │ - ├──────────────────────────────────────────────────────────────────────────→ P2-T5 - ├──────────────────────────────────────────────────────────────────────────→ P2-T6 - ├──────────────────────────────────────────────────────────────────────────→ P2-T7 - │ - └──────────────────────────────────────────────────────────────────────────→ P5-T10 - -P3-T8 ─────────────────────────────────────────────────────────────────────→ P3-T10 -P3-T9 ─────────────────────────────────────────────────────────────────────→ P3-T10 - -P5-T1 → P5-T2, P5-T3, P5-T4, P5-T5, P5-T6, P5-T7, P5-T8, P5-T9 (parallel) - -P5-T10 → P5-T11 - │ - └────→ P5-T12 → P5-T13 - -P3-T10, P4-T1-P4-T9 → P6-T1 → P6-T2 → P6-T3 - │ │ │ - │ │ └────────────────────────────────────────────→ P7-T1 - │ │ - │ ├────────────────────────────────────────────────────→ P6-T4 → P7-T2 - │ ├────────────────────────────────────────────────────→ P6-T5 → P7-T3 - │ ├────────────────────────────────────────────────────→ P6-T6 → P7-T4 - │ └────────────────────────────────────────────────────→ P6-T7 - -P2-T1 → P7-T5 -P3-T10 → P7-T8 -P5-T13 → P7-T7, P7-T9 -P7-T1, P7-T2, P7-T6 → P7-T10 → P7-T11 -``` - ---- - -## 5. Traceability Matrix - -| PRD Section | Requirement | Task IDs | -|-------------|-------------|----------| -| §1.2 D1 | mcpbridge-wrapper script | P6-T1, P6-T2 | -| §1.2 D2 | Installation documentation | P7-T1 | -| §1.2 D3 | Configuration examples | P6-T4, P6-T5, P6-T6, P7-T2, P7-T3, P7-T4 | -| §1.3 S1 | Cursor compatibility | P5-T13 | -| §1.3 S2 | Transparency | P5-T10, P5-T12 | -| §1.3 S3 | Performance <5ms | P3-T10, P5-T11 | -| §1.3 S4 | 100% valid JSON success | P3-T2, P4-T7, P5-T2-P5-T7 | -| §3.1 FR1 | Intercept all stdout | P2-T3, P2-T4 | -| §3.1 FR2 | Forward stdin unmodified | P2-T2 | -| §3.1 FR3 | Parse JSON responses | P3-T1, P3-T2 | -| §3.1 FR4 | Detect missing structuredContent | P3-T3, P4-T3 | -| §3.1 FR5 | Extract text from content | P3-T4, P4-T2 | -| §3.1 FR6 | Parse text as JSON | P3-T5 | -| §3.1 FR7 | Fallback wrapper | P3-T6, P5-T4 | -| §3.1 FR8 | Passthrough non-JSON | P3-T8, P5-T5 | -| §3.1 FR9 | Unbuffered output | P3-T9 | -| §3.1 FR10 | Concurrent bidirectional I/O | P2-T4 | -| §3.1 NFR1 | Latency <5ms | P3-T10, P5-T11 | -| §3.1 NFR2 | Memory <10MB | P4-T9 | -| §5.1 | Error handling scenarios | P4-T1, P4-T5, P4-T6, P4-T7 | -| §5.2 | Edge cases EC1-EC4 | P5-T8, P5-T9 | -| §7.1 | Unit test cases TC1-TC6 | P5-T2-P5-T7 | -| §7.2 | Integration test IT1-IT4 | P5-T10, P5-T12, P5-T13 | - ---- - -## 6. Execution Checklist - -**Status: ✅ COMPLETE - 2026-02-08** - -Before starting implementation, verify: -- [x] Xcode 26.3+ is available for testing (P5-T12) - documented as manual test -- [x] Python 3.7+ is installed - verified 3.10.19 -- [x] Target `~/bin` directory is writable - install script handles this - -During execution: -- [x] P5-T12 (real Xcode test) documented as manual test procedure -- [x] P5-T11 (performance) passed - 0.0023ms avg (well under 5ms) -- [x] All P0 tasks complete - 100% (25/25 tasks) - -Completion criteria: -- [x] All P0 tasks: 100% complete (25/25) -- [x] All P1 tasks: 100% complete (29/29) -- [x] P5-T14 coverage: 98.2% (exceeds 90% requirement) -- [x] P5-T13: All 20 tools documented for manual verification -- [x] P5-T11: <5ms overhead verified (0.0023ms avg) - -Post-Completion Validation: -- [x] P8-T3 validated: Installation with new path `xcodemcpwrapper` tested successfully -- [x] Client compatibility verified: Zed Agent ✅, Cursor ✅, Claude Code ✅, Codex CLI ✅ -- [x] FU-REBUILD-P10-T1-4 completed: Web UI argument examples documented for Zed, Cursor, Claude Code, and Codex CLI (2026-02-11) -- [ ] Known issue documented: Kimi CLI v1.9.0 has MCP connection issues (BUG-T1) - -Phase 10: Web UI Dashboard -- [x] P10-T1: Web UI Control & Audit Dashboard (P1) -- [x] P10-T2: Fix Web UI timeseries charts showing no data -- [x] P10-T3: Recover main branch after accidental Web UI merge (P0) -- [x] REBUILD-P10-T1: Spec-driven rebuild package for Web UI feature - -Rebuild Follow-up Backlog -- [x] FU-REBUILD-P10-T1-1: Align websocket auth flow between backend and dashboard client (P2) -- [x] FU-REBUILD-P10-T1-2: Add explicit CLI validation/error messaging for invalid --web-ui-port values (P2) -- [x] FU-REBUILD-P10-T1-3: Reconcile docs/webui-setup.md env variable guidance with runtime behavior (P2) -- [x] FU-REBUILD-P10-T1-4: Add Web UI argument examples for client configs (Zed, Cursor, Claude Code, Codex CLI), including `--web-ui` and `--web-ui-port` usage (P2) -- [x] FU-REBUILD-P10-T1-5: Validate and fix documentation paths for local-running MCP server with Web UI (P1) -- [x] FU-REBUILD-P10-T1-6: Fix uninstall.sh package detection/removal asymmetry and venv cleanup (P2) -- [x] FU-REBUILD-P10-T1-7: Include Web UI static assets in published package artifacts (P1) - ---- - -#### ✅ FU-REBUILD-P10-T1-6: Fix uninstall.sh package detection/removal asymmetry and venv cleanup - -**Description:** -`scripts/uninstall.sh` has a logic mismatch between detection and removal. Detection checks for both `mcpbridge-wrapper` and `xcodemcpwrapper` pip packages (line 78: `pip3 show mcpbridge-wrapper || pip3 show xcodemcpwrapper`), but the actual uninstall step (line 133) only runs `pip3 uninstall mcpbridge-wrapper -y`. If only `xcodemcpwrapper` were installed as a pip package, the script reports it exists but then tries to uninstall the wrong name. The dry-run output (line 98) also only shows `mcpbridge-wrapper` info. - -Additionally, now that `install.sh` creates a `.venv` and embeds the venv Python path in `~/bin/xcodemcpwrapper`, the uninstall script should be updated to handle venv cleanup symmetrically. - -**Priority:** P2 - -**Dependencies:** FU-REBUILD-P10-T1-5 - -**Parallelizable:** yes - -**Problem Analysis:** - -1. **Detection/removal asymmetry:** Detection checks two package names but removal only targets one. If `xcodemcpwrapper` is the installed pip name, `pip3 uninstall mcpbridge-wrapper` silently fails or errors. - -2. **Dry-run output incomplete:** `pip3 show mcpbridge-wrapper` in dry-run may show nothing even though `xcodemcpwrapper` package is installed — misleading output. - -3. **No venv awareness:** After FU-REBUILD-P10-T1-5, `install.sh` now creates a `.venv`. The uninstall script should offer to clean up the venv or at minimum inform the user about it. - -**Affected Files:** -- `scripts/uninstall.sh` - -**Acceptance Criteria:** -- [ ] Detection and removal are symmetric: uninstall whichever package name is actually installed (or both) -- [ ] Dry-run output accurately reflects which package(s) would be removed -- [ ] Script handles the case where package is installed inside a project `.venv` -- [ ] Existing UX preserved: dry-run, --yes, confirmation flow, clean output - ---- - -#### ✅ FU-REBUILD-P10-T1-5: Validate and fix documentation paths for local-running MCP server with Web UI - -**Description:** -Documentation for the "manual installation" / "local running" scenario contains incorrect or misleading paths to the `mcpbridge-wrapper` executable. When a user follows the recommended development setup (creating a `.venv` virtual environment), the package entry point is installed at `.venv/bin/mcpbridge-wrapper`, but the documentation and configuration examples reference `~/bin/xcodemcpwrapper` (a shell wrapper that calls `python3 -m mcpbridge_wrapper` using the system Python, which may not have the package installed). - -**Priority:** P1 - -**Dependencies:** FU-REBUILD-P10-T1-4 - -**Parallelizable:** yes - -**Problem Analysis:** - -1. **`install.sh` runs `pip3 install -e .` without activating a venv:** On modern macOS with Homebrew Python, this fails due to PEP 668 (`externally-managed-environment`). The README correctly tells users to create a venv first, but the install script does not use one. - -2. **`~/bin/xcodemcpwrapper` wrapper uses system `python3`:** The generated shell script at `~/bin/xcodemcpwrapper` calls `exec python3 -m mcpbridge_wrapper "$@"`. If the package was installed inside `.venv/`, the system `python3` cannot find the `mcpbridge_wrapper` module. - -3. **DocC Installation Method 4 is broken:** `Sources/XcodeMCPWrapper/Documentation.docc/Installation.md` suggests `cp src/mcpbridge_wrapper/cli.py ~/bin/xcodemcpwrapper`. This single-file copy cannot work because `cli.py` imports from other modules in the `mcpbridge_wrapper` package. - -4. **Configuration examples for manual + Web UI use wrong path:** All config templates (cursor-mcp.json, zed-agent.json, claude-code.txt, codex-cli.txt) and all documentation files show `/Users/YOUR_USERNAME/bin/xcodemcpwrapper` for manual installation. For users who set up via venv (as recommended), the correct path should reference the venv entry point, e.g. `/path/to/XcodeMCPWrapper/.venv/bin/mcpbridge-wrapper`. - -**Affected Files:** -- `scripts/install.sh` - Needs venv-aware installation or correct system-level install -- `config/cursor-mcp.json` - Manual path options -- `config/zed-agent.json` - Manual path options -- `config/claude-code.txt` - Manual path examples -- `config/codex-cli.txt` - Manual path examples -- `README.md` - Manual installation configuration sections -- `docs/installation.md` - Installation methods and paths -- `docs/cursor-setup.md` - Manual installation option -- `docs/claude-setup.md` - Manual installation option -- `docs/codex-setup.md` - Manual installation option -- `docs/webui-setup.md` - Web UI usage examples with manual paths -- `Sources/XcodeMCPWrapper/Documentation.docc/Installation.md` - Method 4 broken copy command -- `Sources/XcodeMCPWrapper/Documentation.docc/CursorSetup.md` - Manual path -- `Sources/XcodeMCPWrapper/Documentation.docc/ClaudeCodeSetup.md` - Manual path -- `Sources/XcodeMCPWrapper/Documentation.docc/CodexCLISetup.md` - Manual path - -**Outputs/Artifacts:** -- Fixed `scripts/install.sh` - Either activate venv before pip install, or use the venv Python in the wrapper script -- Updated configuration templates with correct venv-based path option for local development -- Updated all documentation with a clear "Option: Local Development" section showing `.venv/bin/mcpbridge-wrapper` path -- Fixed DocC Installation Method 4 (remove broken single-file copy or replace with correct instructions) -- Validation report confirming all documented paths work end-to-end - -**Acceptance Criteria:** -- [ ] `scripts/install.sh` produces a working `xcodemcpwrapper` that can find the `mcpbridge_wrapper` module (either via venv-aware wrapper or correct system install) -- [ ] Configuration examples include a "local development" option with venv path: `/.venv/bin/mcpbridge-wrapper` -- [ ] Web UI examples for local development use the correct venv path: `/.venv/bin/mcpbridge-wrapper --web-ui --web-ui-port 8080` -- [ ] DocC Installation Method 4 either works correctly or is removed/replaced -- [ ] All existing uvx and pip installation paths remain unchanged and correct -- [ ] All documentation is consistent between README, docs/, config/, and DocC sources -- [ ] A new user following the development setup instructions can successfully run the MCP server locally with Web UI - ---- - -#### ✅ FU-REBUILD-P10-T1-7: Include Web UI static assets in published package artifacts - -**Description:** -Users running the published package via `uvx --from mcpbridge-wrapper[webui] mcpbridge-wrapper --web-ui` can start the dashboard server, but `http://localhost:8080` renders: - -`XcodeMCPWrapper Dashboard` / `Static files not found.` - -Root cause is packaging: the released wheel includes Python modules under `mcpbridge_wrapper/webui/` but omits frontend assets under `mcpbridge_wrapper/webui/static/` (`index.html`, `dashboard.css`, `dashboard.js`). The server falls back to the placeholder HTML when `index.html` is missing. - -**Priority:** P1 - -**Dependencies:** P10-T1, P9-T3 - -**Parallelizable:** yes - -**Outputs/Artifacts:** -- Updated packaging config to include `src/mcpbridge_wrapper/webui/static/*` in wheel/sdist artifacts -- Regression test(s) that fail if dashboard static assets are missing at runtime -- Updated troubleshooting docs with explicit symptom/cause for missing static assets (until patched release is published) -- Patch release plan entry (next version after `0.3.0`) noting Web UI packaging fix - -**Acceptance Criteria:** -- [ ] Built wheel contains: - - `mcpbridge_wrapper/webui/static/index.html` - - `mcpbridge_wrapper/webui/static/dashboard.css` - - `mcpbridge_wrapper/webui/static/dashboard.js` -- [ ] `uvx --from mcpbridge-wrapper[webui] mcpbridge-wrapper --web-ui --web-ui-port 8080` serves full dashboard UI (not fallback "Static files not found.") -- [ ] Automated tests cover dashboard HTML serving path and fail on missing static assets -- [ ] Release notes/changelog clearly call out this fix for Web UI users +This file is intentionally reset for the next planning cycle. +Add new tasks using the canonical template in [TASK_TEMPLATE.md](TASK_TEMPLATE.md). From b83fcb04f3623d1a295cf4bcd3ded62fe9f692e7 Mon Sep 17 00:00:00 2001 From: Egor Merkushev Date: Sun, 1 Mar 2026 01:00:56 +0300 Subject: [PATCH 2/2] Handle intentionally empty workplan in task scripts --- scripts/calc_progress.py | 57 ++++++++++++++++--- scripts/pick_next_task.py | 20 +++++++ .../fixtures/intentionally_empty_workplan.md | 12 ++++ tests/test_calc_progress.py | 52 +++++++++++++++++ tests/unit/test_pick_next_task.py | 25 ++++++++ 5 files changed, 158 insertions(+), 8 deletions(-) create mode 100644 tests/fixtures/intentionally_empty_workplan.md diff --git a/scripts/calc_progress.py b/scripts/calc_progress.py index ef741755..a0331dd7 100755 --- a/scripts/calc_progress.py +++ b/scripts/calc_progress.py @@ -16,6 +16,8 @@ from dataclasses import dataclass from typing import List, Optional +EMPTY_CYCLE_MARKER = "intentionally reset for the next planning cycle" + @dataclass class Task: @@ -95,6 +97,32 @@ def parse_workplan(filepath: Path) -> List[Task]: return tasks +def is_intentionally_empty_workplan(filepath: Path) -> bool: + """Return True when workplan is a deliberate empty-cycle placeholder.""" + try: + content = filepath.read_text().lower() + except OSError: + return False + return EMPTY_CYCLE_MARKER in content + + +def empty_progress() -> dict: + """Return a zeroed progress payload for empty planning cycles.""" + return { + 'total': 0, + 'completed': 0, + 'pending': 0, + 'percent': 0.0, + 'by_priority': { + 'P0': {'total': 0, 'completed': 0}, + 'P1': {'total': 0, 'completed': 0}, + 'P2': {'total': 0, 'completed': 0}, + 'P3': {'total': 0, 'completed': 0}, + }, + 'by_phase': {}, + } + + def calculate_progress(tasks: List[Task]) -> dict: """Calculate progress statistics.""" if not tasks: @@ -201,23 +229,36 @@ def list_tasks(tasks: List[Task], phase: Optional[str] = None, pending_only: boo def main(): workplan_path = Path(__file__).parent.parent / "SPECS" / "Workplan.md" + args = sys.argv[1:] if not workplan_path.exists(): print(f"Error: Workplan not found at {workplan_path}") sys.exit(1) - + + if '--help' in args or '-h' in args: + print(__doc__) + sys.exit(0) + tasks = parse_workplan(workplan_path) - if not tasks: + if is_intentionally_empty_workplan(workplan_path): + if '--phase' in args: + idx = args.index('--phase') + phase = args[idx + 1] if idx + 1 < len(args) else None + list_tasks([], phase=phase) + elif '--todo' in args: + list_tasks([], pending_only=True) + elif '--markdown' in args: + print(format_progress(empty_progress(), markdown=True)) + elif '--json' in args: + import json + print(json.dumps(empty_progress(), indent=2)) + else: + print("No tasks available. Workplan is intentionally reset for the next planning cycle.") + sys.exit(0) print("No tasks found in workplan") sys.exit(1) - args = sys.argv[1:] - - if '--help' in args or '-h' in args: - print(__doc__) - sys.exit(0) - if '--phase' in args: idx = args.index('--phase') phase = args[idx + 1] if idx + 1 < len(args) else None diff --git a/scripts/pick_next_task.py b/scripts/pick_next_task.py index 0d97d645..1b7e3c3c 100755 --- a/scripts/pick_next_task.py +++ b/scripts/pick_next_task.py @@ -14,6 +14,8 @@ from pathlib import Path from typing import Optional +EMPTY_CYCLE_MARKER = "intentionally reset for the next planning cycle" + @dataclass class Task: @@ -144,6 +146,15 @@ def parse_workplan(workplan_path: Path) -> list[Task]: return tasks +def is_intentionally_empty_workplan(workplan_path: Path) -> bool: + """Return True when workplan is a deliberate empty-cycle placeholder.""" + try: + content = workplan_path.read_text().lower() + except OSError: + return False + return EMPTY_CYCLE_MARKER in content + + def get_completed_tasks(state_file: Path) -> set[str]: """Load the set of completed task IDs.""" if not state_file.exists(): @@ -369,6 +380,15 @@ def main(): tasks = parse_workplan(args.workplan) if not tasks: + if is_intentionally_empty_workplan(args.workplan): + if args.progress: + print("No tasks available. Workplan is intentionally reset for the next planning cycle.") + print("Progress: 0/0 tasks completed (0.0%)") + elif args.list: + print("No tasks available. Workplan is intentionally reset for the next planning cycle.") + else: + print("No tasks available in current cycle. Add tasks to SPECS/Workplan.md.") + sys.exit(0) print("Error: No tasks found in workplan", file=sys.stderr) sys.exit(1) diff --git a/tests/fixtures/intentionally_empty_workplan.md b/tests/fixtures/intentionally_empty_workplan.md new file mode 100644 index 00000000..9bcd1dda --- /dev/null +++ b/tests/fixtures/intentionally_empty_workplan.md @@ -0,0 +1,12 @@ +# Workplan: mcpbridge-wrapper + +## Archived Baseline + +The previous workplan for release `0.4.0` was archived at: + +- [Workplan_0.4.0.md](ARCHIVE/_Historical/Workplan_0.4.0.md) + +## Current Cycle + +This file is intentionally reset for the next planning cycle. +Add new tasks using the canonical template in [TASK_TEMPLATE.md](TASK_TEMPLATE.md). diff --git a/tests/test_calc_progress.py b/tests/test_calc_progress.py index ecca9b94..6c40456e 100644 --- a/tests/test_calc_progress.py +++ b/tests/test_calc_progress.py @@ -123,6 +123,24 @@ def test_completed_tasks(self): assert result["by_priority"]["P0"]["completed"] == 2 +class TestEmptyCycleHandling: + """Tests for intentionally empty workplan handling.""" + + def test_detects_intentionally_empty_workplan(self): + """Placeholder workplan marker should be recognized.""" + assert cp.is_intentionally_empty_workplan(FIXTURES_DIR / "intentionally_empty_workplan.md") + + def test_empty_progress_payload(self): + """Zeroed payload should be returned for empty cycles.""" + progress = cp.empty_progress() + assert progress["total"] == 0 + assert progress["completed"] == 0 + assert progress["pending"] == 0 + assert progress["percent"] == 0.0 + assert progress["by_priority"]["P0"]["total"] == 0 + assert progress["by_phase"] == {} + + class TestFormatProgress: """Tests for format_progress function.""" @@ -304,3 +322,37 @@ def test_script_with_test_fixture(self): if backup_workplan.exists(): shutil.copy(backup_workplan, original_workplan) backup_workplan.unlink() + + def test_script_with_intentionally_empty_workplan(self): + """Reset workplan placeholder should return success with empty progress.""" + import shutil + + spec_dir = Path(__file__).parent.parent / "SPECS" + original_workplan = spec_dir / "Workplan.md" + backup_workplan = spec_dir / "Workplan.md.bak" + empty_fixture = FIXTURES_DIR / "intentionally_empty_workplan.md" + + if original_workplan.exists(): + shutil.copy(original_workplan, backup_workplan) + + try: + shutil.copy(empty_fixture, original_workplan) + + result = subprocess.run( + [sys.executable, "scripts/calc_progress.py", "--json"], + capture_output=True, + text=True, + cwd=Path(__file__).parent.parent, + ) + + assert result.returncode == 0 + data = json.loads(result.stdout) + assert data["total"] == 0 + assert data["completed"] == 0 + assert data["pending"] == 0 + assert data["percent"] == 0.0 + + finally: + if backup_workplan.exists(): + shutil.copy(backup_workplan, original_workplan) + backup_workplan.unlink() diff --git a/tests/unit/test_pick_next_task.py b/tests/unit/test_pick_next_task.py index 255acffe..3f539047 100644 --- a/tests/unit/test_pick_next_task.py +++ b/tests/unit/test_pick_next_task.py @@ -596,6 +596,31 @@ def test_missing_workplan(self, tmp_path, capsys): main() assert exc_info.value.code == 1 + def test_intentionally_empty_workplan_exits_zero(self, tmp_path, capsys): + """A reset workplan placeholder should be treated as a valid empty cycle.""" + state_file = tmp_path / "state.json" + empty_workplan = tmp_path / "Workplan.md" + empty_workplan.write_text( + "# Workplan: Test\n\n" + "## Current Cycle\n\n" + "This file is intentionally reset for the next planning cycle.\n" + ) + + with pytest.raises(SystemExit) as exc_info, patch( + "sys.argv", + [ + "pick_next_task.py", + "--workplan", + str(empty_workplan), + "--state", + str(state_file), + ], + ): + main() + assert exc_info.value.code == 0 + captured = capsys.readouterr() + assert "No tasks available in current cycle" in captured.out + # ============================================================================= # Integration tests