Commit 2173d65
feat(health): Unified Health Status Implementation (#192)
* docs: add unified health status design for OAuth UX consistency
* docs: add unified health status specification (012)
* docs: add unified health status specification and implementation plan
Add complete specification and implementation plan for consistent server
health status across CLI, tray, web UI, and MCP tools.
Artifacts:
- spec.md: Feature specification with user stories and requirements
- plan.md: Implementation plan with technical context and constitution check
- research.md: Research findings and decisions
- data-model.md: HealthStatus entity definition and state transitions
- contracts/api.yaml: OpenAPI schema additions
- quickstart.md: Implementation guide and verification checklist
Related #191
* /speckit:analyze fixes
* feat(health): add unified health status calculation for upstream servers
Implement unified health status that provides consistent server health
information across all interfaces (CLI, REST API, MCP tools).
Core changes:
- Add internal/health package with CalculateHealth() function
- Add HealthStatus struct with level, admin_state, summary, detail, action
- Integrate health calculation into runtime.GetAllServers()
- Add health field to MCP handleListUpstreams() response
- Update CLI upstream list to show health status and action hints
- Add HealthStatus schema to OpenAPI spec
- Add oauth_expiry_warning_hours config option
Health levels: healthy, degraded, unhealthy
Admin states: enabled, disabled, quarantined
Actions: login, restart, enable, approve, view_logs
The health calculator uses a priority-based algorithm:
1. Admin state (disabled/quarantined) short-circuits
2. Connection state (error/disconnected/connecting)
3. OAuth state (expired/error/expiring soon)
4. Healthy connected state
Includes 20+ unit tests covering all health scenarios including
FR-016 verification (token with refresh returns healthy).
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
* validate what is complete
* feat(health): implement unified health status across all interfaces
Complete implementation of the unified health status feature (012):
## Backend (already committed)
- HealthStatus struct with level, admin_state, summary, detail, action
- CalculateHealth() function in internal/health/calculator.go
- Health field added to contracts.Server
## CLI
- Updated upstream list to use health.level for status emoji
- Added action hints column showing CLI commands for fixes
- Distinct indicators for disabled/quarantined admin states
## Tray App
- Updated getServerStatusDisplay() to use unified health status
- Health-based status indicators (green/orange/red/paused/locked)
- Added restart action menu items based on health.action
## Web UI
- ServerCard.vue uses health.level for badge color
- Added action buttons (Login, Restart, Enable, Approve, View Logs)
- HealthStatus TypeScript interface in contracts.ts
## Dashboard
- X servers need attention banner for degraded/unhealthy servers
- Quick-fix action buttons in banner
- Filters out disabled/quarantined from attention list
## Documentation
- Updated CLAUDE.md with health status documentation
- Added HealthStatus schema to oas/swagger.yaml
- Created followup doc for verify-oas-coverage.sh issues
All 44 tasks complete.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
* fix(frontend): add HealthStatus type to api.ts for TypeScript compilation
The Server interface in api.ts was missing the health field and
HealthStatus interface that were added to contracts.ts. This caused
TypeScript compilation errors in ServerCard.vue and Dashboard.vue.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
* fix(health): improve unified health status consistency across interfaces
- Refactor CLI auth status to use unified health status from backend (FR-006, FR-007)
- CLI upstream list now uses health.CalculateHealth() for DRY principle (I-003)
- Add tooltip in ServerCard.vue showing health.detail for context (M-004)
- Add defensive null check in Dashboard.vue for backward compatibility (I-004)
- Include exact token expiration time in Detail field (M-002)
- Add debug logging for health status calculation (M-005)
- Add E2E tests verifying health field structure in MCP responses (FR-017, FR-018)
- Add unit tests ensuring Summary is never empty (FR-004, I-002)
- Extract health status in management service ListServers
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
* fix(health): use ordered slice for error pattern matching
The formatErrorSummary function used a map for error pattern matching,
but Go map iteration order is non-deterministic. This caused flaky test
failures when error messages matched multiple patterns (e.g., 'dial tcp:
no such host' matches both 'dial tcp' and 'no such host').
Changed to an ordered slice where more specific patterns (like 'no such
host') are checked before generic ones (like 'dial tcp').
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
* fix(tests): E2E tests use isolated config and instance-scoped container cleanup
Two issues were causing E2E tests to fail:
1. Tests loaded user's real ~/.mcpproxy/mcp_config.json instead of a clean
test config, causing connection attempts to 15+ real upstream servers.
Fixed by creating minimal config files in test temp directories.
2. Docker container cleanup affected ALL mcpproxy instances on the machine,
not just the test instance. This caused 15+ second shutdown delays as
the test server tried to clean up containers from other running instances.
Fixed by filtering container operations by instance ID label.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
* fix: repair merge conflict in types.go and regenerate OpenAPI
The GitHub UI merge resolution accidentally deleted the closing brace
for the HealthStatus struct. This fix adds the missing `}` and
regenerates the OpenAPI artifacts.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* test: update upstream_cmd tests for unified health status format
The table output format was changed to use unified health status
instead of separate ENABLED/CONNECTED columns. Updated tests to:
- Check for new headers: NAME, PROTOCOL, TOOLS, STATUS, ACTION
- Verify health status emojis (✅, ⏸️, 🔒, ❌) based on health level
and admin state instead of yes/no boolean values
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: Dumbris <a.dumbris@gmail.com>1 parent 0500460 commit 2173d65
34 files changed
Lines changed: 3354 additions & 243 deletions
File tree
- cmd/mcpproxy
- docs
- designs
- followups
- frontend/src
- components
- types
- views
- internal
- config
- contracts
- health
- management
- runtime
- server
- tray
- upstream
- core
- oas
- scripts
- specs/012-unified-health-status
- checklists
- contracts
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
159 | 159 | | |
160 | 160 | | |
161 | 161 | | |
| 162 | + | |
| 163 | + | |
| 164 | + | |
| 165 | + | |
| 166 | + | |
| 167 | + | |
| 168 | + | |
| 169 | + | |
| 170 | + | |
| 171 | + | |
| 172 | + | |
| 173 | + | |
| 174 | + | |
| 175 | + | |
| 176 | + | |
| 177 | + | |
| 178 | + | |
| 179 | + | |
| 180 | + | |
| 181 | + | |
| 182 | + | |
| 183 | + | |
| 184 | + | |
| 185 | + | |
| 186 | + | |
162 | 187 | | |
163 | 188 | | |
| 189 | + | |
| 190 | + | |
| 191 | + | |
| 192 | + | |
| 193 | + | |
| 194 | + | |
| 195 | + | |
| 196 | + | |
| 197 | + | |
| 198 | + | |
| 199 | + | |
| 200 | + | |
| 201 | + | |
| 202 | + | |
| 203 | + | |
| 204 | + | |
| 205 | + | |
| 206 | + | |
| 207 | + | |
| 208 | + | |
| 209 | + | |
| 210 | + | |
| 211 | + | |
| 212 | + | |
| 213 | + | |
| 214 | + | |
| 215 | + | |
| 216 | + | |
| 217 | + | |
| 218 | + | |
| 219 | + | |
| 220 | + | |
| 221 | + | |
| 222 | + | |
| 223 | + | |
| 224 | + | |
| 225 | + | |
| 226 | + | |
| 227 | + | |
| 228 | + | |
| 229 | + | |
| 230 | + | |
| 231 | + | |
| 232 | + | |
| 233 | + | |
| 234 | + | |
| 235 | + | |
| 236 | + | |
| 237 | + | |
| 238 | + | |
| 239 | + | |
| 240 | + | |
| 241 | + | |
| 242 | + | |
| 243 | + | |
| 244 | + | |
| 245 | + | |
| 246 | + | |
| 247 | + | |
| 248 | + | |
| 249 | + | |
| 250 | + | |
| 251 | + | |
| 252 | + | |
| 253 | + | |
164 | 254 | | |
165 | 255 | | |
166 | 256 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
228 | 228 | | |
229 | 229 | | |
230 | 230 | | |
231 | | - | |
232 | 231 | | |
233 | | - | |
234 | | - | |
235 | 232 | | |
236 | 233 | | |
237 | 234 | | |
| |||
247 | 244 | | |
248 | 245 | | |
249 | 246 | | |
250 | | - | |
251 | | - | |
252 | | - | |
253 | | - | |
254 | | - | |
255 | | - | |
256 | | - | |
257 | | - | |
258 | | - | |
259 | | - | |
260 | | - | |
261 | | - | |
262 | | - | |
263 | | - | |
264 | | - | |
265 | | - | |
266 | | - | |
267 | | - | |
268 | | - | |
269 | | - | |
270 | | - | |
271 | | - | |
272 | | - | |
273 | | - | |
274 | | - | |
275 | | - | |
276 | | - | |
277 | | - | |
278 | | - | |
| 247 | + | |
| 248 | + | |
| 249 | + | |
| 250 | + | |
| 251 | + | |
| 252 | + | |
| 253 | + | |
| 254 | + | |
| 255 | + | |
| 256 | + | |
| 257 | + | |
| 258 | + | |
| 259 | + | |
| 260 | + | |
| 261 | + | |
| 262 | + | |
| 263 | + | |
| 264 | + | |
| 265 | + | |
| 266 | + | |
| 267 | + | |
| 268 | + | |
| 269 | + | |
| 270 | + | |
| 271 | + | |
279 | 272 | | |
280 | | - | |
281 | | - | |
282 | | - | |
283 | | - | |
284 | | - | |
285 | | - | |
286 | | - | |
287 | | - | |
288 | | - | |
| 273 | + | |
289 | 274 | | |
290 | 275 | | |
291 | 276 | | |
292 | 277 | | |
293 | | - | |
| 278 | + | |
| 279 | + | |
| 280 | + | |
| 281 | + | |
| 282 | + | |
| 283 | + | |
| 284 | + | |
| 285 | + | |
| 286 | + | |
| 287 | + | |
| 288 | + | |
| 289 | + | |
| 290 | + | |
| 291 | + | |
| 292 | + | |
| 293 | + | |
| 294 | + | |
| 295 | + | |
| 296 | + | |
294 | 297 | | |
295 | 298 | | |
296 | 299 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
18 | 18 | | |
19 | 19 | | |
20 | 20 | | |
| 21 | + | |
21 | 22 | | |
22 | 23 | | |
23 | 24 | | |
| |||
177 | 178 | | |
178 | 179 | | |
179 | 180 | | |
| 181 | + | |
| 182 | + | |
| 183 | + | |
| 184 | + | |
| 185 | + | |
| 186 | + | |
| 187 | + | |
| 188 | + | |
| 189 | + | |
| 190 | + | |
| 191 | + | |
| 192 | + | |
| 193 | + | |
| 194 | + | |
| 195 | + | |
| 196 | + | |
| 197 | + | |
180 | 198 | | |
181 | 199 | | |
182 | 200 | | |
183 | 201 | | |
184 | 202 | | |
185 | 203 | | |
186 | | - | |
| 204 | + | |
| 205 | + | |
| 206 | + | |
| 207 | + | |
| 208 | + | |
| 209 | + | |
| 210 | + | |
| 211 | + | |
187 | 212 | | |
188 | 213 | | |
189 | 214 | | |
| |||
206 | 231 | | |
207 | 232 | | |
208 | 233 | | |
209 | | - | |
210 | | - | |
211 | | - | |
212 | | - | |
| 234 | + | |
| 235 | + | |
| 236 | + | |
| 237 | + | |
213 | 238 | | |
214 | 239 | | |
215 | 240 | | |
216 | | - | |
217 | 241 | | |
218 | | - | |
219 | 242 | | |
220 | | - | |
221 | 243 | | |
222 | | - | |
223 | | - | |
224 | | - | |
| 244 | + | |
| 245 | + | |
| 246 | + | |
| 247 | + | |
| 248 | + | |
| 249 | + | |
| 250 | + | |
| 251 | + | |
| 252 | + | |
| 253 | + | |
| 254 | + | |
| 255 | + | |
225 | 256 | | |
226 | 257 | | |
227 | | - | |
228 | | - | |
229 | | - | |
| 258 | + | |
| 259 | + | |
| 260 | + | |
| 261 | + | |
| 262 | + | |
| 263 | + | |
| 264 | + | |
| 265 | + | |
| 266 | + | |
| 267 | + | |
| 268 | + | |
| 269 | + | |
| 270 | + | |
| 271 | + | |
| 272 | + | |
| 273 | + | |
230 | 274 | | |
231 | 275 | | |
232 | | - | |
233 | | - | |
234 | | - | |
235 | | - | |
236 | | - | |
237 | | - | |
238 | | - | |
239 | | - | |
240 | | - | |
241 | | - | |
242 | | - | |
243 | | - | |
244 | | - | |
245 | | - | |
246 | | - | |
247 | | - | |
248 | | - | |
249 | | - | |
250 | | - | |
251 | | - | |
252 | | - | |
253 | | - | |
254 | | - | |
255 | | - | |
256 | | - | |
257 | | - | |
258 | | - | |
259 | | - | |
260 | | - | |
261 | | - | |
262 | | - | |
263 | | - | |
264 | | - | |
265 | | - | |
266 | | - | |
267 | | - | |
268 | | - | |
269 | | - | |
270 | | - | |
271 | | - | |
| 276 | + | |
| 277 | + | |
| 278 | + | |
| 279 | + | |
| 280 | + | |
| 281 | + | |
| 282 | + | |
| 283 | + | |
| 284 | + | |
| 285 | + | |
| 286 | + | |
| 287 | + | |
| 288 | + | |
272 | 289 | | |
273 | 290 | | |
274 | | - | |
275 | | - | |
| 291 | + | |
| 292 | + | |
276 | 293 | | |
277 | 294 | | |
278 | 295 | | |
| |||
686 | 703 | | |
687 | 704 | | |
688 | 705 | | |
689 | | - | |
690 | | - | |
691 | | - | |
692 | | - | |
693 | | - | |
694 | | - | |
695 | | - | |
696 | | - | |
697 | | - | |
698 | | - | |
699 | | - | |
700 | | - | |
701 | | - | |
702 | | - | |
703 | | - | |
704 | | - | |
705 | | - | |
706 | | - | |
707 | | - | |
708 | | - | |
709 | | - | |
0 commit comments