@@ -34,6 +34,30 @@ type StdioMCPClient struct {
34
34
capabilities mcp.ServerCapabilities
35
35
}
36
36
37
+ // NewStdioMCPClientWithIO returns a new stdio-based MCP client using existing
38
+ // input, output, and logging streams instead of spawning a subprocess.
39
+ // This is useful for testing and simulating client behavior.
40
+ func NewStdioMCPClientWithIO (input io.Reader , output io.WriteCloser , logging io.ReadCloser ) * StdioMCPClient {
41
+ client := & StdioMCPClient {
42
+ cmd : nil ,
43
+ stdin : output ,
44
+ stdout : bufio .NewReader (input ),
45
+ stderr : logging ,
46
+ responses : make (map [int64 ]chan RPCResponse ),
47
+ done : make (chan struct {}),
48
+ }
49
+
50
+ // Start reading responses in a goroutine and wait for it to be ready
51
+ ready := make (chan struct {})
52
+ go func () {
53
+ close (ready )
54
+ client .readResponses ()
55
+ }()
56
+ <- ready
57
+
58
+ return client
59
+ }
60
+
37
61
// NewStdioMCPClient creates a new stdio-based MCP client that communicates with a subprocess.
38
62
// It launches the specified command with given arguments and sets up stdin/stdout pipes for communication.
39
63
// Returns an error if the subprocess cannot be started or the pipes cannot be created.
@@ -64,28 +88,11 @@ func NewStdioMCPClient(
64
88
return nil , fmt .Errorf ("failed to create stderr pipe: %w" , err )
65
89
}
66
90
67
- client := & StdioMCPClient {
68
- cmd : cmd ,
69
- stdin : stdin ,
70
- stderr : stderr ,
71
- stdout : bufio .NewReader (stdout ),
72
- responses : make (map [int64 ]chan RPCResponse ),
73
- done : make (chan struct {}),
74
- }
75
-
76
91
if err := cmd .Start (); err != nil {
77
92
return nil , fmt .Errorf ("failed to start command: %w" , err )
78
93
}
79
94
80
- // Start reading responses in a goroutine and wait for it to be ready
81
- ready := make (chan struct {})
82
- go func () {
83
- close (ready )
84
- client .readResponses ()
85
- }()
86
- <- ready
87
-
88
- return client , nil
95
+ return NewStdioMCPClientWithIO (stdout , stdin , stderr ), nil
89
96
}
90
97
91
98
// Close shuts down the stdio client, closing the stdin pipe and waiting for the subprocess to exit.
@@ -98,6 +105,11 @@ func (c *StdioMCPClient) Close() error {
98
105
if err := c .stderr .Close (); err != nil {
99
106
return fmt .Errorf ("failed to close stderr: %w" , err )
100
107
}
108
+
109
+ if c .cmd == nil {
110
+ return nil
111
+ }
112
+
101
113
return c .cmd .Wait ()
102
114
}
103
115
0 commit comments