|
7 | 7 | "fmt" |
8 | 8 | "log/slog" |
9 | 9 | "slices" |
| 10 | + "strings" |
| 11 | + "time" |
10 | 12 |
|
11 | 13 | "github.com/mark3labs/mcp-go/mcp" |
12 | 14 |
|
@@ -51,16 +53,51 @@ func (c *Client) Start(ctx context.Context) error { |
51 | 53 | Version: "1.0.0", |
52 | 54 | } |
53 | 55 |
|
54 | | - _, err := c.client.Initialize(ctx, initRequest) |
55 | | - if err != nil { |
56 | | - slog.Error("Failed to initialize MCP client", "error", err) |
57 | | - return fmt.Errorf("failed to initialize MCP client: %w", err) |
| 56 | + const maxRetries = 3 |
| 57 | + for attempt := 0; ; attempt++ { |
| 58 | + _, err := c.client.Initialize(ctx, initRequest) |
| 59 | + if err == nil { |
| 60 | + break |
| 61 | + } |
| 62 | + // TODO(krissetto): This is a temporary fix to handle the case where the remote server hasn't finished its async init |
| 63 | + // and we send the notifications/initialized message before the server is ready. Fix upstream in mcp-go if possible. |
| 64 | + // |
| 65 | + // Only retry when initialization fails due to sending the initialized notification. |
| 66 | + if !isInitNotificationSendError(err) { |
| 67 | + slog.Error("Failed to initialize MCP client", "error", err) |
| 68 | + return fmt.Errorf("failed to initialize MCP client: %w", err) |
| 69 | + } |
| 70 | + if attempt >= maxRetries { |
| 71 | + slog.Error("Failed to initialize MCP client after retries", "error", err) |
| 72 | + return fmt.Errorf("failed to initialize MCP client after retries: %w", err) |
| 73 | + } |
| 74 | + backoff := time.Duration(200*(attempt+1)) * time.Millisecond |
| 75 | + slog.Debug("MCP initialize failed to send initialized notification; retrying", "id", c.logId, "attempt", attempt+1, "backoff_ms", backoff.Milliseconds()) |
| 76 | + select { |
| 77 | + case <-time.After(backoff): |
| 78 | + case <-ctx.Done(): |
| 79 | + return fmt.Errorf("failed to initialize MCP client: %w", ctx.Err()) |
| 80 | + } |
58 | 81 | } |
59 | 82 |
|
60 | 83 | slog.Debug("MCP client started and initialized successfully") |
61 | 84 | return nil |
62 | 85 | } |
63 | 86 |
|
| 87 | +// isInitNotificationSendError returns true if initialization failed while sending the |
| 88 | +// notifications/initialized message to the server. |
| 89 | +func isInitNotificationSendError(err error) bool { |
| 90 | + if err == nil { |
| 91 | + return false |
| 92 | + } |
| 93 | + msg := strings.ToLower(err.Error()) |
| 94 | + // mcp-go client returns this error |
| 95 | + if strings.Contains(msg, "failed to send initialized notification") { |
| 96 | + return true |
| 97 | + } |
| 98 | + return false |
| 99 | +} |
| 100 | + |
64 | 101 | // Stop stops the MCP server |
65 | 102 | func (c *Client) Stop() error { |
66 | 103 | slog.Debug("Stopping MCP client") |
|
0 commit comments