Feature Branch: 013-structured-server-state
Created: 2025-12-13
Updated: 2025-12-16
Status: Ready for Implementation
Depends On: #192 (Unified Health Status) - merged to main
The system has two separate data sources for server health:
- Health Status (
server.health) - Per-server health with action buttons - Diagnostics (
/api/v1/diagnostics) - System-wide aggregation with CLI hints
These systems detect the same issues independently, leading to:
- Duplicate banners on the Dashboard showing identical information
- Inconsistent data between CLI (
mcpproxy doctor) and Web UI - "Fix" buttons that show CLI hints instead of navigating to relevant pages
Goal: Health becomes the single source of truth for per-server issues. Diagnostics aggregates from Health for system-wide views.
As a developer, when I see a server health issue, I want the "Fix" button to navigate me to the right place to fix it.
Acceptance Scenarios:
- Given a server with a missing secret, When I click Fix, Then I navigate to
/ui/secrets - Given a server with OAuth config issue, When I click Fix, Then I navigate to server config tab
- Given a server with connection error, When I click Restart, Then the server restarts
As a developer, I want mcpproxy doctor and the Web UI to show the same issues.
Acceptance Scenarios:
- Given a missing secret, When I run
mcpproxy doctor, Then I see the same servers affected as in the Web UI - Given an OAuth issue, When I view Dashboard, Then I see the same information as
mcpproxy doctor --json
As a developer, I want to see ONE consolidated health display on the Dashboard.
Acceptance Scenarios:
- Given servers with issues, When I view Dashboard, Then I see one "Servers Needing Attention" section (not two separate banners)
As a developer, when I click on a server in the macOS tray menu, I want to see the appropriate action based on the server's health status.
Acceptance Scenarios:
- Given a stdio OAuth server needing login (e.g.,
npx @anthropic/mcp-gcal), When I click on it in the tray, Then I see "⚠️ Login Required" as a menu option - Given a server with
health.action == "login", When I view tray, Then "Login Required" appears regardless of whether server uses URL or command - Given a healthy server, When I view tray status, Then the tray shows the same connected count as Web UI
As a developer, when a server needs authentication, I don't want to see redundant technical error messages.
Acceptance Scenarios:
- Given a server with
health.action == "login", When I view it in Web UI, Then I see only the "Login" button, not a verbose error alert - Given a server with
health.action == "set_secret", When I view it, Then I see only the "Set Secret" button, not the underlying error
- FR-001: Health MUST detect missing secrets and set
action: "set_secret"with secret name indetail - FR-002: Health MUST detect OAuth config issues and set
action: "configure"with error indetail - FR-003: Health MUST be the single source of truth for all per-server issues
- FR-004: Health actions MUST include:
login,restart,enable,approve,set_secret,configure,view_logs,"" - FR-005: Each action MUST map to a UI navigation target or in-place action
| Action | UI Behavior |
|---|---|
login |
Trigger OAuth flow |
restart |
Restart the server |
enable |
Enable the server |
approve |
Unquarantine the server |
set_secret |
Navigate to /ui/secrets |
configure |
Navigate to server config tab |
view_logs |
Navigate to server logs tab |
"" |
No action needed |
- FR-006: Diagnostics MUST aggregate from individual server Health objects
- FR-007: Diagnostics MUST NOT have independent detection logic for issues that Health already detects
- FR-008: Diagnostics MUST group
set_secretissues by secret name (cross-cutting: one secret affects multiple servers) - FR-009: Diagnostics MUST include system-level checks (Docker status) that aren't per-server
- FR-010: Dashboard MUST display ONE consolidated health section
- FR-011: Fix/action buttons MUST navigate to relevant pages, not show CLI hints
- FR-012:
upstream listMUST handle new Health actions with appropriate CLI hints
- FR-013: Tray MUST use
health.levelfor server status icons, not legacyconnectedfield - FR-014: Tray MUST use
health.actionto determine available actions, not URL heuristics - FR-015: Tray MUST show "Login" action when
health.action == "login", regardless of transport protocol (stdio/http) - FR-016: Tray MUST use
health.summaryfor status text, not construct from legacy fields - FR-017: Tray MUST use
health.detailfor tooltip details, notlast_error
| Health Action | Tray Menu Item | Behavior |
|---|---|---|
login |
" |
Opens OAuth flow |
restart |
"🔄 Restart" | Restarts server |
enable |
"Enable" | Enables server |
approve |
"Approve" | Unquarantines server |
set_secret |
" |
Opens Web UI secrets page |
configure |
" |
Opens Web UI server config |
view_logs |
"View Logs" | Opens Web UI logs page |
"" |
(standard menu) | No special action needed |
- FR-018: Web UI MUST NOT show redundant
last_errorwhenhealth.actionalready conveys the issue - FR-019: When
health.actionislogin,set_secret, orconfigure, the action button is sufficient - suppress verbose error alert
- SC-001: Users see exactly ONE health section on Dashboard
- SC-002:
mcpproxy doctoroutput is derived from same data as Web UI - SC-003: All action buttons navigate to appropriate fix locations
- SC-004: Missing secrets show which servers are affected (aggregated by secret name)
- SC-005: Tray shows "Login Required" for stdio OAuth servers (e.g., Google Calendar via npx)
- SC-006: Tray connected count matches Web UI and CLI (all use
health.level) - SC-007: Web UI does not show verbose error message when login button is already displayed
healthy- No issuesdegraded- Minor issues (connecting, token expiring)unhealthy- Needs attention
enabled- Normal operationdisabled- User disabledquarantined- Pending approval
| Scenario | Level | Action | Detail | Web UI | CLI Hint |
|---|---|---|---|---|---|
| Healthy | healthy |
"" |
- | - | - |
| Connecting | degraded |
"" |
- | - | - |
| Token expiring | degraded |
login |
expiry time | OAuth flow | auth login --server=X |
| Connection error | unhealthy |
restart |
error message | Restarts server | upstream restart X |
| OAuth needed | unhealthy |
login |
- | OAuth flow | auth login --server=X |
| Missing secret | unhealthy |
set_secret |
secret name | /ui/secrets |
Set SECRET_NAME |
| OAuth config issue | unhealthy |
configure |
error/param | Server config tab | Edit config |
| Disabled | healthy |
enable |
- | Enables server | upstream enable X |
| Quarantined | healthy |
approve |
- | Approves server | Approve in Web UI |
- Adding new health checks (disk space, index health, latency metrics)
- Removing deprecated flat fields on Server struct
- Changes to the MCP protocol tool responses
- Use:
Related #[issue-number]- Links without auto-closing - Do NOT use:
Fixes #,Closes #,Resolves #- These auto-close on merge
- Do NOT include AI attribution in commits