fix(mcp): require 3 consecutive ping failures before marking server disconnected

Single transient errors (e.g. 504 from upstream proxy) no longer
instantly disconnect MCP servers. Requires healthFailThreshold (3)
consecutive failures before setting connected=false and triggering
reconnect. Applies to both Manager.healthLoop and poolHealthLoop.
This commit is contained in:
viettranx committed 2026-03-31 23:15:59 +07:00
1 parent e48ba1df7f
commit ff4d370d27
2 files changed
+24 -5

No files matched your search

+12 -3
View File
@@ -241,21 +241,30 @@ func (m *Manager) healthLoop(ctx context.Context, ss *serverState) {
ss.connected.Store(true)
ss.mu.Lock()
ss.reconnAttempts = 0
ss.healthFailures = 0
ss.lastErr = ""
ss.mu.Unlock()
continue
}
ss.connected.Store(false)
ss.mu.Lock()
ss.healthFailures++
failures := ss.healthFailures
ss.lastErr = err.Error()
ss.mu.Unlock()
slog.Warn("mcp.server.health_failed", "server", ss.name, "error", err)
m.tryReconnect(ctx, ss)
slog.Warn("mcp.server.health_failed", "server", ss.name, "error", err, "consecutive", failures)
// Only mark disconnected and attempt reconnect after consecutive failures
// to tolerate transient errors (e.g. 504 from upstream proxy).
if failures >= healthFailThreshold {
ss.connected.Store(false)
m.tryReconnect(ctx, ss)
}
} else {
ss.connected.Store(true)
ss.mu.Lock()
ss.reconnAttempts = 0
ss.healthFailures = 0
ss.lastErr = ""
ss.mu.Unlock()
}
+12 -2
View File
@@ -610,17 +610,27 @@ func poolHealthLoop(ctx context.Context, ss *serverState) {
if err := ss.client.Ping(ctx); err != nil {
if isMethodNotFound(err) {
ss.connected.Store(true)
ss.mu.Lock()
ss.healthFailures = 0
ss.mu.Unlock()
continue
}
ss.connected.Store(false)
ss.mu.Lock()
ss.healthFailures++
failures := ss.healthFailures
ss.lastErr = err.Error()
ss.mu.Unlock()
slog.Warn("mcp.pool.health_failed", "server", ss.name, "error", err)
slog.Warn("mcp.pool.health_failed", "server", ss.name, "error", err, "consecutive", failures)
if failures >= healthFailThreshold {
ss.connected.Store(false)
}
} else {
ss.connected.Store(true)
ss.mu.Lock()
ss.reconnAttempts = 0
ss.healthFailures = 0
ss.lastErr = ""
ss.mu.Unlock()
}