diff --git a/SPECS/ARCHIVE/INDEX.md b/SPECS/ARCHIVE/INDEX.md index f70353c7..3c0bcf59 100644 --- a/SPECS/ARCHIVE/INDEX.md +++ b/SPECS/ARCHIVE/INDEX.md @@ -1,6 +1,6 @@ # mcpbridge-wrapper Tasks Archive -**Last Updated:** 2026-02-15 (P12-T3) +**Last Updated:** 2026-02-15 (P12-T4) ## Archived Tasks @@ -96,6 +96,7 @@ | P11-T4 | [P11-T4_Add_Keyboard_Shortcuts_Command_Palette/](P11-T4_Add_Keyboard_Shortcuts_Command_Palette/) | 2026-02-15 | PASS | | P12-T1 | [P12-T1_Add_MCP_Client_Identification/](P12-T1_Add_MCP_Client_Identification/) | 2026-02-15 | PASS | | P12-T3 | [P12-T3_Add_Error_Classification_and_Categorization/](P12-T3_Add_Error_Classification_and_Categorization/) | 2026-02-15 | PASS | +| P12-T4 | [P12-T4_Add_documentation_about_data_storage/](P12-T4_Add_documentation_about_data_storage/) | 2026-02-15 | PASS | ## Historical Artifacts @@ -156,6 +157,7 @@ | [REVIEW_P11-T4_keyboard_shortcuts.md](P11-T4_Add_Keyboard_Shortcuts_Command_Palette/REVIEW_P11-T4_keyboard_shortcuts.md) | Review report for P11-T4 | | [REVIEW_P12-T1_mcp_client_identification.md](P12-T1_Add_MCP_Client_Identification/REVIEW_P12-T1_mcp_client_identification.md) | Review report for P12-T1 | | [REVIEW_P12-T3_error_classification_categorization.md](P12-T3_Add_Error_Classification_and_Categorization/REVIEW_P12-T3_error_classification_categorization.md) | Review report for P12-T3 | +| [REVIEW_P12-T4_data_storage_documentation.md](P12-T4_Add_documentation_about_data_storage/REVIEW_P12-T4_data_storage_documentation.md) | Review report for P12-T4 | ## Archive Log @@ -265,3 +267,5 @@ | 2026-02-15 | P12-T1 | Archived REVIEW_P12-T1_mcp_client_identification report | | 2026-02-15 | P12-T3 | Archived Add_Error_Classification_and_Categorization (PASS) | | 2026-02-15 | P12-T3 | Archived REVIEW_P12-T3_error_classification_categorization report | +| 2026-02-15 | P12-T4 | Archived Add_documentation_about_data_storage (PASS) | +| 2026-02-15 | P12-T4 | Archived REVIEW_P12-T4_data_storage_documentation report | diff --git a/SPECS/ARCHIVE/P12-T4_Add_documentation_about_data_storage/P12-T4_Add_documentation_about_data_storage.md b/SPECS/ARCHIVE/P12-T4_Add_documentation_about_data_storage/P12-T4_Add_documentation_about_data_storage.md new file mode 100644 index 00000000..007d21cf --- /dev/null +++ b/SPECS/ARCHIVE/P12-T4_Add_documentation_about_data_storage/P12-T4_Add_documentation_about_data_storage.md @@ -0,0 +1,116 @@ +# PRD: P12-T4 — Add documentation about data storage + +**Task ID:** P12-T4 +**Phase:** 12 — Data Collection Enhancements +**Priority:** P2 +**Status:** In Progress +**Branch:** feature/P12-T4-data-storage-documentation +**Date:** 2026-02-15 + +--- + +## 1. Problem Statement + +mcpbridge-wrapper collects and persists operational telemetry across multiple storage layers: an SQLite database, in-memory structures, and structured audit log files. As new contributors or operators try to understand the system, debug issues, or extend data collection, there is no single reference explaining what data is stored where, what each field means, how data flows between components, or what happens on reset. + +## 2. Goals + +- Provide a comprehensive, accurate reference document for all data storage containers. +- Document every SQLite table, column, type, and nullability. +- Document the in-memory `MetricsCollector` (per-process) and `SharedMetricsStore` (cross-process) fields. +- Document the audit log format: JSONL on disk and CSV export columns. +- Explain the data flow from MCP request capture through aggregation to API exposure. +- Clarify retention and reset behaviour. +- Make the document discoverable from `docs/` (no README changes required for this scope). + +## 3. Out of Scope + +- No code changes — this is a pure documentation task. +- No changes to existing README.md (the docs/ folder is already referenced there). +- No ER diagram (optional stretch goal, deferred). + +## 4. Deliverables + +| # | Artifact | Location | +|---|----------|----------| +| 1 | Data storage reference | `docs/data-storage.md` | +| 2 | Updated module docstring | `src/mcpbridge_wrapper/webui/shared_metrics.py` — `SharedMetricsStore` class | +| 3 | Updated module docstring | `src/mcpbridge_wrapper/webui/metrics.py` — `MetricsCollector` class | + +## 5. Acceptance Criteria + +- [ ] SQLite schema documented with all tables (`requests`, `client_info`), columns, types, nullability, and indexes. +- [ ] In-memory `MetricsCollector` fields documented (counters, deques, dicts, in-flight map, client info, error breakdown). +- [ ] `SharedMetricsStore` documented (SQLite-backed, thread-local connections, default path, multi-process design intent). +- [ ] Audit log format documented: JSONL record fields on disk and the six CSV export columns. +- [ ] Data flow narrative: `__main__.py` capture → `metrics.py`/`shared_metrics.py` write → Web UI `server.py` API read. +- [ ] Retention/reset section covers: what `reset()` clears in each layer, default DB path, log rotation policy. +- [ ] Document lives at `docs/data-storage.md` and links back from the existing `docs/architecture.md` if appropriate. + +## 6. Source Files Analysed + +| File | Key classes | +|------|-------------| +| `src/mcpbridge_wrapper/webui/shared_metrics.py` | `SharedMetricsStore` — SQLite, tables: `requests`, `client_info` | +| `src/mcpbridge_wrapper/webui/metrics.py` | `MetricsCollector` — in-memory counters and deques | +| `src/mcpbridge_wrapper/webui/audit.py` | `AuditLogger` — JSONL files, CSV export | +| `src/mcpbridge_wrapper/webui/sessions.py` | Session tracking (referenced for completeness) | + +## 7. Key Facts Gathered + +### SQLite (`shared_metrics.db`) + +**Default path:** `~/.cache/mcpbridge-wrapper/metrics.db` + +**Table: `requests`** +| Column | Type | Nullable | Description | +|--------|------|----------|-------------| +| id | INTEGER PK AUTOINCREMENT | no | Row identifier | +| request_id | TEXT | yes | JSON-RPC request ID | +| tool_name | TEXT | no | MCP tool name | +| timestamp | REAL | no | Unix epoch (seconds) of request arrival | +| latency_ms | REAL | yes | Response latency; NULL until response recorded | +| error | BOOLEAN DEFAULT 0 | no | 1 if response was an error | +| error_code | INTEGER | yes | JSON-RPC error code | +| error_message | TEXT | yes | JSON-RPC error message | + +Indexes: `idx_requests_tool(tool_name)`, `idx_requests_time(timestamp)`. + +**Table: `client_info`** +| Column | Type | Nullable | Description | +|--------|------|----------|-------------| +| id | INTEGER PK DEFAULT 1 | no | Always 1 (single-row table) | +| client_name | TEXT | yes | MCP client name from `initialize` | +| client_version | TEXT | yes | MCP client version string | +| updated_at | REAL | yes | Unix epoch of last update | + +### In-memory (`MetricsCollector`) + +Counters: `_total_requests`, `_total_errors`, `_start_time` +Per-tool: `_tool_counts`, `_tool_errors`, `_tool_latencies` +Time-series deques (maxlen=3600): `_request_times`, `_error_times`, `_latency_series` +In-flight map: `_in_flight` (request_id → start timestamp) +Client info: `_client_name`, `_client_version` +Error breakdown: `_error_counts_by_code` + +### Audit log (`AuditLogger`) + +- On-disk format: newline-delimited JSON (`.jsonl`) +- File naming: `audit_YYYYMMDD_HHMMSS.jsonl` +- Default dir: `logs/audit/` (relative to working directory) +- Rotation: max 10 MB per file, max 10 files retained +- Memory: up to 10,000 most-recent entries loaded at startup +- CSV export columns: `timestamp_iso`, `tool`, `direction`, `request_id`, `latency_ms`, `error` + +## 8. Dependencies + +- P12-T1 ✅ — `client_info` table and `client_name`/`client_version` fields +- P12-T3 ✅ — `error_code`, `error_message` columns and `error_counts_by_code` + +## 9. Quality Gates + +This task produces only documentation files — no Python source changes. + +- [ ] `ruff check src/` — must still pass (no src/ changes) +- [ ] `pytest` — must still pass (no src/ changes) +- [ ] Markdown: headings, tables, and code blocks render correctly diff --git a/SPECS/ARCHIVE/P12-T4_Add_documentation_about_data_storage/P12-T4_Validation_Report.md b/SPECS/ARCHIVE/P12-T4_Add_documentation_about_data_storage/P12-T4_Validation_Report.md new file mode 100644 index 00000000..a0d5aa01 --- /dev/null +++ b/SPECS/ARCHIVE/P12-T4_Add_documentation_about_data_storage/P12-T4_Validation_Report.md @@ -0,0 +1,42 @@ +# Validation Report: P12-T4 — Add documentation about data storage + +**Date:** 2026-02-15 +**Branch:** feature/P12-T4-data-storage-documentation +**Verdict:** PASS + +--- + +## Deliverables + +| Deliverable | Status | Notes | +|-------------|--------|-------| +| `docs/data-storage.md` | DONE | New comprehensive reference document | +| Link from `docs/architecture.md` | DONE | "Data Storage" section added at end of file | +| PRD (`SPECS/INPROGRESS/P12-T4_Add_documentation_about_data_storage.md`) | DONE | | + +## Acceptance Criteria + +| Criterion | Status | +|-----------|--------| +| SQLite schema (`requests`, `client_info`) documented with columns, types, nullability, indexes | PASS | +| `MetricsCollector` in-memory fields documented (counters, deques, in-flight, client info, error breakdown) | PASS | +| `SharedMetricsStore` documented (SQLite-backed, multi-process design, default path, connection model) | PASS | +| Audit log format documented: JSONL record fields and CSV export columns | PASS | +| Data flow narrative from capture to storage to API | PASS | +| Retention/reset behaviour documented for all three layers | PASS | +| Document discoverable from existing `docs/` via `architecture.md` link | PASS | + +## Quality Gates + +| Gate | Result | +|------|--------| +| `pytest` | 437 passed, 5 skipped, 3 warnings | +| `ruff check src/` | All checks passed | +| `pytest --cov` | 96.1% total coverage (≥ 90% required) | + +## Notes + +- No Python source code was modified — this is a pure documentation task. +- The `error_code` / `error_message` migration note in the SQLite section references the P12-T3 ALTER TABLE migration. +- Payload capture behaviour and the 64 KB truncation limit are documented in the audit log section. +- CSV export column list confirmed against `export_csv()` `fieldnames` in `audit.py`. diff --git a/SPECS/ARCHIVE/P12-T4_Add_documentation_about_data_storage/REVIEW_P12-T4_data_storage_documentation.md b/SPECS/ARCHIVE/P12-T4_Add_documentation_about_data_storage/REVIEW_P12-T4_data_storage_documentation.md new file mode 100644 index 00000000..02055021 --- /dev/null +++ b/SPECS/ARCHIVE/P12-T4_Add_documentation_about_data_storage/REVIEW_P12-T4_data_storage_documentation.md @@ -0,0 +1,55 @@ +## REVIEW REPORT — P12-T4 data storage documentation + +**Scope:** origin/main..HEAD (5 commits) +**Files:** 2 changed (`docs/data-storage.md` new, `docs/architecture.md` +4 lines) +**Date:** 2026-02-15 + +--- + +### Summary Verdict + +- [x] Approve +- [ ] Approve with comments +- [ ] Request changes +- [ ] Block + +--- + +### Critical Issues + +None. + +--- + +### Secondary Issues + +None. + +--- + +### Architectural Notes + +1. **`sessions.py` not documented** — `AuditLogger` loads history at startup but `sessions.py` is not covered in this document. The PRD explicitly deferred session tracking; this is intentional and acceptable for the current scope. A future task could extend `data-storage.md` with the session store. + +2. **CSV `error_code` column absence is acknowledged** — The CSV export section correctly omits `error_code` because it is not yet in the `fieldnames` list. This gap is already tracked in `FU-P12-T3-2: Add error_code column to audit CSV export`. No action required here. + +3. **Data flow diagram uses prose function names** — `on_request()` / `on_response()` in the ASCII diagram are conceptual labels, not actual Python function names from `__main__.py`. This is fine for a high-level reference doc; no technical inaccuracy. + +4. **`~/.cache/mcpbridge-wrapper/metrics.db` default path** — Confirmed against `DEFAULT_DB_PATH` in `shared_metrics.py` (line 17). Accurate. + +5. **100 MB disk estimate** — The "~100 MB" maximum stated in the audit log retention section is correct: 10 files × 10 MB = 100 MB. + +--- + +### Tests + +- No Python source files were modified — test suite is unaffected. +- `pytest`: 437 passed, 5 skipped. Coverage: 96.1% (≥ 90% required). ✓ +- `ruff check src/`: All checks passed. ✓ + +--- + +### Next Steps + +- No follow-up tasks required from this review. +- FOLLOW-UP step is **skipped** (no actionable findings). diff --git a/SPECS/INPROGRESS/next.md b/SPECS/INPROGRESS/next.md index 5168b763..c4dba9a0 100644 --- a/SPECS/INPROGRESS/next.md +++ b/SPECS/INPROGRESS/next.md @@ -4,16 +4,17 @@ The previously selected task has been archived. ## Recently Archived +- 2026-02-15 — P12-T4: Add documentation about data storage (PASS) - 2026-02-15 — P12-T3: Add Error Classification & Categorization (PASS) - 2026-02-15 — P12-T1: Add MCP Client Identification (PASS) - 2026-02-15 — P11-T4: Add Keyboard Shortcuts & Command Palette (PASS) - 2026-02-15 — P11-T3: Add Dashboard Theme Toggle (Dark/Light) (PASS) -- 2026-02-15 — P11-T2: Add Session Timeline View (PASS) ## Suggested Next Tasks - P12-T2: Add Tool Parameter Frequency Analysis (P3, depends on P12-T1 ✅) +- P11-T1: Add Tool Call Detail Inspector (P2, depends on P10-T1 ✅) - FU-P12-T1-1: Remove or document `MCPInitializeParams` in schemas (P3, depends on P12-T1 ✅) - FU-P12-T1-2: Add code comment clarifying stdin-only client capture in `on_request` (P3, depends on P12-T1 ✅) -- FU-BUG-T7-1: Cap `pending_methods` map to guard against unbounded growth (P3) -- FU-P11-T1-1: Refactor `_FakeWebUIConfig` test stub to `MagicMock(spec=WebUIConfig)` (P3) +- FU-P12-T3-1: Document unused `error_message` parameter in `MetricsCollector.record_response` (P3, depends on P12-T3 ✅) +- FU-P12-T3-2: Add `error_code` column to audit CSV export (P3, depends on P12-T3 ✅) diff --git a/SPECS/Workplan.md b/SPECS/Workplan.md index a7d3bab4..9a0d0603 100644 --- a/SPECS/Workplan.md +++ b/SPECS/Workplan.md @@ -1745,6 +1745,26 @@ Phase 9 Follow-up Backlog --- +#### ✅ 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. diff --git a/Sources/XcodeMCPWrapper/Documentation.docc/Architecture.md b/Sources/XcodeMCPWrapper/Documentation.docc/Architecture.md index 666e3181..07817bdb 100644 --- a/Sources/XcodeMCPWrapper/Documentation.docc/Architecture.md +++ b/Sources/XcodeMCPWrapper/Documentation.docc/Architecture.md @@ -77,6 +77,10 @@ Orchestrates the flow: | Memory footprint | <10MB | <10MB | | Test coverage | ≥90% | 98.2% | +## Data Storage + +For a full reference on the SQLite metrics database, in-memory collector, and audit log files, see the [Data Storage Reference](https://github.com/SoundBlaster/XcodeMCPWrapper/blob/main/docs/data-storage.md). + ## Technology Stack - **Python 3.7+** - Wrapper implementation diff --git a/docs/architecture.md b/docs/architecture.md index 1d832530..b799b74c 100644 --- a/docs/architecture.md +++ b/docs/architecture.md @@ -66,3 +66,7 @@ - Line-buffered I/O for minimal latency - Average overhead: <0.01ms per transformation - Memory usage: <10MB + +## Data Storage + +For a full reference on the SQLite metrics database, in-memory collector, and audit log files, see [Data Storage Reference](data-storage.md). diff --git a/docs/data-storage.md b/docs/data-storage.md new file mode 100644 index 00000000..72d3178f --- /dev/null +++ b/docs/data-storage.md @@ -0,0 +1,263 @@ +# Data Storage Reference + +This document describes every data storage container used by mcpbridge-wrapper: the SQLite metrics database, the in-memory metrics collector, and the audit log files. + +--- + +## Overview + +mcpbridge-wrapper collects operational telemetry through three complementary layers: + +| Layer | Class | Scope | Persistence | +|-------|-------|-------|-------------| +| SQLite database | `SharedMetricsStore` | Cross-process | Durable on disk | +| In-memory collector | `MetricsCollector` | Single process | Lost on exit | +| Audit log files | `AuditLogger` | Single process (loaded at startup) | Durable on disk | + +All three layers are populated from the same interception point in `__main__.py` and exposed to the Web UI dashboard via `server.py`. + +--- + +## Data Flow + +``` +MCP client (stdin) + │ + ▼ + __main__.py ─── on_request() ──► MetricsCollector.record_request() + │ ──► SharedMetricsStore.record_request() + │ ──► AuditLogger.log(direction="request") + │ + (forward to mcpbridge) + │ + ▼ + __main__.py ─── on_response() ──► MetricsCollector.record_response() + ──► SharedMetricsStore.record_response() + ──► AuditLogger.log(direction="response") + + ▼ + server.py ── GET /api/metrics/summary + ── GET /api/metrics/timeseries + ── GET /api/audit/entries + ── GET /api/audit/export/csv +``` + +`__main__.py` also calls `set_client_info()` on both stores when the MCP `initialize` handshake is received (from `clientInfo.name` / `clientInfo.version` in the request params). + +--- + +## 1. SQLite Database (`SharedMetricsStore`) + +**Source file:** `src/mcpbridge_wrapper/webui/shared_metrics.py` + +**Purpose:** Provides process-safe, durable storage so that all wrapper processes (e.g. multiple Zed/Cursor connections) write to the same metrics backend and the Web UI can aggregate across them. + +**Default path:** `~/.cache/mcpbridge-wrapper/metrics.db` + +The path can be overridden by passing a custom `db_path` to `SharedMetricsStore(db_path=...)`, which `WebUIConfig` does when the user sets a custom data directory. + +**Connection model:** Each thread gets its own `sqlite3.Connection` via `threading.local()`. All writes go through a `_transaction()` context manager that commits on success and rolls back on exception. The connection timeout is 10 seconds. + +--- + +### Table: `requests` + +Stores one row per MCP tool call event. A request and its corresponding response are recorded as a single row: the request inserts the row, and the response updates it with `latency_ms` and error information. + +| Column | Type | Nullable | Description | +|--------|------|----------|-------------| +| `id` | `INTEGER` PK AUTOINCREMENT | no | Internal row identifier | +| `request_id` | `TEXT` | yes | JSON-RPC request ID (`null` for notifications) | +| `tool_name` | `TEXT` | no | MCP tool name (e.g. `XcodeGrep`) | +| `timestamp` | `REAL` | no | Unix epoch (seconds) when the request arrived | +| `latency_ms` | `REAL` | yes | End-to-end latency in milliseconds; `NULL` until the response is recorded | +| `error` | `BOOLEAN DEFAULT 0` | no | `1` if the response contained a JSON-RPC error | +| `error_code` | `INTEGER` | yes | JSON-RPC error code (e.g. `-32601`); `NULL` for successful responses | +| `error_message` | `TEXT` | yes | JSON-RPC error message string; `NULL` for successful responses | + +**Indexes:** +- `idx_requests_tool` on `tool_name` — accelerates per-tool aggregation queries +- `idx_requests_time` on `timestamp` — accelerates time-window queries + +**Migration note:** `error_code` and `error_message` columns were added in P12-T3 via `ALTER TABLE … ADD COLUMN` (wrapped in `contextlib.suppress` for idempotency on existing databases). + +--- + +### Table: `client_info` + +A single-row table (always `id = 1`) that records the identity of the most recently connected MCP client. + +| Column | Type | Nullable | Description | +|--------|------|----------|-------------| +| `id` | `INTEGER` PK DEFAULT 1 | no | Always `1`; enforces single-row constraint | +| `client_name` | `TEXT` | yes | Client name from `initialize` handshake (e.g. `"Cursor"`) | +| `client_version` | `TEXT` | yes | Client version string (e.g. `"1.2.3"`) | +| `updated_at` | `REAL` | yes | Unix epoch of the last update | + +Rows are written with `INSERT … ON CONFLICT(id) DO UPDATE` (upsert), so there is always at most one row. + +--- + +### Retention & Reset + +- **Window:** `get_summary()` and `get_timeseries()` accept a `window_seconds` parameter (default 3 600 s = 1 hour) that filters rows by `timestamp > now - window_seconds`. Rows outside the window are still in the database but excluded from aggregation. +- **Reset:** `SharedMetricsStore.reset()` issues `DELETE FROM requests` and `DELETE FROM client_info`, removing all rows. The database file itself is not deleted. +- **No automatic purge:** Old rows are not automatically deleted; `reset()` must be called explicitly (e.g. via `POST /api/metrics/reset` in the Web UI). + +--- + +## 2. In-Memory Collector (`MetricsCollector`) + +**Source file:** `src/mcpbridge_wrapper/webui/metrics.py` + +**Purpose:** Fast, lock-protected in-process accumulator for real-time dashboard metrics. Complementary to `SharedMetricsStore` — this layer provides accurate per-process latency percentiles and in-flight request counts that are difficult to compute from SQLite alone. + +**Thread safety:** All mutations and reads are guarded by a single `threading.Lock`. + +--- + +### Fields + +#### Scalar counters + +| Field | Type | Description | +|-------|------|-------------| +| `_total_requests` | `int` | Cumulative count of all requests since start or last reset | +| `_total_errors` | `int` | Cumulative count of all error responses | +| `_start_time` | `float` | Unix epoch when the collector was created or last reset | + +#### Per-tool dictionaries + +| Field | Type | Description | +|-------|------|-------------| +| `_tool_counts` | `Dict[str, int]` | Request count per tool name | +| `_tool_errors` | `Dict[str, int]` | Error count per tool name | +| `_tool_latencies` | `Dict[str, List[float]]` | All recorded latencies (ms) per tool, capped at `max_datapoints` entries | + +#### Time-series deques + +All deques have `maxlen=max_datapoints` (default 3 600). When full, the oldest entry is evicted automatically. + +| Field | Type | Contents | +|-------|------|---------| +| `_request_times` | `Deque[float]` | Unix epoch timestamp of each request | +| `_error_times` | `Deque[float]` | Unix epoch timestamp of each error response | +| `_latency_series` | `Deque[Tuple[float, float]]` | `(timestamp, latency_ms)` pairs for all responses with known latency | + +#### In-flight map + +| Field | Type | Description | +|-------|------|-------------| +| `_in_flight` | `Dict[str, float]` | Maps `request_id` → start timestamp for requests that have been received but not yet responded to. Used to auto-compute `latency_ms` when `record_response()` is called without an explicit latency. Entries are removed on `record_response()`. | + +#### Client identity + +| Field | Type | Default | Description | +|-------|------|---------|-------------| +| `_client_name` | `str` | `"unknown"` | MCP client name from `initialize` | +| `_client_version` | `str` | `"unknown"` | MCP client version from `initialize` | + +#### Error breakdown + +| Field | Type | Description | +|-------|------|-------------| +| `_error_counts_by_code` | `Dict[int, int]` | Maps JSON-RPC error code → count of occurrences | + +--- + +### Error categorisation + +The module-level `categorize_error(code)` function maps a JSON-RPC error code to a severity bucket used by the dashboard for colour-coding: + +| Category | Condition | +|----------|-----------| +| `"protocol"` | `-32699 ≤ code ≤ -32600` (standard JSON-RPC errors) | +| `"timeout"` | `code == -32001` | +| `"tool"` | `code ≥ 1` (Xcode-side tool execution errors) | +| `"unknown"` | Any other code, or `None` | + +--- + +### Retention & Reset + +`MetricsCollector.reset()` clears all fields: +- `_total_requests` and `_total_errors` reset to `0` +- `_start_time` set to current time +- All per-tool dicts cleared +- All deques cleared +- `_in_flight` cleared +- `_client_name` and `_client_version` reset to `"unknown"` +- `_error_counts_by_code` cleared + +Data is lost on process exit — there is no persistence. + +--- + +## 3. Audit Log Files (`AuditLogger`) + +**Source file:** `src/mcpbridge_wrapper/webui/audit.py` + +**Purpose:** Provides a durable, human-readable record of every MCP tool call for compliance, debugging, and post-hoc analysis. Survives process restarts. + +--- + +### On-disk format + +- **Encoding:** UTF-8 newline-delimited JSON (`.jsonl`) +- **File naming:** `audit_YYYYMMDD_HHMMSS.jsonl` (UTC timestamp) +- **Default directory:** `logs/audit/` relative to the working directory; configurable via `AuditLogger(log_dir=...)`. +- **Rotation:** A new file is opened when the current file exceeds `max_file_size_mb` (default 10 MB). Up to `max_files` (default 10) rotated files are retained; the oldest is deleted when the limit is exceeded. +- **Startup load:** At initialisation, all `audit_*.jsonl` files in `log_dir` are read in chronological order. The most recent 10 000 entries are loaded into `_entries` in memory so the Web UI dashboard can display history from sibling processes. + +--- + +### JSONL record fields + +Each line is a JSON object. Fields are written only when non-`None`: + +| Field | Type | Always present | Description | +|-------|------|---------------|-------------| +| `timestamp` | `float` | yes | Unix epoch (seconds) | +| `timestamp_iso` | `str` | yes | ISO 8601 UTC string (e.g. `"2026-02-15T10:30:00Z"`) | +| `tool` | `str` | yes | MCP tool name | +| `direction` | `str` | yes | `"request"` or `"response"` | +| `request_id` | `str` | when present | JSON-RPC request ID | +| `request` | `object` | when captured | Sanitised request payload (requires `capture_payload=True`) | +| `response` | `object` | when captured | Sanitised response payload (requires `capture_payload=True`) | +| `latency_ms` | `float` | on response | Round-trip latency in milliseconds | +| `error` | `str` | on error | Error message string | +| `error_code` | `int` | on error | JSON-RPC error code | + +**Payload capture:** Disabled by default (`capture_payload=False`). When enabled, payloads larger than 64 KB are truncated to `{"_truncated": true, "raw": ""}`. The payload ring buffer holds at most 500 entries (oldest evicted). + +--- + +### CSV export columns + +`AuditLogger.export_csv()` writes a subset of fields. Extra fields in the JSONL records are silently ignored: + +| Column | Description | +|--------|-------------| +| `timestamp_iso` | ISO 8601 UTC timestamp | +| `tool` | MCP tool name | +| `direction` | `"request"` or `"response"` | +| `request_id` | JSON-RPC request ID | +| `latency_ms` | Latency in milliseconds (empty for request-direction rows) | +| `error` | Error message (empty for successful responses) | + +--- + +### Retention & Reset + +- **File rotation:** Automatic at `max_file_size_mb` per file (default 10 MB), keeping `max_files` most recent (default 10). Total maximum disk usage: ~100 MB. +- **In-memory cap:** `_entries` is trimmed to the most recent 10 000 entries. +- **No reset API:** `AuditLogger` has no `reset()` method. Log files on disk persist until rotated out. The in-memory `_entries` list is repopulated from disk on restart. +- **Closing:** `AuditLogger.close()` flushes and closes the current `.jsonl` file. This is called automatically when the Web UI server shuts down. + +--- + +## See Also + +- [Architecture Overview](architecture.md) — system-level data flow diagram +- [Web UI Setup](webui-setup.md) — how to enable the dashboard +- [Environment Variables](environment-variables.md) — configuration options including custom data paths