-
Notifications
You must be signed in to change notification settings - Fork 3
feat: stream container stderr in real-time instead of only on failure #621
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -1,6 +1,7 @@ | ||||||||||||||||||
| package mcp | ||||||||||||||||||
|
|
||||||||||||||||||
| import ( | ||||||||||||||||||
| "bufio" | ||||||||||||||||||
| "bytes" | ||||||||||||||||||
| "context" | ||||||||||||||||||
| "encoding/json" | ||||||||||||||||||
|
|
@@ -190,11 +191,25 @@ func NewConnection(ctx context.Context, command string, args []string, env map[s | |||||||||||||||||
| } | ||||||||||||||||||
| } | ||||||||||||||||||
|
|
||||||||||||||||||
| // Capture stderr to help diagnose container failures | ||||||||||||||||||
| // Capture and stream stderr to help diagnose container issues | ||||||||||||||||||
| // The SDK's CommandTransport only uses stdin/stdout for MCP protocol, | ||||||||||||||||||
| // so we can capture stderr separately for debugging | ||||||||||||||||||
| // Use a TeeReader-style approach: write to both a buffer (for error reporting) | ||||||||||||||||||
| // and to a pipe that streams to logs in real-time | ||||||||||||||||||
| var stderrBuf bytes.Buffer | ||||||||||||||||||
| cmd.Stderr = &stderrBuf | ||||||||||||||||||
| stderrPipeReader, stderrPipeWriter := io.Pipe() | ||||||||||||||||||
| cmd.Stderr = io.MultiWriter(&stderrBuf, stderrPipeWriter) | ||||||||||||||||||
|
|
||||||||||||||||||
| // Stream stderr to logs in a goroutine | ||||||||||||||||||
| go func() { | ||||||||||||||||||
| defer stderrPipeReader.Close() | ||||||||||||||||||
| scanner := bufio.NewScanner(stderrPipeReader) | ||||||||||||||||||
| for scanner.Scan() { | ||||||||||||||||||
| line := scanner.Text() | ||||||||||||||||||
| logger.LogInfo("backend", "[%s stderr] %s", command, line) | ||||||||||||||||||
| logConn.Printf("[stderr] %s", line) | ||||||||||||||||||
| } | ||||||||||||||||||
| }() | ||||||||||||||||||
|
|
||||||||||||||||||
|
||||||||||||||||||
| // Ensure the stderr streaming goroutine terminates when the context is canceled | |
| go func() { | |
| <-ctx.Done() | |
| // Closing the reader will unblock the scanner and allow the goroutine to exit | |
| _ = stderrPipeReader.Close() | |
| }() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The scanner may encounter errors (e.g., if the pipe is broken or a line is too long), but these errors are not checked or logged. After the scanning loop ends, scanner.Err() should be checked to detect and log any scanning errors. This is a common pattern seen elsewhere in the codebase (e.g., internal/cmd/root.go:439 and internal/config/validation_env.go, though the latter doesn't check scanner.Err() either).