Skip to content

Commit 43a4688

Browse files
docs: add unified health status design for OAuth UX consistency
1 parent 5bbab84 commit 43a4688

1 file changed

Lines changed: 231 additions & 0 deletions

File tree

Lines changed: 231 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,231 @@
1+
# Unified Health Status Design
2+
3+
**Date**: 2025-12-10
4+
**Status**: Ready for implementation
5+
6+
## Problem Statement
7+
8+
**Current issues:**
9+
1. **Inconsistent status** - CLI, tray, and web show different health interpretations
10+
2. **Missing OAuth visibility** - Token expiration not shown in tray/web
11+
3. **No actionable guidance** - Users see errors but not how to fix them
12+
4. **Conflated concepts** - Admin state (disabled/quarantined) mixed with health
13+
14+
**Root cause:** Each interface calculates status independently from raw fields, leading to drift. For example:
15+
- CLI reads `oauth_status` and shows "Token Expired"
16+
- Tray only checks HTTP connectivity and shows "Healthy"
17+
- Same server, different conclusions
18+
19+
**Goals:**
20+
- Single source of truth for server health in the backend
21+
- Consistent display across CLI, tray, and web UI
22+
- Traffic light model: healthy (green) / degraded (yellow) / unhealthy (red)
23+
- Every degraded/unhealthy state includes an action to resolve it
24+
- Admin state (enabled/disabled/quarantined) shown separately from health
25+
26+
**Non-goals:**
27+
- Changing OAuth flow mechanics
28+
- Adding new OAuth features
29+
- Redesigning the web UI layout
30+
31+
## Data Model
32+
33+
**New `HealthStatus` struct** (in `internal/contracts/types.go`):
34+
35+
```go
36+
type HealthStatus struct {
37+
Level string `json:"level"` // "healthy", "degraded", "unhealthy"
38+
AdminState string `json:"admin_state"` // "enabled", "disabled", "quarantined"
39+
Summary string `json:"summary"` // "Connected (5 tools)", "Token expiring in 2h"
40+
Detail string `json:"detail"` // Optional longer explanation
41+
Action string `json:"action"` // "login", "restart", "enable", "approve", "view_logs", ""
42+
}
43+
```
44+
45+
**Added to existing `Server` struct:**
46+
47+
```go
48+
type Server struct {
49+
// ... existing fields ...
50+
Health HealthStatus `json:"health"` // New unified health status
51+
}
52+
```
53+
54+
**Level values:**
55+
| Level | Meaning | View convention |
56+
|-------|---------|-----------------|
57+
| `healthy` | Ready to use, no issues | green |
58+
| `degraded` | Works but needs attention soon | yellow |
59+
| `unhealthy` | Broken, can't use until fixed | red |
60+
61+
**Action types:**
62+
| Action | Meaning |
63+
|--------|---------|
64+
| `""` | No action needed (healthy state) |
65+
| `login` | OAuth authentication required |
66+
| `restart` | Server needs restart |
67+
| `enable` | Server is disabled |
68+
| `approve` | Server is quarantined |
69+
| `view_logs` | Check logs for details |
70+
71+
## Health Calculation Logic
72+
73+
**Location:** `internal/runtime/runtime.go` in `GetAllServers()` (or extracted to `internal/health/calculator.go`)
74+
75+
**Priority order** (first match wins):
76+
77+
```
78+
1. Admin state checks (shown instead of health when not enabled)
79+
- quarantined → AdminState: "quarantined"
80+
- disabled → AdminState: "disabled"
81+
82+
2. Unhealthy (red) conditions
83+
- connection refused/failed → "unhealthy", Action: "restart"
84+
- auth failed (bad credentials) → "unhealthy", Action: "login"
85+
- server crashed → "unhealthy", Action: "restart"
86+
- config error → "unhealthy", Action: "view_logs"
87+
- token expired → "unhealthy", Action: "login"
88+
- refresh failed (after retries)→ "unhealthy", Action: "login"
89+
- user logged out → "unhealthy", Action: "login"
90+
91+
3. Degraded (yellow) conditions
92+
- token expiring soon, no refresh token → "degraded", Action: "login"
93+
- connecting (in progress) → "degraded", Action: ""
94+
95+
4. Healthy (green)
96+
- connected + authenticated (OAuth servers)
97+
- connected (non-OAuth servers)
98+
- token valid OR auto-refresh working
99+
```
100+
101+
**OAuth-specific logic:**
102+
| Condition | Level | Action |
103+
|-----------|-------|--------|
104+
| Token valid OR auto-refresh working | `healthy` | - |
105+
| Token expiring soon, no refresh token | `degraded` | `login` |
106+
| Token expired | `unhealthy` | `login` |
107+
| Refresh failed (after retries) | `unhealthy` | `login` |
108+
| User logged out | `unhealthy` | `login` |
109+
110+
**Key distinction:**
111+
- **Degraded** = works now but will break soon without action
112+
- **Unhealthy** = broken, can't use until fixed
113+
114+
## Interface Display
115+
116+
Each interface renders `HealthStatus` consistently but adapted to its medium.
117+
118+
### CLI
119+
120+
**`mcpproxy upstream list` and `mcpproxy auth status`:**
121+
```
122+
Server Health Action
123+
───────────────────────────────────────────────────────────────────
124+
slack 🟢 Connected (5 tools)
125+
github 🟡 Token expiring in 45m → auth login --server=github
126+
filesystem 🔴 Connection refused → upstream restart filesystem
127+
new-server ⏸️ Quarantined → Approve in Web UI
128+
old-server ⏹️ Disabled → upstream enable old-server
129+
```
130+
131+
### Tray Menu
132+
133+
```
134+
🟢 slack
135+
🟡 github - Token expiring
136+
🔴 filesystem - Error
137+
⏸️ new-server (Quarantined)
138+
⏹️ old-server (Disabled)
139+
```
140+
141+
Clicking yellow/red servers opens Web UI to the relevant fix page.
142+
143+
### Web UI
144+
145+
| Location | Shows | Actions |
146+
|----------|-------|---------|
147+
| **Dashboard** | "X servers need attention" banner | Quick-fix buttons per server |
148+
| **ServerCard** | Colored status badge + summary | Login/Restart/Reconnect based on `action` field |
149+
| **ServerDetail** | Full health details | Same actions + logs |
150+
151+
### Action Hint Mapping
152+
153+
Each interface maps the `Action` field to its own UX:
154+
155+
**CLI:**
156+
```
157+
"login" → "auth login --server=%s"
158+
"restart" → "upstream restart %s"
159+
"enable" → "upstream enable %s"
160+
"approve" → "Approve in Web UI or config"
161+
"view_logs"→ "upstream logs %s"
162+
```
163+
164+
**Tray:**
165+
```
166+
"login" → opens http://localhost:8080/ui/servers/{name}?action=login
167+
"restart" → triggers API call directly
168+
"enable" → triggers API call directly
169+
"approve" → opens http://localhost:8080/ui/servers/{name}?action=approve
170+
```
171+
172+
**Web UI:**
173+
```
174+
"login" → Login button
175+
"restart" → Restart button
176+
"enable" → Enable toggle
177+
"approve" → Approve button
178+
```
179+
180+
## Implementation Changes
181+
182+
**Files to modify:**
183+
184+
| File | Change |
185+
|------|--------|
186+
| `internal/contracts/types.go` | Add `HealthStatus` struct |
187+
| `internal/runtime/runtime.go` | Calculate `Health` in `GetAllServers()` |
188+
| `internal/httpapi/server.go` | Ensure `health` field is included in API response |
189+
| `cmd/mcpproxy/upstream_cmd.go` | Update `upstream list` to use `Health` field |
190+
| `cmd/mcpproxy/auth_cmd.go` | Update `auth status` to use `Health` field |
191+
| `internal/tray/managers.go` | Update `getServerStatusDisplay()` to use `Health` field |
192+
| `frontend/src/components/ServerCard.vue` | Use `health` for badge color + show action |
193+
| `frontend/src/views/Dashboard.vue` | Use `health.level` to filter servers needing attention |
194+
195+
**No backward compatibility needed** - all clients (CLI, tray, web) ship together in mcpproxy releases.
196+
197+
## Architecture
198+
199+
```
200+
┌─────────────────────────────────────────────────────────────┐
201+
│ Backend (Runtime) │
202+
│ ┌─────────────────────────────────────────────────────────┐│
203+
│ │ CalculateHealth() → HealthStatus ││
204+
│ │ - Level: healthy/degraded/unhealthy ││
205+
│ │ - AdminState: enabled/disabled/quarantined ││
206+
│ │ - Summary: "Connected (5 tools)" ││
207+
│ │ - Action: login/restart/enable/approve/"" ││
208+
│ └─────────────────────────────────────────────────────────┘│
209+
└─────────────────────────────────────────────────────────────┘
210+
211+
GET /api/v1/servers
212+
213+
┌───────────────────┼───────────────────┐
214+
│ │ │
215+
▼ ▼ ▼
216+
┌─────────┐ ┌─────────┐ ┌─────────┐
217+
│ CLI │ │ Tray │ │ Web UI │
218+
│ │ │ │ │ │
219+
│ 🟢/🟡/🔴 │ │ 🟢/🟡/🔴 │ │ badges │
220+
│ + hint │ │ + click │ │ + btns │
221+
└─────────┘ └─────────┘ └─────────┘
222+
```
223+
224+
**Key principle:** Backend owns health calculation. Interfaces only render.
225+
226+
## Success Criteria
227+
228+
1. All three interfaces show identical health status for any server
229+
2. Yellow/red states always include actionable guidance
230+
3. OAuth token issues visible in tray and web (not just CLI)
231+
4. Admin state (disabled/quarantined) clearly distinct from health

0 commit comments

Comments
 (0)