Commit 513c06e
feat: Structured Server State - Health as Single Source of Truth (smart-mcp-proxy#205)
* docs: update 013 spec to reflect current implementation state
Mark completed items from smart-mcp-proxy#192 (Unified Health Status):
- HealthStatus struct and calculator implemented
- Health field on Server struct
- Frontend HealthStatus interface and component integration
- Action buttons working in ServerCard and Dashboard
Narrow remaining scope to:
- Structured state objects (OAuthState, ConnectionState)
- Doctor() aggregation from Health
- Dashboard UI consolidation
* docs: streamline 013 spec to focus on remaining work
Remove completed items (smart-mcp-proxy#192 Unified Health Status):
- HealthStatus struct, calculator, and frontend integration
- Health field on Server
- Action buttons in UI
Remaining scope:
- OAuthState and ConnectionState structured types
- Doctor() aggregation from Health
- Dashboard UI consolidation (remove duplicate diagnostics)
* docs: redesign 013 spec with Health as single source of truth
- Health becomes the single source of truth for per-server issues
- Add new Health actions: set_secret, configure
- Doctor() aggregates from Health instead of independent detection
- UI navigates to fix locations (secrets page, config tab)
- Remove OAuthState/ConnectionState structured objects from scope
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
* docs: add CLI handling to 013 spec
- Add FR-012: upstream list MUST handle new Health actions
- Add CLI Hint column to Actions table
- Add Phase 4: CLI Updates to plan.md
- Add section 8: CLI Updates to quickstart.md
* Add tasks
* feat: add set_secret and configure health actions
Implement Health as single source of truth for server state by adding
two new action types that detect and surface configuration issues:
- set_secret: Shown when a server references an unresolved secret
(e.g., ${env:MISSING_TOKEN}). Detail field contains the secret name.
- configure: Shown when OAuth configuration has parameter errors
(e.g., missing RFC 8707 resource parameter).
Changes:
- Add ActionSetSecret/ActionConfigure constants and extraction helpers
- Extend HealthCalculatorInput with MissingSecret/OAuthConfigErr fields
- Add detection logic in CalculateHealth() with unit tests
- Refactor Doctor() to aggregate diagnostics from Health.Action
- Add action buttons in ServerCard.vue and Dashboard.vue
- Update CLI hints in upstream_cmd.go for new actions
- Update TypeScript types and swagger.yaml documentation
This ensures CLI (mcpproxy doctor), Web UI, and MCP tools all show
consistent health information derived from the same source.
* fix: propagate health field to tray for consistent connected count
The tray was showing incorrect connected counts (8/15) compared to CLI
(13 healthy) because the health field was not being propagated through
the API client layer.
Changes:
- Add HealthStatus struct and Health field to tray API client
- Extract health from JSON response in GetServers()
- Include health in adapter GetAllServers() output map
- Add extractHealthLevel() helper to managers.go
- Add debug logging to trace health extraction
- Add validation script (scripts/validate-health-api.sh)
- Add tests for health propagation and consistency
Spec 013: Health is the single source of truth for server status.
* fix(frontend): use health.level for connected server count
The web UI was showing 8 connected servers instead of 13 because it
was using the legacy connected boolean field instead of health.level.
Added isServerConnected() helper that uses health.level === healthy
as the source of truth (per Spec 013), with fallback to legacy connected
field for backward compatibility.
This aligns the web UI with the tray and CLI which were fixed in the
previous commit.
* fix: improve doctor CLI output and health propagation
- Fix OAuth display to show server objects with specific auth login hints
- Add sortArrayByServerName for consistent doctor output ordering
- Fix health extraction to handle both struct pointers and maps
- Add health status calculation to GetAllServers() endpoint
- Update Action field docs to include set_secret and configure
* fix: use correct field names for missing secrets in doctor output
Update doctor command to use field names from contracts.MissingSecretInfo:
- secret_name instead of name
- used_by (array) instead of server
Also fix tests to match the actual backend JSON structure.
* make swagger
* fix(frontend): add Login option to server detail page Actions dropdown
Use unified health.action to show Login button consistently with
ServerCard.vue, replacing the narrow needsOAuth check that only
matched errors containing 'authorization'.
* chore(frontend): remove dead needsOAuth computed properties
Both ServerCard.vue and ServerDetail.vue now use healthAction from the
unified health status. The needsOAuth computed properties were no longer
referenced anywhere and can be safely removed.
* fix: add build tag to managers_test.go for Linux CI compatibility
The test file was missing the same build constraint as managers.go,
causing test compilation failures on Linux (ubuntu-latest in CI).
The managers.go file has //go:build !nogui && !headless && !linux
but managers_test.go had no build tag, leading to undefined:
extractHealthLevel errors when running tests on Linux.
* chore: remove unused diagnostics functions after Health.Action refactor
* refactor: extract isHealthy helper pattern for consistent health checks
Add centralized helper functions for checking server health status:
Go:
- Add IsHealthy() to internal/health/constants.go for core server use
- Add isServerHealthy() to tray adapter for local HealthStatus type
- Update tray adapter to use helper instead of duplicated inline logic
TypeScript:
- Create frontend/src/utils/health.ts with isServerConnected() helper
- Add HealthLevel, AdminState, HealthAction constants matching backend
- Update stores/servers.ts to import from centralized utility
Benefits:
- Single source of truth for health check logic per language
- Consistent fallback behavior when health is nil/undefined
- Easier maintenance when updating health check algorithm
- Better testability with isolated helper functions
Implements suggestion smart-mcp-proxy#1 from PR smart-mcp-proxy#205 review.
* refactor(types): eliminate magic strings for health actions
Add health constants generation to cmd/generate-types to create
TypeScript types from a single source of truth:
- Add HealthLevel, AdminState, and HealthAction const/type exports
- Add HealthStatus interface using the typed unions
- Fix generate-types output path to frontend/src/types/
- Update Server interface with missing fields (connecting, authenticated,
tool_list_token_size, oauth_status, token_expires_at, user_logged_out,
health)
- Add UpdateInfo and InfoResponse types
- Update api.ts to re-export health types from contracts.ts
- Add documentation to Go constants noting TypeScript synchronization
This prevents drift between Go and TypeScript when adding new health
actions and provides compile-time type checking for action strings.
* refactor(frontend): consolidate APIResponse type to single source of truth
Remove duplicate APIResponse interface from api.ts and re-export it from
contracts.ts, which is the generated source of truth for types derived
from Go constants.
* test(tray): add real unit tests for ServerAdapter with mock client
Replace documentation-style tests with 22 actual unit tests that verify
adapter behavior through mock dependency injection. Changes include:
- Add ClientInterface to enable mock-based testing
- Refactor ServerAdapter to depend on interface instead of concrete Client
- Add MockClient implementation for controlled test scenarios
- Test health data preservation through GetAllServers transformation
- Test isServerHealthy helper with all health level and fallback cases
- Test GetUpstreamStats correctly uses health.level as source of truth
- Test error handling paths for API failures
- Add end-to-end health data flow integration test
* feat(tray,frontend): use health.action as single source of truth for actions
Implements User Stories 4 and 5 from spec 013:
Tray (US4 - FR-013 through FR-017):
- Remove serverSupportsOAuth() URL heuristic, use health.action instead
- Show Login Required for stdio OAuth servers (e.g., npx @anthropic/mcp-gcal)
- Add menu items for set_secret and configure health actions
- Use health.level for connected count (no legacy fallback)
- Use health.detail for tooltip instead of last_error
Web UI (US5 - FR-018, FR-019):
- Suppress redundant last_error display when health.action conveys the issue
- Add shouldShowError computed property
- Login/set_secret/configure actions suppress verbose error alert
Tests:
- TestServerNeedsAction: verifies health.action detection
- TestTrayLoginActionForStdioServers: stdio OAuth servers show login
- TestConnectedCountHealthLevelOnly: no fallback to legacy connected
- TestHealthActionMenuItemSelection: correct menu items per action
---------
Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: Algis Dumbris <a.dumbris@gmail.com>1 parent 4f48334 commit 513c06e
38 files changed
Lines changed: 4065 additions & 386 deletions
File tree
- cmd
- generate-types
- mcpproxy-tray/internal/api
- mcpproxy
- frontend/src
- components
- stores
- types
- utils
- views
- internal
- contracts
- health
- management
- runtime
- server
- tray
- oas
- scripts
- specs/013-structured-server-state
- checklists
- contracts
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
14 | 14 | | |
15 | 15 | | |
16 | 16 | | |
17 | | - | |
| 17 | + | |
18 | 18 | | |
19 | 19 | | |
20 | 20 | | |
| |||
46 | 46 | | |
47 | 47 | | |
48 | 48 | | |
| 49 | + | |
| 50 | + | |
| 51 | + | |
| 52 | + | |
| 53 | + | |
| 54 | + | |
| 55 | + | |
| 56 | + | |
| 57 | + | |
| 58 | + | |
| 59 | + | |
| 60 | + | |
| 61 | + | |
| 62 | + | |
| 63 | + | |
| 64 | + | |
| 65 | + | |
| 66 | + | |
| 67 | + | |
| 68 | + | |
| 69 | + | |
| 70 | + | |
| 71 | + | |
| 72 | + | |
| 73 | + | |
| 74 | + | |
| 75 | + | |
| 76 | + | |
| 77 | + | |
| 78 | + | |
| 79 | + | |
| 80 | + | |
| 81 | + | |
| 82 | + | |
| 83 | + | |
| 84 | + | |
| 85 | + | |
| 86 | + | |
| 87 | + | |
| 88 | + | |
| 89 | + | |
| 90 | + | |
| 91 | + | |
| 92 | + | |
49 | 93 | | |
50 | 94 | | |
51 | 95 | | |
| |||
63 | 107 | | |
64 | 108 | | |
65 | 109 | | |
| 110 | + | |
| 111 | + | |
66 | 112 | | |
67 | 113 | | |
68 | 114 | | |
69 | 115 | | |
70 | 116 | | |
71 | 117 | | |
| 118 | + | |
72 | 119 | | |
73 | 120 | | |
74 | 121 | | |
| 122 | + | |
| 123 | + | |
| 124 | + | |
| 125 | + | |
75 | 126 | | |
76 | 127 | | |
77 | 128 | | |
| |||
307 | 358 | | |
308 | 359 | | |
309 | 360 | | |
| 361 | + | |
| 362 | + | |
| 363 | + | |
| 364 | + | |
| 365 | + | |
| 366 | + | |
| 367 | + | |
| 368 | + | |
| 369 | + | |
| 370 | + | |
| 371 | + | |
| 372 | + | |
| 373 | + | |
| 374 | + | |
| 375 | + | |
| 376 | + | |
| 377 | + | |
| 378 | + | |
| 379 | + | |
| 380 | + | |
| 381 | + | |
310 | 382 | | |
311 | 383 | | |
312 | 384 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
10 | 10 | | |
11 | 11 | | |
12 | 12 | | |
| 13 | + | |
| 14 | + | |
| 15 | + | |
| 16 | + | |
| 17 | + | |
| 18 | + | |
| 19 | + | |
| 20 | + | |
| 21 | + | |
| 22 | + | |
| 23 | + | |
| 24 | + | |
| 25 | + | |
| 26 | + | |
| 27 | + | |
| 28 | + | |
| 29 | + | |
| 30 | + | |
| 31 | + | |
| 32 | + | |
| 33 | + | |
13 | 34 | | |
14 | 35 | | |
15 | | - | |
| 36 | + | |
16 | 37 | | |
17 | 38 | | |
18 | 39 | | |
19 | | - | |
| 40 | + | |
20 | 41 | | |
21 | 42 | | |
22 | 43 | | |
| |||
61 | 82 | | |
62 | 83 | | |
63 | 84 | | |
64 | | - | |
| 85 | + | |
65 | 86 | | |
66 | 87 | | |
67 | 88 | | |
| |||
114 | 135 | | |
115 | 136 | | |
116 | 137 | | |
117 | | - | |
| 138 | + | |
118 | 139 | | |
119 | 140 | | |
120 | 141 | | |
| |||
215 | 236 | | |
216 | 237 | | |
217 | 238 | | |
218 | | - | |
| 239 | + | |
219 | 240 | | |
220 | 241 | | |
221 | 242 | | |
| |||
230 | 251 | | |
231 | 252 | | |
232 | 253 | | |
233 | | - | |
| 254 | + | |
| 255 | + | |
| 256 | + | |
| 257 | + | |
| 258 | + | |
| 259 | + | |
| 260 | + | |
| 261 | + | |
| 262 | + | |
| 263 | + | |
| 264 | + | |
| 265 | + | |
| 266 | + | |
| 267 | + | |
234 | 268 | | |
235 | 269 | | |
236 | 270 | | |
| |||
0 commit comments