charmbracelet/crush · error

failed to authenticate MCP: %w

Error message

failed to authenticate MCP: %w

What it means

This error is returned by Client.MCPAuthenticate when the HTTP POST to /workspaces/{id}/mcp/auth fails before a response is available. It wraps the underlying transport error (connection failure, DNS resolution, timeout, or context cancellation) with %w, so the root cause is preserved via errors.Unwrap/Is. It means the authentication request never completed successfully at the network layer.

Source

Thrown at internal/client/proto.go:378

	}
	var resp proto.MCPAuthResponse
	if err := json.NewDecoder(rsp.Body).Decode(&resp); err != nil {
		return "", fmt.Errorf("failed to decode MCP auth URL: %w", err)
	}
	return resp.AuthURL, nil
}

// MCPAuthenticate runs the OAuth flow for a named MCP server. The server's
// local browser is suppressed; the caller is responsible for surfacing the
// authorization URL (via polling [Client.MCPPendingAuth] / state events)
// and opening it on the user's machine. The call blocks until the flow
// completes, fails, or ctx is cancelled.
func (c *Client) MCPAuthenticate(ctx context.Context, id, name string) error {
	rsp, err := c.post(ctx, fmt.Sprintf("/workspaces/%s/mcp/auth", id), nil,
		jsonBody(proto.MCPNameRequest{Name: name}),
		http.Header{"Content-Type": []string{"application/json"}})
	if err != nil {
		return fmt.Errorf("failed to authenticate MCP: %w", err)
	}
	defer rsp.Body.Close()
	if rsp.StatusCode != http.StatusOK {
		var e proto.Error
		if err := json.NewDecoder(rsp.Body).Decode(&e); err == nil && e.Message != "" {
			return fmt.Errorf("failed to authenticate MCP: %s", e.Message)
		}
		return fmt.Errorf("failed to authenticate MCP: status code %d", rsp.StatusCode)
	}
	return nil
}

// MCPRefreshPrompts refreshes prompts for a named MCP client.
func (c *Client) MCPRefreshPrompts(ctx context.Context, id, name string) error {
	rsp, err := c.post(ctx, fmt.Sprintf("/workspaces/%s/mcp/refresh-prompts", id), nil,
		jsonBody(struct {
			Name string `json:"name"`
		}{Name: name}),

View on GitHub (pinned to 7944b8e522)

Solutions

  1. Inspect the wrapped error with errors.Unwrap (or %v of the returned error) to identify the root cause
  2. Verify the MCP server/API endpoint is running and reachable at the configured base URL
  3. Confirm the workspace id and MCP name are correct and the network/proxy allows the request
  4. Retry with a fresh context and adequate timeout if the cause was a cancelled or expired ctx

Example fix

// before
err := client.MCPAuthenticate(ctx, "ws-123", "filesystem") // ctx already near deadline

// after
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
defer cancel()
if err := client.MCPAuthenticate(ctx, "ws-123", "filesystem"); err != nil {
    log.Printf("mcp auth failed: %v", err) // wrapped cause is visible
}
Defensive patterns

Strategy: retry

Validate before calling

// Pre-flight connectivity check before calling the API
func serverReachable(ctx context.Context, baseURL string) error {
    ctx, cancel := context.WithTimeout(ctx, 5*time.Second)
    defer cancel()
    req, _ := http.NewRequestWithContext(ctx, http.MethodHead, baseURL, nil)
    resp, err := http.DefaultClient.Do(req)
    if err != nil {
        return fmt.Errorf("server unreachable: %w", err)
    }
    resp.Body.Close()
    return nil
}

Type guard

// Narrow the wrapped transport cause
func isTransportErr(err error) bool {
    var netErr net.Error
    var urlErr *url.Error
    return errors.As(err, &netErr) || errors.As(err, &urlErr) || errors.Is(err, context.DeadlineExceeded) || errors.Is(err, context.Canceled)
}

Try / catch

// Go has no try/catch; use retry with error unwrapping
if err := client.MCPAuthenticate(ctx, id, name); err != nil {
    if errors.Is(err, context.Canceled) {
        return err // do not retry caller cancellation
    }
    if isTransportErr(err) {
        err = retryWithBackoff(3, func() error { return client.MCPAuthenticate(ctx, id, name) })
    }
    return err
}

Prevention

When it happens

Trigger: Calling MCPAuthenticate(ctx, id, name) when the server is unreachable, the base URL is misconfigured, the network is down, TLS fails, or the passed ctx is cancelled before the request completes.

Common situations: Dev environment with the API server not running; wrong host/port in client configuration; corporate proxy or VPN blocking the request; long-running auth triggering context deadline exceeded; expired session behind an auth proxy returning connection resets.

Understand the failure class

Background: 'Something went wrong' / 'Request failed (500)' / 'HTTP error! status: 404' — what failed HTTP requests actually mean and how to find the real cause — this error's family across 28 libraries.

Related errors


AI-assisted analysis of charmbracelet/crush@7944b8e522 (2026-08-29). Data as JSON: /api/errors/02e8f1c5ca4d2acf. Report an issue: GitHub.