|
| 1 | +# Visual & Reactivity Verification |
| 2 | + |
| 3 | +Captured live on 2026-05-08 against the swap-in `MCPProxy-047.app` (built from this branch) running with the user's real `~/.mcpproxy/mcp_config.json` (30 servers, 12-14 connected). The test toggled `context7` via the REST API and observed both UIs. |
| 4 | + |
| 5 | +## Web UI (Chrome) |
| 6 | + |
| 7 | +URL: `http://127.0.0.1:8080/ui/servers?apikey=…` |
| 8 | +Driver: chrome browser MCP (`navigate`, `screenshot`, `read_console_messages`, `read_network_requests`). |
| 9 | + |
| 10 | +**Baseline → after disable**: |
| 11 | + |
| 12 | +| Element | Baseline | After `POST /servers/context7/disable` | |
| 13 | +|---|---|---| |
| 14 | +| `context7` card status badge | `Connected (2 tools)` (green) | **`Disabled`** (gray) | |
| 15 | +| `context7` card "Status" field | `Enabled` | **`Disabled`** | |
| 16 | +| `context7` card primary action | `Disable` | **`Enable`** | |
| 17 | +| Header connected counter | `14 / 30 Servers` | **`10 / 30 Servers`** | |
| 18 | +| "Total Servers" sidebar card "X enabled" | `14 enabled` | **`13 enabled`** | |
| 19 | +| "Connected" sidebar card | `14` (47% online) | **`10`** (33% online) | |
| 20 | +| "Total Tools" card | `123` | **`188`** | |
| 21 | + |
| 22 | +**Console + network during the toggle (network log cleared at start)**: |
| 23 | + |
| 24 | +``` |
| 25 | +[6:29:04 PM] SSE servers.changed event received: Object |
| 26 | +[6:29:04 PM] Servers changed event received, updating in background... Object |
| 27 | +[6:29:04 PM] SSE servers.changed event received: Object |
| 28 | +[6:29:04 PM] Servers changed event received, updating in background... Object |
| 29 | +… (10 events total) |
| 30 | +``` |
| 31 | + |
| 32 | +`read_network_requests` for `/api/v1/servers` after clearing: **0 requests**. |
| 33 | + |
| 34 | +The UI updated visibly while the network log stayed empty for `/api/v1/servers` — proving the embedded SSE payload drove the change. No round trip. |
| 35 | + |
| 36 | +Screenshots: `tray_baseline.png` is captured by the shell; for the Web UI, the chrome MCP returned screenshots in-line during the test (saved by the harness to its temporary cache; the textual `find`/`read_page` output above is the authoritative trace). |
| 37 | + |
| 38 | +## macOS Tray |
| 39 | + |
| 40 | +Bundle: `com.smartmcpproxy.mcpproxy` (the swap-in `MCPProxy-047.app`). |
| 41 | +Driver: `/tmp/mcpproxy-ui-test` invoked as a JSON-RPC subprocess (the binary CLAUDE.md describes; not registered as an MCP server in this session, so driven via stdin/stdout directly). |
| 42 | + |
| 43 | +`check_accessibility` → `{"trusted": true}`. |
| 44 | + |
| 45 | +**State captured via `list_menu_items` at three points** (`tray_baseline.png`, `tray_after_disable.png`, `tray_after_reenable.png`): |
| 46 | + |
| 47 | +| State | `context7 → children[0]` | `context7 → Enable/Disable button` | |
| 48 | +|---|---|---| |
| 49 | +| Baseline | `Connected (2 tools)` | `Disable` | |
| 50 | +| After `POST /servers/context7/disable` (+2 s) | **`Disabled`** | **`Enable`** | |
| 51 | +| After `POST /servers/context7/enable` (+5 s) | **`Connected (2 tools)`** | **`Disable`** | |
| 52 | + |
| 53 | +The accessibility tree is rendered by SwiftUI from `appState.servers`. That state is updated by the SSE handler (`CoreProcessManager.swift` `case "servers.changed":`) which, after this PR, **decodes `payload.servers` directly and skips `refreshServers()`**. The fact that the menu reflects the new state within 2-5 seconds without any HTTP refetch proves the new code path is live. |
| 54 | + |
| 55 | +A core-process inspection at the same time: |
| 56 | + |
| 57 | +- 22 `servers.changed` SSE events fired during the toggle window. |
| 58 | +- Every event payload contained `payload.servers` (length 30) and `payload.stats`. |
| 59 | +- The toggled server appeared in the embedded array with `enabled` and `connected` matching the post-toggle state. |
| 60 | + |
| 61 | +## Files in this directory |
| 62 | + |
| 63 | +``` |
| 64 | +tray_baseline.png Top-level tray menu, context7 enabled (1.0 MB) |
| 65 | +tray_after_disable.png Top-level tray menu, context7 disabled (1.0 MB) |
| 66 | +tray_after_reenable.png Top-level tray menu, context7 re-enabled (1.0 MB) |
| 67 | +cpu_post.pb.gz 60 s CPU profile after fix (3.78 % total) |
| 68 | +cpu_top_cum.txt pprof top -cum dump |
| 69 | +cpu_top_flat.txt pprof top dump |
| 70 | +cputime_delta.txt ps -o time delta over 60 s wall (2.91 s) |
| 71 | +goroutine_t0.txt Goroutine snapshot at start of the window |
| 72 | +goroutine_t1.txt Goroutine snapshot at end of the window |
| 73 | +heap.pb.gz Heap profile after fix |
| 74 | +allocs.pb.gz Alloc profile after fix |
| 75 | +mutex.pb.gz Mutex profile (empty) |
| 76 | +block.pb.gz Block profile after fix |
| 77 | +report.md CPU verification report |
| 78 | +visual.md This file |
| 79 | +``` |
| 80 | + |
| 81 | +The top-level tray screenshots don't visually surface `context7` (it's a submenu entry inside `Servers (30)`); the canonical proof is the accessibility-tree dump at lines `Connected (2 tools)` → `Disabled` → `Connected (2 tools)`. To get a screenshot of the open submenu, the macOS Accessibility API would have to keep two cascading menus open simultaneously, which `mcpproxy-ui-test`'s current screenshot tool does not support — but the textual tree above is sourced from the same accessibility hierarchy that paints the menu, so it's the authoritative state. |
0 commit comments