@@ -39,7 +39,6 @@ type Stdio struct {
39
39
done chan struct {}
40
40
onNotification func (mcp.JSONRPCNotification )
41
41
notifyMu sync.RWMutex
42
- processExited chan struct {}
43
42
exitErr atomic.Value
44
43
}
45
44
@@ -71,9 +70,8 @@ func NewStdio(
71
70
args : args ,
72
71
env : env ,
73
72
74
- responses : make (map [int64 ]chan * JSONRPCResponse ),
75
- done : make (chan struct {}),
76
- processExited : make (chan struct {}),
73
+ responses : make (map [int64 ]chan * JSONRPCResponse ),
74
+ done : make (chan struct {}),
77
75
}
78
76
79
77
return client
@@ -83,7 +81,14 @@ func (c *Stdio) Start(ctx context.Context) error {
83
81
if err := c .spawnCommand (ctx ); err != nil {
84
82
return err
85
83
}
86
- return nil
84
+
85
+ // Start reading responses in a goroutine and wait for it to be ready
86
+ ready := make (chan struct {})
87
+ go func () {
88
+ close (ready )
89
+ c .readResponses ()
90
+ }()
91
+ return waitUntilReadyOrExit (ready , c .done , readyTimeout )
87
92
}
88
93
89
94
// spawnCommand spawns a new process running c.command.
@@ -121,27 +126,25 @@ func (c *Stdio) spawnCommand(ctx context.Context) error {
121
126
if err := cmd .Start (); err != nil {
122
127
return fmt .Errorf ("failed to start command: %w" , err )
123
128
}
129
+
124
130
go func () {
125
131
err := cmd .Wait ()
126
132
if err != nil {
127
133
c .exitErr .Store (err )
128
134
}
129
- close (c .processExited )
130
- }()
131
-
132
- // Start reading responses in a goroutine and wait for it to be ready
133
- ready := make (chan struct {})
134
- go func () {
135
- close (ready )
136
- c .readResponses ()
135
+ tryCloseDone (c .done )
137
136
}()
138
-
139
- if err := waitUntilReadyOrExit (ready , c .processExited , readyTimeout ); err != nil {
140
- return err
141
- }
142
137
return nil
143
138
}
144
139
140
+ func tryCloseDone (done chan struct {}) {
141
+ select {
142
+ case <- done :
143
+ return
144
+ default :
145
+ }
146
+ close (done )
147
+ }
145
148
func waitUntilReadyOrExit (ready <- chan struct {}, exited <- chan struct {}, timeout time.Duration ) error {
146
149
select {
147
150
case <- exited :
@@ -161,14 +164,15 @@ func waitUntilReadyOrExit(ready <-chan struct{}, exited <-chan struct{}, timeout
161
164
// Close shuts down the stdio client, closing the stdin pipe and waiting for the subprocess to exit.
162
165
// Returns an error if there are issues closing stdin or waiting for the subprocess to terminate.
163
166
func (c * Stdio ) Close () error {
164
- close (c .done )
167
+ // cancel all in-flight request
168
+ tryCloseDone (c .done )
165
169
if err := c .stdin .Close (); err != nil {
166
170
return fmt .Errorf ("failed to close stdin: %w" , err )
167
171
}
168
172
if err := c .stderr .Close (); err != nil {
169
173
return fmt .Errorf ("failed to close stderr: %w" , err )
170
174
}
171
- <- c . processExited
175
+
172
176
if err , ok := c .exitErr .Load ().(error ); ok && err != nil {
173
177
return err
174
178
}
@@ -270,6 +274,7 @@ func (c *Stdio) SendRequest(
270
274
deleteResponseChan ()
271
275
return nil , fmt .Errorf ("failed to write request: %w" , err )
272
276
}
277
+
273
278
select {
274
279
case <- ctx .Done ():
275
280
deleteResponseChan ()
0 commit comments