Skip to content

Commit 317ce0f

Browse files
author
Jose Bovet Derpich
committed
feat(adapter): enhance StdioAdapter with process readiness checks and improved error handling
Signed-off-by: Jose Bovet Derpich <jose.bovet@walmart.com>
1 parent 2507430 commit 317ce0f

File tree

1 file changed

+81
-2
lines changed

1 file changed

+81
-2
lines changed

pkg/adapter/stdio.go

Lines changed: 81 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import (
44
"context"
55
"fmt"
66
"os"
7+
"strings"
78
"time"
89

910
mcpclient "github.com/mark3labs/mcp-go/client"
@@ -40,8 +41,6 @@ func (s *StdioAdapter) Connect(ctx context.Context) error {
4041
}
4142

4243
s.logf("Connecting to MCP server via stdio: %s %v", s.config.Command, s.config.Args)
43-
44-
// Create stdio client
4544
client, err := mcpclient.NewStdioMCPClient(
4645
s.config.Command,
4746
s.config.Env,
@@ -51,21 +50,41 @@ func (s *StdioAdapter) Connect(ctx context.Context) error {
5150
return fmt.Errorf("failed to create stdio client: %w", err)
5251
}
5352

53+
// Log client for debugging
54+
s.logf("Created stdio client: %+v", client)
55+
5456
s.client = client
5557

58+
// Wait and check if process is still alive
59+
if err := s.waitForProcessReady(ctx); err != nil {
60+
if closeErr := s.client.Close(); closeErr != nil {
61+
s.logf("Warning: failed to close stdio client during cleanup: %v", closeErr)
62+
}
63+
return err
64+
}
65+
5666
// Initialize the connection
5767
initRequest := mcp.InitializeRequest{}
5868
initRequest.Params.ProtocolVersion = mcp.LATEST_PROTOCOL_VERSION
5969
initRequest.Params.ClientInfo = mcp.Implementation{
6070
Name: "mcp-cli-adapter",
6171
Version: "1.0.0",
6272
}
73+
initRequest.Params.Capabilities = mcp.ClientCapabilities{
74+
Roots: &struct {
75+
ListChanged bool `json:"listChanged,omitempty"`
76+
}{
77+
ListChanged: true,
78+
},
79+
}
6380

81+
s.logf("Sending initialize request with timeout: %v", s.config.Timeout)
6482
ctx, cancel := context.WithTimeout(ctx, s.config.Timeout)
6583
defer cancel()
6684

6785
result, err := s.client.Initialize(ctx, initRequest)
6886
if err != nil {
87+
s.logf("Initialize failed: %v", err)
6988
if err := s.client.Close(); err != nil {
7089
// Log the error but don't return it since this is likely in a cleanup context
7190
fmt.Fprintf(os.Stderr, "Warning: failed to close stdio client: %v\n", err)
@@ -190,3 +209,63 @@ func (s *StdioAdapter) GetPrompt(ctx context.Context, name string, arguments map
190209

191210
return result, nil
192211
}
212+
213+
func (s *StdioAdapter) waitForProcessReady(ctx context.Context) error {
214+
s.logf("Waiting for process to be ready...")
215+
216+
// Check multiple times with increasing delays
217+
delays := []time.Duration{100 * time.Millisecond, 500 * time.Millisecond, 1 * time.Second}
218+
219+
for i, delay := range delays {
220+
select {
221+
case <-ctx.Done():
222+
return ctx.Err()
223+
case <-time.After(delay):
224+
}
225+
226+
s.logf("Process readiness check %d/%d", i+1, len(delays))
227+
228+
// Try a quick ping to see if process responds
229+
pingCtx, cancel := context.WithTimeout(ctx, 1*time.Second)
230+
err := s.pingProcess(pingCtx)
231+
cancel()
232+
233+
if err == nil {
234+
s.logf("Process is ready!")
235+
return nil
236+
}
237+
238+
if isProcessExitError(err) {
239+
return fmt.Errorf("process exited unexpectedly - check command '%s %v'",
240+
s.config.Command, s.config.Args)
241+
}
242+
243+
s.logf("Process not ready yet (attempt %d): %v", i+1, err)
244+
}
245+
246+
return fmt.Errorf("process did not become ready within expected time")
247+
}
248+
249+
func (s *StdioAdapter) pingProcess(ctx context.Context) error {
250+
// Send a minimal request to test if the process is alive and responding
251+
initRequest := mcp.InitializeRequest{}
252+
initRequest.Params.ProtocolVersion = mcp.LATEST_PROTOCOL_VERSION
253+
initRequest.Params.ClientInfo = mcp.Implementation{
254+
Name: "ping",
255+
Version: "1.0.0",
256+
}
257+
258+
_, err := s.client.Initialize(ctx, initRequest)
259+
return err
260+
}
261+
func isProcessExitError(err error) bool {
262+
if err == nil {
263+
return false
264+
}
265+
errStr := err.Error()
266+
return strings.Contains(errStr, "process") ||
267+
strings.Contains(errStr, "exit") ||
268+
strings.Contains(errStr, "pipe") ||
269+
strings.Contains(errStr, "broken") ||
270+
strings.Contains(errStr, "EOF")
271+
}

0 commit comments

Comments
 (0)