fix(agent): make MCP initialization failure non-fatal
When all MCP servers fail to connect (e.g., network unreachable), the agent loop would exit immediately, leaving the application in a zombie state where the gateway is running but no messages can be processed. Changes: - Downgrade MCP init failure from fatal error to warning in Run(), ProcessDirectWithChannel(), ProcessHeartbeat(), and Continue() - Add 30s timeout to HTTP client used for SSE/HTTP MCP transports to prevent indefinite blocking on unreachable servers The agent now continues operating without MCP tools when servers are unavailable, rather than becoming completely unresponsive.
This commit is contained in:
parent
dbf5d9ce1f
commit
8d4cc3fbba
4 changed files with 20 additions and 10 deletions
|
|
@ -132,7 +132,8 @@ func (al *AgentLoop) Run(ctx context.Context) error {
|
|||
return err
|
||||
}
|
||||
if err := al.ensureMCPInitialized(ctx); err != nil {
|
||||
return err
|
||||
logger.WarnCF("agent", "MCP initialization failed, continuing without MCP tools",
|
||||
map[string]any{"error": err.Error()})
|
||||
}
|
||||
|
||||
idleTicker := time.NewTicker(100 * time.Millisecond)
|
||||
|
|
|
|||
|
|
@ -48,7 +48,8 @@ func (al *AgentLoop) ProcessDirectWithChannel(
|
|||
return "", err
|
||||
}
|
||||
if err := al.ensureMCPInitialized(ctx); err != nil {
|
||||
return "", err
|
||||
logger.WarnCF("agent", "MCP initialization failed, processing without MCP tools",
|
||||
map[string]any{"error": err.Error()})
|
||||
}
|
||||
|
||||
msg := bus.InboundMessage{
|
||||
|
|
@ -73,7 +74,8 @@ func (al *AgentLoop) ProcessHeartbeat(
|
|||
return "", err
|
||||
}
|
||||
if err := al.ensureMCPInitialized(ctx); err != nil {
|
||||
return "", err
|
||||
logger.WarnCF("agent", "MCP initialization failed, processing heartbeat without MCP tools",
|
||||
map[string]any{"error": err.Error()})
|
||||
}
|
||||
|
||||
agent := al.GetRegistry().GetDefaultAgent()
|
||||
|
|
|
|||
|
|
@ -370,8 +370,8 @@ func (al *AgentLoop) Continue(ctx context.Context, sessionKey, channel, chatID s
|
|||
return "", err
|
||||
}
|
||||
if err := al.ensureMCPInitialized(ctx); err != nil {
|
||||
al.activeTurnStates.Delete(sessionKey)
|
||||
return "", err
|
||||
logger.WarnCF("agent", "MCP initialization failed, continuing steering without MCP tools",
|
||||
map[string]any{"error": err.Error(), "session_key": sessionKey})
|
||||
}
|
||||
|
||||
steeringMsgs := al.dequeueSteeringMessagesForScopeWithFallback(sessionKey)
|
||||
|
|
|
|||
|
|
@ -12,6 +12,7 @@ import (
|
|||
"strings"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
"time"
|
||||
|
||||
"github.com/modelcontextprotocol/go-sdk/mcp"
|
||||
|
||||
|
|
@ -341,14 +342,18 @@ func connectServer(
|
|||
DisableStandaloneSSE: disableStandaloneSSE,
|
||||
}
|
||||
|
||||
// Set up HTTP client with a connection timeout to avoid hanging
|
||||
// indefinitely when the MCP server is unreachable.
|
||||
mcpHTTPClient := &http.Client{
|
||||
Timeout: 30 * time.Second,
|
||||
}
|
||||
|
||||
// Add custom headers if provided
|
||||
if len(cfg.Headers) > 0 {
|
||||
// Create a custom HTTP client with header-injecting transport
|
||||
sseTransport.HTTPClient = &http.Client{
|
||||
Transport: &headerTransport{
|
||||
base: http.DefaultTransport,
|
||||
headers: cfg.Headers,
|
||||
},
|
||||
mcpHTTPClient.Transport = &headerTransport{
|
||||
base: http.DefaultTransport,
|
||||
headers: cfg.Headers,
|
||||
}
|
||||
logger.DebugCF("mcp", "Added custom HTTP headers",
|
||||
map[string]any{
|
||||
|
|
@ -357,6 +362,8 @@ func connectServer(
|
|||
})
|
||||
}
|
||||
|
||||
sseTransport.HTTPClient = mcpHTTPClient
|
||||
|
||||
transport = sseTransport
|
||||
case "stdio":
|
||||
if cfg.Command == "" {
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue