sipeed/picoclaw · error
failed to reach MCP server %q: %w
Error message
failed to reach MCP server %q: %w
What it means
Wrapper around the probe failure in picoclaw mcp test <name>. The probe forces Enabled=true on a single-server Manager config and calls LoadFromMCPConfig, which spawns the local command or dials the remote URL and performs the MCP handshake. Any failure — spawn errors, path validation, transport errors, handshake or timeout — is reported under this message with %w, preserving the underlying cause. The probe runs under --timeout, default 5s.
Source
Thrown at cmd/picoclaw/internal/mcp/test.go:35
Args: cobra.ExactArgs(1),
RunE: func(cmd *cobra.Command, args []string) error {
cfg, err := loadConfig()
if err != nil {
return err
}
name := args[0]
server, exists := cfg.Tools.MCP.Servers[name]
if !exists {
return fmt.Errorf("MCP server %q not found", name)
}
ctx, cancel := context.WithTimeout(context.Background(), timeout)
defer cancel()
result, err := serverProbe(ctx, name, server, cfg.WorkspacePath())
if err != nil {
return fmt.Errorf("failed to reach MCP server %q: %w", name, err)
}
fmt.Fprintf(cmd.OutOrStdout(), "✓ MCP server %q reachable (%d tools).\n", name, result.ToolCount)
return nil
},
}
cmd.Flags().DurationVar(&timeout, "timeout", 5*time.Second, "Connection timeout")
return cmd
}
View on GitHub (pinned to 49183d7e8d)
Solutions
- Raise the budget: picoclaw mcp test <name> --timeout 30s
- Run the configured command manually with the same env/args and check it starts and speaks MCP on stdio
- Verify command path (exists, is a file, is executable) and env/env_file contents
- Read the wrapped error — it distinguishes spawn, transport, and handshake failures
Example fix
# before $ picoclaw mcp test weather failed to reach MCP server "weather": failed to connect to server weather: context deadline exceeded # after $ picoclaw mcp test weather --timeout 30s
Defensive patterns
Strategy: retry
Validate before calling
// pre-flight the stdio command before testing connectivity
if cmd := server.Command; cmd != "" {
if _, err := exec.LookPath(cmd); err != nil {
if info, statErr := os.Stat(cmd); statErr != nil || info.IsDir() {
return fmt.Errorf("command %s unusable: %v", cmd, err)
}
}
} Type guard
func isReachFailure(err error) bool {
return err != nil && strings.Contains(err.Error(), "failed to reach MCP server")
}
func isProbeTimeout(err error) bool {
return errors.Is(err, context.DeadlineExceeded)
} Try / catch
var result probeResult
var err error
for attempt := 0; attempt < 3; attempt++ {
ctx, cancel := context.WithTimeout(context.Background(), timeout*time.Duration(attempt+1))
result, err = serverProbe(ctx, name, server, ws)
cancel()
if err == nil || !errors.Is(err, context.DeadlineExceeded) {
break // only retry timeouts; hard failures surface immediately
}
} Prevention
- Use --timeout 30s or more for cold starts (package downloads on first npx/uvx run)
- Warm the server once manually so caches exist before automated tests
- Distinguish timeouts (retryable) from spawn/auth failures (not) via the wrapped error
When it happens
Trigger: Server process fails to start (missing binary, missing env secrets, bad args); local command is a directory or lacks the execute bit; remote endpoint refused/unreachable; handshake exceeding the 5s default on slow-starting servers (e.g. first run of npx/uvx downloading packages).
Common situations: Local servers whose secrets live only in the user's shell; cold starts of package-run servers exceeding 5s; wrong command path after moving a binary; firewalled or mis-routed remote MCP URLs.
Related errors
- failed to connect to MCP server %q: %w
- failed to confirm overwrite: %w
- aborted: MCP server %q already exists
- missing value for %s
- usage: picoclaw mcp add [flags] <name> <command-or-url> [arg
AI-assisted analysis of sipeed/picoclaw@49183d7e8d (2026-08-15).
Data as JSON: /api/errors/20aa91400ad0adb7.
Report an issue: GitHub.