@@ -387,6 +387,7 @@ func createMCPSession(ctx context.Context, name string, m config.MCPConfig, reso
387387
388388 session, err := client.Connect(mcpCtx, transport, nil)
389389 if err != nil {
390+ err = maybeStdioErr(err, transport)
390391 updateMCPState(name, MCPStateError, maybeTimeoutErr(err, timeout), nil, 0)
391392 slog.Error("error starting mcp client", "error", err, "name", name)
392393 cancel()
@@ -398,6 +399,27 @@ func createMCPSession(ctx context.Context, name string, m config.MCPConfig, reso
398399 return session, nil
399400}
400401
402+// maybeStdioErr if a stdio mcp prints an error in non-json format, it'll fail
403+// to parse, and the cli will then close it, causing the EOF error.
404+// so, if we got an EOF err, and the transport is STDIO, we try to exec it
405+// again with a timeout and collect the output so we can add details to the
406+// error.
407+// this happens particularly when starting things with npx, e.g. if node can't
408+// be found or some other error like that.
409+func maybeStdioErr(err error, transport mcp.Transport) error {
410+ if !errors.Is(err, io.EOF) {
411+ return err
412+ }
413+ ct, ok := transport.(*mcp.CommandTransport)
414+ if !ok {
415+ return err
416+ }
417+ if err2 := stdioMCPCheck(ct.Command); err2 != nil {
418+ err = errors.Join(err, err2)
419+ }
420+ return err
421+}
422+
401423func maybeTimeoutErr(err error, timeout time.Duration) error {
402424 if errors.Is(err, context.Canceled) {
403425 return fmt.Errorf("timed out after %s", timeout)
@@ -465,3 +487,15 @@ func (rt headerRoundTripper) RoundTrip(req *http.Request) (*http.Response, error
465487func mcpTimeout(m config.MCPConfig) time.Duration {
466488 return time.Duration(cmp.Or(m.Timeout, 15)) * time.Second
467489}
490+
491+func stdioMCPCheck(old *exec.Cmd) error {
492+ ctx, cancel := context.WithTimeout(context.Background(), time.Second*5)
493+ defer cancel()
494+ cmd := exec.CommandContext(ctx, old.Path, old.Args...)
495+ cmd.Env = old.Env
496+ out, err := cmd.CombinedOutput()
497+ if err == nil || errors.Is(ctx.Err(), context.DeadlineExceeded) {
498+ return nil
499+ }
500+ return fmt.Errorf("%w: %s", err, string(out))
501+}