@@ -12,6 +12,7 @@ import (
1212 "mcpproxy-go/internal/config"
1313 "mcpproxy-go/internal/runtime/configsvc"
1414 "mcpproxy-go/internal/runtime/stateview"
15+ "mcpproxy-go/internal/upstream/types"
1516)
1617
1718// Supervisor manages the desired vs actual state reconciliation for upstream servers.
@@ -487,6 +488,30 @@ func (s *Supervisor) updateStateView(name string, state *ServerState) {
487488
488489 // Update connection info if available
489490 if state .ConnectionInfo != nil {
491+ // Extract LastError from ConnectionInfo and convert to string with limit
492+ if state .ConnectionInfo .LastError != nil {
493+ errorStr := state .ConnectionInfo .LastError .Error ()
494+ // Limit error string to 500 characters to prevent UI hangs
495+ const maxErrorLen = 500
496+ if len (errorStr ) > maxErrorLen {
497+ errorStr = errorStr [:maxErrorLen ] + "... (truncated)"
498+ }
499+ status .LastError = errorStr
500+
501+ // Set last error time if available
502+ if ! state .ConnectionInfo .LastRetryTime .IsZero () {
503+ t := state .ConnectionInfo .LastRetryTime
504+ status .LastErrorTime = & t
505+ }
506+ } else {
507+ status .LastError = ""
508+ status .LastErrorTime = nil
509+ }
510+
511+ // Copy retry count
512+ status .RetryCount = state .ConnectionInfo .RetryCount
513+
514+ // Store full connection info in metadata for debugging
490515 if status .Metadata == nil {
491516 status .Metadata = make (map [string ]interface {})
492517 }
@@ -628,16 +653,18 @@ func (s *Supervisor) updateSnapshotFromEvent(event Event) {
628653 state .LastSeen = event .Timestamp
629654
630655 // Phase 7.1 FIX: Don't fetch tools on connect events - let background indexing handle it
631- // Just update the cached tool count from the client (non-blocking)
656+ // Just update the cached tool count and connection info from the client (non-blocking)
632657 var toolCount int
633- if connected {
634- if actualState , err := s .upstream .GetServerState (event .ServerName ); err == nil {
635- toolCount = actualState .ToolCount
636- } else {
637- s .logger .Warn ("Failed to get server state for tool count" ,
638- zap .String ("server" , event .ServerName ),
639- zap .Error (err ))
640- }
658+ var connInfo * types.ConnectionInfo
659+ if actualState , err := s .upstream .GetServerState (event .ServerName ); err == nil {
660+ toolCount = actualState .ToolCount
661+ // Update ConnectionInfo for error propagation to UI
662+ state .ConnectionInfo = actualState .ConnectionInfo
663+ connInfo = actualState .ConnectionInfo
664+ } else {
665+ s .logger .Warn ("Failed to get server state for tool count" ,
666+ zap .String ("server" , event .ServerName ),
667+ zap .Error (err ))
641668 }
642669
643670 // Update stateview
@@ -661,6 +688,27 @@ func (s *Supervisor) updateSnapshotFromEvent(event Event) {
661688 status .Tools = nil // Clear tools on disconnect
662689 status .ToolCount = 0
663690 }
691+
692+ // Update ConnectionInfo for immediate error propagation to UI
693+ if connInfo != nil {
694+ if connInfo .LastError != nil {
695+ errorStr := connInfo .LastError .Error ()
696+ const maxErrorLen = 500
697+ if len (errorStr ) > maxErrorLen {
698+ errorStr = errorStr [:maxErrorLen ] + "... (truncated)"
699+ }
700+ status .LastError = errorStr
701+
702+ if ! connInfo .LastRetryTime .IsZero () {
703+ t := connInfo .LastRetryTime
704+ status .LastErrorTime = & t
705+ }
706+ } else {
707+ status .LastError = ""
708+ status .LastErrorTime = nil
709+ }
710+ status .RetryCount = connInfo .RetryCount
711+ }
664712 })
665713
666714 // Trigger reactive tool discovery when server connects
0 commit comments